notebooklm-sdk 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,17 +1,18 @@
1
1
  # notebooklm-sdk
2
2
 
3
+ ### Automate NotebookLM from your code.
4
+
3
5
  [![npm version](https://img.shields.io/npm/v/notebooklm-sdk?style=flat-square)](https://www.npmjs.com/package/notebooklm-sdk)
4
6
  ![types](https://img.shields.io/npm/types/notebooklm-sdk?style=flat-square)
5
7
  ![license](https://img.shields.io/npm/l/notebooklm-sdk?style=flat-square)
6
8
 
7
- A lightweight, zero-dependency TypeScript SDK for the NotebookLM API.
8
- Works in **Node.js, Bun, and Deno**.
9
+ Generate AI podcasts, chat with documents, run web research, and manage notebooks programmatically — from Node.js, Bun, or Deno.
9
10
 
10
- > This SDK is a TypeScript port of [notebooklm-py](https://github.com/teng-lin/notebooklm-py).
11
+ > ⚠️ **Unofficial.** This SDK reverse-engineers the NotebookLM internal API. It may break when Google updates their service. Not affiliated with Google.
11
12
 
12
- ---
13
+ TypeScript port of [notebooklm-py](https://github.com/teng-lin/notebooklm-py).
13
14
 
14
- ## Installation
15
+ ## Install
15
16
 
16
17
  ```bash
17
18
  npm install notebooklm-sdk
@@ -19,289 +20,131 @@ npm install notebooklm-sdk
19
20
  bun add notebooklm-sdk
20
21
  ```
21
22
 
22
- ## Authentication
23
-
24
- ### Quick Login (Recommended)
25
-
26
- First, install playwright:
27
-
28
- ```bash
29
- bun add -d playwright
30
- bunx playwright install chromium
31
- ```
32
-
33
- Then, authenticate using the CLI:
23
+ ## Quickstart
34
24
 
35
25
  ```bash
26
+ # Run once to authenticate
36
27
  npx notebooklm-sdk login
37
- # or
38
- bun x notebooklm-sdk login
39
28
  ```
40
29
 
41
- This opens a real browser for Google sign-in and generates a
42
- `storage_state.json` file you can reuse.
43
-
44
- Then connect using the file:
45
-
46
30
  ```ts
47
31
  import { NotebookLMClient } from "notebooklm-sdk";
48
32
 
49
- const client = await NotebookLMClient.connect({
50
- cookiesFile: "./storage_state.json",
51
- });
52
- ```
53
-
54
- <details>
55
- <summary>Manual Authentication</summary>
56
-
57
- You can authenticate in multiple ways depending on your setup.
58
-
59
- #### 1. Use `.env` Cookie String
60
-
61
- Copy the `Cookie` header from DevTools → Network and store it:
62
-
63
- ```bash
64
- NOTEBOOKLM_COOKIE="SID=...; HSID=..."
65
- ```
66
-
67
- Then:
68
-
69
- ```ts
70
- const client = await NotebookLMClient.connect({
71
- cookies: process.env.NOTEBOOKLM_COOKIE,
72
- });
73
- ```
74
-
75
- ---
33
+ const client = await NotebookLMClient.connect(); // uses ~/.notebooklm/session.json
76
34
 
77
- #### 2. Use Playwright `storage_state.json`
35
+ // Create a notebook and add a source
36
+ const { id } = await client.notebooks.create("My Research");
37
+ await client.sources.addUrl(id, "https://en.wikipedia.org/wiki/TypeScript");
78
38
 
79
- If you already have a Playwright storage file:
80
-
81
- ```ts
82
- const client = await NotebookLMClient.connect({
83
- cookiesFile: "./storage_state.json",
39
+ // Generate a podcast and download it
40
+ const { artifactId } = await client.artifacts.createAudio(id, {
41
+ format: "deep_dive",
84
42
  });
85
- ```
86
-
87
- ---
88
-
89
- #### 3. Pass Cookies Directly
43
+ const audio = await client.artifacts.waitUntilReady(id, artifactId);
44
+ const mp3 = await client.artifacts.downloadAudio(id, audio.id);
90
45
 
91
- You can also pass cookies at runtime:
92
-
93
- ```ts
94
- const client = await NotebookLMClient.connect({
95
- cookies: "SID=...; HSID=...",
96
- });
46
+ // Chat with the sources
47
+ const res = await client.chat.ask(id, "Summarize the key points.");
48
+ console.log(res.answer);
97
49
  ```
98
50
 
99
- </details>
51
+ ## Use cases
100
52
 
101
- ---
53
+ - **Blog & content generation** — research a topic, import sources, generate a blog post or briefing doc automatically
54
+ - **Content pipelines** — ingest articles or reports, generate a podcast or briefing doc on a schedule
55
+ - **Research automation** — run web research, import results, and query them via chat
56
+ - **Document Q&A bots** — feed documents into a notebook and build a chat interface on top
57
+ - **Batch artifact generation** — generate quizzes, flashcards, or study guides from a library of sources
58
+ - **Notebook management tools** — create, organize, and share notebooks programmatically
102
59
 
103
- ## Notebooks
104
-
105
- ```ts
106
- const notebooks = await client.notebooks.list();
107
- const nb = await client.notebooks.get(id);
60
+ ## Authentication
108
61
 
109
- const { id: newId } = await client.notebooks.create("My Notebook");
62
+ ### Login (recommended)
110
63
 
111
- await client.notebooks.rename(newId, "New Title");
112
- await client.notebooks.delete(newId);
64
+ Install Playwright once:
113
65
 
114
- const summary = await client.notebooks.getSummary(id);
115
- const description = await client.notebooks.getDescription(id);
66
+ ```bash
67
+ bun add -d playwright
68
+ bunx playwright install chromium
116
69
  ```
117
70
 
118
- ---
119
-
120
- ## Sources
71
+ Authenticate:
121
72
 
122
- ```ts
123
- const sources = await client.sources.list(notebookId);
124
-
125
- const { sourceId } = await client.sources.addUrl(
126
- notebookId,
127
- "https://example.com",
128
- );
129
- const { sourceId } = await client.sources.addText(notebookId, "Text", "Title");
130
- const { sourceId } = await client.sources.addFile(
131
- notebookId,
132
- buffer,
133
- "file.pdf",
134
- );
135
-
136
- await client.sources.waitUntilReady(notebookId, sourceId);
137
-
138
- await client.sources.delete(notebookId, sourceId);
73
+ ```bash
74
+ npx notebooklm-sdk login
139
75
  ```
140
76
 
141
- ---
142
-
143
- ## Artifacts
77
+ Opens a real Chrome window for Google sign-in. Session is saved to `~/.notebooklm/session.json` and auto-discovered on every `connect()` — no config needed.
144
78
 
145
- Generate AI outputs from notebook sources.
146
-
147
- ```ts
148
- const { artifactId } = await client.artifacts.createAudio(notebookId, {
149
- format: AudioFormat.DEEP_DIVE,
150
- length: AudioLength.DEFAULT,
151
- language: "en",
152
- });
153
-
154
- const { artifactId } = await client.artifacts.createVideo(notebookId, {
155
- format: VideoFormat.EXPLAINER,
156
- });
157
-
158
- const { artifactId } = await client.artifacts.createQuiz(notebookId);
159
- const { artifactId } = await client.artifacts.createFlashcards(notebookId);
160
-
161
- const { artifactId } = await client.artifacts.createReport(notebookId, {
162
- format: "briefing_doc",
163
- });
79
+ ```bash
80
+ npx notebooklm-sdk whoami # verify session is valid
164
81
  ```
165
82
 
166
- Wait & download:
167
-
168
- ```ts
169
- await client.artifacts.waitUntilReady(notebookId, artifactId);
83
+ ### CI / Server
170
84
 
171
- const audio = await client.artifacts.downloadAudio(notebookId, artifactId);
172
- const video = await client.artifacts.downloadVideo(notebookId, artifactId);
85
+ Pass cookies via environment variable:
173
86
 
174
- const markdown = await client.artifacts.getReportMarkdown(
175
- notebookId,
176
- artifactId,
177
- );
178
- const html = await client.artifacts.getInteractiveHtml(notebookId, artifactId);
87
+ ```bash
88
+ NOTEBOOKLM_COOKIES="SID=...; HSID=..."
179
89
  ```
180
90
 
181
- ---
182
-
183
- ## Chat
184
-
185
91
  ```ts
186
- const res = await client.chat.ask(notebookId, "What is this about?");
187
- console.log(res.answer);
188
-
189
- const follow = await client.chat.ask(notebookId, "Tell me more.", {
190
- conversationId: res.conversationId,
92
+ const client = await NotebookLMClient.connect({
93
+ cookies: process.env.NOTEBOOKLM_COOKIES,
191
94
  });
192
-
193
- const convId = await client.chat.getLastConversationId(notebookId);
194
- const turns = await client.chat.getConversationTurns(notebookId, convId);
195
95
  ```
196
96
 
197
- ---
97
+ > To get cookie values: open NotebookLM in Chrome → DevTools → Network → any request → copy the `Cookie` header.
198
98
 
199
- ## Notes
99
+ ## What you can build
200
100
 
201
- ```ts
202
- const { noteId } = await client.notes.create(notebookId, "# My Note");
101
+ | API | What it does |
102
+ | ----------- | ------------------------------------------------------- |
103
+ | `notebooks` | Create, rename, delete, list notebooks |
104
+ | `sources` | Add URLs, text, files; wait for processing |
105
+ | `artifacts` | Generate and download AI outputs (see below) |
106
+ | `chat` | Ask questions, follow-up conversations |
107
+ | `research` | Run web research, import results as sources |
108
+ | `notes` | Create and manage saved notes |
109
+ | `sharing` | Control public access and per-user permissions |
110
+ | `settings` | Get/set output language |
203
111
 
204
- await client.notes.update(notebookId, noteId, "Updated");
205
- await client.notes.delete(notebookId, noteId);
206
- ```
112
+ **Artifact types**
207
113
 
208
- ---
114
+ | Artifact | Method | Output |
115
+ | -------------- | -------------------- | ----------------- |
116
+ | Audio Overview | `createAudio()` | Downloadable MP3 |
117
+ | Video Overview | `createVideo()` | Downloadable MP4 |
118
+ | Slide Deck | `createSlideDeck()` | PDF / PPTX download |
119
+ | Infographic | `createInfographic()`| PNG download |
120
+ | Mind Map | `createMindMap()` | JSON download |
121
+ | Reports | `createReport()` | Markdown download |
122
+ | Flashcards | `createFlashcards()` | Interactive HTML |
123
+ | Quiz | `createQuiz()` | Interactive HTML |
124
+ | Data Table | `createDataTable()` | CSV (headers + rows) |
209
125
 
210
- ## Research
211
-
212
- ```ts
213
- const task = await client.research.start(
214
- notebookId,
215
- "Latest advances in quantum computing",
216
- "web",
217
- "deep",
218
- );
219
-
220
- const result = await client.research.poll(notebookId);
221
-
222
- if (result.status === "completed") {
223
- await client.research.importSources(
224
- notebookId,
225
- result.taskId,
226
- result.sources.slice(0, 2),
227
- );
228
- }
229
- ```
230
-
231
- ---
232
-
233
- ## Sharing
234
-
235
- ```ts
236
- await client.sharing.setPublic(notebookId, true);
237
-
238
- await client.sharing.addUser(
239
- notebookId,
240
- "user@example.com",
241
- SharePermission.VIEWER,
242
- );
243
- ```
244
-
245
- ---
246
-
247
- ## Settings
248
-
249
- ```ts
250
- const lang = await client.settings.getOutputLanguage();
251
- await client.settings.setOutputLanguage("ja");
252
- ```
253
-
254
- ---
126
+ [Full API reference](./DOCS.md)
255
127
 
256
128
  ## Examples
257
129
 
258
- Runnable scripts are in [`examples/`](./examples).
259
-
260
- **Setup:**
261
-
262
- 1. `npm run login` to create `storage_state.json`.
263
- 2. Run any example below.
264
-
265
130
  ```bash
266
- # for auto login
267
- bun run login
268
- bun run examples/basic.ts
269
-
270
- # for manual cookie
271
- bunx dotenv -e .env -- bunx tsx examples/basic.ts
272
- ```
273
-
274
- ---
275
-
276
- ## Error Handling
277
-
278
- All errors extend `NotebookLMError`.
279
-
280
- ```ts
281
- try {
282
- await client.artifacts.downloadAudio(notebookId, artifactId);
283
- } catch (err) {
284
- if (err instanceof ArtifactNotReadyError) {
285
- // still processing
286
- }
287
- }
288
- ```
289
-
290
- ---
291
-
292
- ## Project Structure
131
+ npx notebooklm-sdk login
293
132
 
133
+ bun run examples/basic.ts # list notebooks and sources
134
+ bun run examples/chat.ts # ask questions, follow-up conversations
135
+ bun run examples/audio.ts # generate and download a podcast
136
+ bun run examples/report.ts # generate and download a briefing doc
137
+ bun run examples/slide-deck.ts # generate and download PDF / PPTX
138
+ bun run examples/infographic.ts # generate and download a PNG infographic
139
+ bun run examples/mind-map.ts # generate and download mind map JSON
140
+ bun run examples/data-table.ts # generate and download a CSV data table
141
+ bun run examples/notes.ts # create, update, list, and delete notes
142
+ bun run examples/research.ts # web research and import sources
143
+ bun run examples/research-and-chat.ts # research → import → chat
144
+ bun run examples/full-lifecycle.ts # create, add sources, chat, delete
145
+ bun run examples/download.ts # download all completed artifacts
294
146
  ```
295
- src/
296
- client.ts
297
- auth.ts
298
- api/
299
- rpc/
300
- types/
301
- ```
302
-
303
- ---
304
147
 
305
148
  ## License
306
149
 
307
- MIT
150
+ MIT · TypeScript port of [notebooklm-py](https://github.com/teng-lin/notebooklm-py)
package/dist/auth.cjs CHANGED
@@ -37,10 +37,8 @@ function loadCookiesFromFile(filePath) {
37
37
  try {
38
38
  raw = fs.readFileSync(filePath, "utf-8");
39
39
  } catch {
40
- throw new AuthError(
41
- `Session file not found: ${filePath}
42
- Run: npx notebooklm-sdk login`
43
- );
40
+ throw new AuthError(`Session file not found: ${filePath}
41
+ Run: npx notebooklm-sdk login`);
44
42
  }
45
43
  return extractCookiesFromStorageState(JSON.parse(raw));
46
44
  }
@@ -175,9 +173,7 @@ async function connect(opts = {}) {
175
173
  } else if (envCookies) {
176
174
  cookieMap = loadCookiesFromString(envCookies);
177
175
  } else {
178
- throw new AuthError(
179
- "No session found. Run: npx notebooklm-sdk login"
180
- );
176
+ throw new AuthError("No session found. Run: npx notebooklm-sdk login");
181
177
  }
182
178
  }
183
179
  const { csrfToken, sessionId } = await fetchTokens(cookieMap);
@@ -203,9 +199,7 @@ async function login(opts = {}) {
203
199
  try {
204
200
  playwright.chromium.executablePath();
205
201
  } catch {
206
- throw new Error(
207
- "Playwright browser not found. Run: npx playwright install chromium"
208
- );
202
+ throw new Error("Playwright browser not found. Run: npx playwright install chromium");
209
203
  }
210
204
  if (!fs.existsSync(DEFAULT_SESSION_DIR)) {
211
205
  fs.mkdirSync(DEFAULT_SESSION_DIR, { recursive: true });
package/dist/auth.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types/errors.ts","../src/auth.ts","../src/auth/browser.ts"],"names":["join","homedir","readFileSync","existsSync","DEFAULT_SESSION_FILE","NOTEBOOKLM_URL","chromium","mkdirSync"],"mappings":";;;;;;;;;;AAOO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EACzC,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,KAAK,WAAA,CAAY,IAAA;AAAA,EAC/B;AACF,CAAA;AAuBO,IAAM,QAAA,GAAN,cAAuB,eAAA,CAAgB;AAAA,EACnC,QAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EAET,WAAA,CACE,OAAA,EACA,IAAA,GAKI,EAAC,EACL;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,WAAW,IAAA,CAAK,QAAA;AACrB,IAAA,IAAA,CAAK,WAAA,GAAc,KAAK,WAAA,GAAc,IAAA,CAAK,YAAY,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,GAAI,MAAA;AACvE,IAAA,IAAA,CAAK,UAAU,IAAA,CAAK,OAAA;AACpB,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA,CAAK,QAAA,IAAY,EAAC;AAAA,EACpC;AACF,CAAA;AAEO,IAAM,SAAA,GAAN,cAAwB,QAAA,CAAS;AAAC,CAAA;;;ACpDzC,IAAM,oBAAA,GAAuBA,SAAA,CAAKC,UAAA,EAAQ,EAAG,eAAe,cAAc,CAAA;AAoBnE,SAAS,oBAAoB,QAAA,EAA6B;AAC/D,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAMC,eAAA,CAAa,UAAU,OAAO,CAAA;AAAA,EACtC,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,SAAA;AAAA,MACR,2BAA2B,QAAQ;AAAA,6BAAA;AAAA,KACrC;AAAA,EACF;AACA,EAAA,OAAO,8BAAA,CAA+B,IAAA,CAAK,KAAA,CAAM,GAAG,CAAC,CAAA;AACvD;AAGO,SAAS,sBAAsB,YAAA,EAExB;AACZ,EAAA,OAAO,+BAA+B,YAAY,CAAA;AACpD;AAGO,SAAS,wBAAwB,YAAA,EAE7B;AACT,EAAA,MAAM,MAAiB,EAAC;AACxB,EAAA,KAAA,MAAW,CAAA,IAAK,YAAA,CAAa,OAAA,IAAW,EAAC,EAAG;AAC1C,IAAA,IAAI,EAAE,MAAA,KAAW,aAAA,IAAiB,CAAA,CAAE,IAAA,IAAQ,EAAE,KAAA,EAAO;AACnD,MAAA,GAAA,CAAI,EAAE,IAAI,CAAA,GAAI,IAAI,CAAA,CAAE,IAAI,KAAK,CAAA,CAAE,KAAA;AAAA,IACjC;AAAA,EACF;AACA,EAAA,OAAO,kBAAkB,GAAG,CAAA;AAC9B;AAGO,SAAS,mBAAmB,GAAA,EAA2B;AAC5D,EAAA,OAAO,EAAE,GAAG,GAAA,EAAI;AAClB;AAGO,SAAS,sBAAsB,SAAA,EAA8B;AAClE,EAAA,MAAM,MAAiB,EAAC;AACxB,EAAA,KAAA,MAAW,IAAA,IAAQ,SAAA,CAAU,KAAA,CAAM,MAAM,CAAA,EAAG;AAC1C,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AAC5B,IAAA,IAAI,MAAM,CAAA,EAAG;AACX,MAAA,MAAM,OAAO,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,GAAG,EAAE,IAAA,EAAK;AACrC,MAAA,MAAM,QAAQ,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,CAAC,EAAE,IAAA,EAAK;AACvC,MAAA,IAAI,IAAA,EAAM,GAAA,CAAI,IAAI,CAAA,GAAI,KAAA;AAAA,IACxB;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,+BAA+B,YAAA,EAE1B;AACZ,EAAA,MAAM,UAAqB,EAAC;AAG5B,EAAA,KAAA,MAAW,MAAA,IAAU,YAAA,CAAa,OAAA,IAAW,EAAC,EAAG;AAC/C,IAAA,MAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,KAAA,EAAM,GAAI,MAAA;AAChC,IAAA,IAAI,CAAC,eAAA,CAAgB,MAAM,CAAA,IAAK,CAAC,IAAA,EAAM;AAEvC,IAAA,MAAM,SAAS,MAAA,KAAW,aAAA;AAC1B,IAAA,IAAI,EAAE,IAAA,IAAQ,OAAA,CAAA,IAAY,MAAA,EAAQ;AAChC,MAAA,OAAA,CAAQ,IAAI,CAAA,GAAI,KAAA;AACI,IACtB;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,OAAA,CAAQ,KAAK,CAAA,EAAG;AACnB,IAAA,MAAM,IAAI,SAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;AAEA,SAAS,gBAAgB,MAAA,EAAyB;AAChD,EAAA,IACE,MAAA,KAAW,aAAA,IACX,MAAA,KAAW,uBAAA,IACX,WAAW,wBAAA,EACX;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,IAAI,MAAA,CAAO,UAAA,CAAW,UAAU,CAAA,EAAG;AACjC,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,kBAAkB,OAAA,EAA4B;AAC5D,EAAA,OAAO,OAAO,OAAA,CAAQ,OAAO,CAAA,CAC1B,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,GAAG,CAAC,CAAA,CAAA,EAAI,CAAC,CAAA,CAAE,CAAA,CAC3B,KAAK,IAAI,CAAA;AACd;AAMA,IAAM,cAAA,GAAiB,gCAAA;AAEvB,eAAsB,YACpB,OAAA,EACmD;AACnD,EAAA,MAAM,YAAA,GAAe,kBAAkB,OAAO,CAAA;AAE9C,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,cAAA,EAAgB;AAAA,IAC3C,OAAA,EAAS,EAAE,MAAA,EAAQ,YAAA,EAAa;AAAA,IAChC,QAAA,EAAU;AAAA,GACX,CAAA;AAED,EAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,IAAA,MAAM,IAAI,SAAA,CAAU,CAAA,sCAAA,EAAyC,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,EAChF;AAEA,EAAA,MAAM,WAAW,QAAA,CAAS,GAAA;AAC1B,EAAA,IAAI,oBAAA,CAAqB,QAAQ,CAAA,EAAG;AAClC,IAAA,MAAM,IAAI,SAAA,CAAU,CAAA,0BAAA,EAA6B,QAAQ,CAAA,yBAAA,CAA2B,CAAA;AAAA,EACtF;AAEA,EAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,EAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,IAAA,EAAM,QAAQ,CAAA;AACjD,EAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,IAAA,EAAM,QAAQ,CAAA;AAEjD,EAAA,OAAO,EAAE,WAAW,SAAA,EAAU;AAChC;AAEA,SAAS,gBAAA,CAAiB,MAAc,QAAA,EAA0B;AAChE,EAAA,MAAM,KAAA,GAAQ,0BAAA,CAA2B,IAAA,CAAK,IAAI,CAAA;AAClD,EAAA,IAAI,CAAC,KAAA,GAAQ,CAAC,CAAA,EAAG;AACf,IAAA,IAAI,qBAAqB,QAAQ,CAAA,IAAK,IAAA,CAAK,QAAA,CAAS,qBAAqB,CAAA,EAAG;AAC1E,MAAA,MAAM,IAAI,UAAU,4DAA4D,CAAA;AAAA,IAClF;AACA,IAAA,MAAM,IAAI,UAAU,wDAAwD,CAAA;AAAA,EAC9E;AACA,EAAA,OAAO,MAAM,CAAC,CAAA;AAChB;AAEA,SAAS,gBAAA,CAAiB,MAAc,QAAA,EAA0B;AAChE,EAAA,MAAM,KAAA,GAAQ,0BAAA,CAA2B,IAAA,CAAK,IAAI,CAAA;AAClD,EAAA,IAAI,CAAC,KAAA,GAAQ,CAAC,CAAA,EAAG;AACf,IAAA,IAAI,qBAAqB,QAAQ,CAAA,IAAK,IAAA,CAAK,QAAA,CAAS,qBAAqB,CAAA,EAAG;AAC1E,MAAA,MAAM,IAAI,UAAU,4DAA4D,CAAA;AAAA,IAClF;AACA,IAAA,MAAM,IAAI,UAAU,wDAAwD,CAAA;AAAA,EAC9E;AACA,EAAA,OAAO,MAAM,CAAC,CAAA;AAChB;AAEA,SAAS,qBAAqB,GAAA,EAAsB;AAClD,EAAA,OAAO,IAAI,QAAA,CAAS,qBAAqB,CAAA,IAAK,GAAA,CAAI,SAAS,QAAQ,CAAA;AACrE;AAeA,eAAsB,OAAA,CAAQ,IAAA,GAAuB,EAAC,EAAwB;AAC5E,EAAA,IAAI,SAAA;AACJ,EAAA,IAAI,kBAAA,GAAoC,IAAA;AAExC,EAAA,IAAI,KAAK,OAAA,EAAS;AAChB,IAAA,SAAA,GAAY,qBAAA,CAAsB,KAAK,OAAO,CAAA;AAAA,EAChD,CAAA,MAAA,IAAW,KAAK,WAAA,EAAa;AAC3B,IAAA,SAAA,GAAY,mBAAA,CAAoB,KAAK,WAAW,CAAA;AAAA,EAClD,CAAA,MAAA,IAAW,KAAK,aAAA,EAAe;AAC7B,IAAA,IAAI,SAAA,IAAa,KAAK,aAAA,IAAiB,KAAA,CAAM,QAAQ,IAAA,CAAK,aAAA,CAAc,OAAO,CAAA,EAAG;AAChF,MAAA,MAAM,eAAe,IAAA,CAAK,aAAA;AAG1B,MAAA,SAAA,GAAY,sBAAsB,YAAY,CAAA;AAC9C,MAAA,kBAAA,GAAqB,wBAAwB,YAAY,CAAA;AAAA,IAC3D,CAAA,MAAO;AACL,MAAA,SAAA,GAAY,kBAAA,CAAmB,KAAK,aAA0B,CAAA;AAAA,IAChE;AAAA,EACF,CAAA,MAAO;AAEL,IAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,GAAA,CAAI,oBAAoB,CAAA;AACnD,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,GAAA,CAAI,yBAAyB,CAAA;AACrD,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,SAAA,GAAY,oBAAoB,OAAO,CAAA;AAAA,IACzC,CAAA,MAAA,IAAWC,aAAA,CAAW,oBAAoB,CAAA,EAAG;AAC3C,MAAA,MAAM,GAAA,GAAMD,eAAA,CAAa,oBAAA,EAAsB,OAAO,CAAA;AACtD,MAAA,MAAM,YAAA,GAAe,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AACnC,MAAA,SAAA,GAAY,sBAAsB,YAAY,CAAA;AAC9C,MAAA,kBAAA,GAAqB,wBAAwB,YAAY,CAAA;AAAA,IAC3D,CAAA,MAAA,IAAWC,aAAA,CAAW,oBAAoB,CAAA,EAAG;AAC3C,MAAA,MAAM,GAAA,GAAMD,eAAA,CAAa,oBAAA,EAAsB,OAAO,CAAA;AACtD,MAAA,MAAM,YAAA,GAAe,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AACnC,MAAA,SAAA,GAAY,sBAAsB,YAAY,CAAA;AAC9C,MAAA,kBAAA,GAAqB,wBAAwB,YAAY,CAAA;AAAA,IAC3D,WAAW,UAAA,EAAY;AACrB,MAAA,SAAA,GAAY,sBAAsB,UAAU,CAAA;AAAA,IAC9C,CAAA,MAAO;AACL,MAAA,MAAM,IAAI,SAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,EAAE,SAAA,EAAW,SAAA,EAAU,GAAI,MAAM,YAAY,SAAS,CAAA;AAC5D,EAAA,MAAM,YAAA,GAAe,kBAAkB,SAAS,CAAA;AAEhD,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,SAAA;AAAA,IACT,SAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,oBAAoB,kBAAA,IAAsB;AAAA,GAC5C;AACF;AC/OO,IAAM,mBAAA,GAAsBF,SAAAA,CAAKC,UAAAA,EAAQ,EAAG,aAAa;AAEzD,IAAMG,qBAAAA,GAAuBJ,SAAAA,CAAK,mBAAA,EAAqB,cAAc;AAmB5E,IAAMK,eAAAA,GAAiB,gCAAA;AACvB,IAAM,mBAAA,GAAsB,8BAAA;AAW5B,eAAsB,KAAA,CAAM,IAAA,GAAqB,EAAC,EAI/C;AACD,EAAA,MAAM;AAAA,IACJ,aAAA,GAAgBL,SAAAA,CAAK,mBAAA,EAAqB,eAAe,CAAA;AAAA,IACzD,QAAA,GAAW,KAAA;AAAA,IACX,WAAA,GAAc;AAAA,GAChB,GAAI,IAAA;AAGJ,EAAA,IAAI;AACF,IAAAM,mBAAA,CAAS,cAAA,EAAe;AAAA,EAC1B,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAGA,EAAA,IAAI,CAACH,aAAAA,CAAW,mBAAmB,CAAA,EAAG;AACpC,IAAAI,YAAA,CAAU,mBAAA,EAAqB,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAAA,EACpD;AAEA,EAAA,IAAI,OAAA;AAEJ,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,QAAA;AAAA,IACA,IAAA,EAAM,CAAC,+CAA+C;AAAA,GACxD;AAEA,EAAA,OAAA,GAAU,MAAMD,mBAAA,CAAS,uBAAA,CAAwB,aAAA,EAAe;AAAA,IAC9D,GAAG,aAAA;AAAA,IACH,OAAA,EAAS,WAAA,KAAgB,QAAA,GAAW,QAAA,GAAW;AAAA,GAChD,CAAA;AAED,EAAA,MAAM,IAAA,GAAO,QAAQ,KAAA,EAAM,CAAE,CAAC,CAAA,IAAM,MAAM,QAAQ,OAAA,EAAQ;AAC1D,EAAA,MAAM,IAAA,CAAK,KAAKD,eAAc,CAAA;AAG9B,EAAA,IAAI,IAAA,CAAK,GAAA,EAAI,CAAE,QAAA,CAAS,qBAAqB,CAAA,EAAG;AAC9C,IAAA,OAAA,CAAQ,IAAI,kDAAkD,CAAA;AAI9D,IAAA,MAAM,IAAA,CAAK,UAAA;AAAA,MACT,CAAC,GAAA,KAAQ;AACP,QAAA,OAAO,IAAI,QAAA,KAAa,uBAAA,IAA2B,CAAC,GAAA,CAAI,QAAA,CAAS,SAAS,QAAQ,CAAA;AAAA,MACpF,CAAA;AAAA,MACA,EAAE,SAAS,CAAA;AAAE,KACf;AAAA,EACF;AAGA,EAAA,MAAM,KAAK,IAAA,CAAK,mBAAA,EAAqB,EAAE,SAAA,EAAW,QAAQ,CAAA;AAC1D,EAAA,MAAM,KAAK,IAAA,CAAKA,eAAAA,EAAgB,EAAE,SAAA,EAAW,QAAQ,CAAA;AAErD,EAAA,MAAM,YAAA,GAAe,MAAM,OAAA,CAAQ,YAAA,EAAa;AAChD,EAAA,MAAM,OAAA,GAAU,sBAAsB,YAAmB,CAAA;AAEzD,EAAA,MAAM,QAAQ,KAAA,EAAM;AAEpB,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,YAAA;AAAA,IACA,cAAc,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,CACjC,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,GAAG,CAAC,CAAA,CAAA,EAAI,CAAC,CAAA,CAAE,CAAA,CAC3B,KAAK,IAAI;AAAA,GACd;AACF","file":"auth.cjs","sourcesContent":["/**\n * All exceptions for notebooklm-sdk.\n *\n * All errors extend NotebookLMError so you can catch everything with:\n * try { ... } catch (e) { if (e instanceof NotebookLMError) ... }\n */\n\nexport class NotebookLMError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Network (transport-level, before RPC processing)\n// ---------------------------------------------------------------------------\n\nexport class NetworkError extends NotebookLMError {\n readonly methodId?: string;\n readonly originalError?: Error;\n\n constructor(message: string, opts: { methodId?: string; originalError?: Error } = {}) {\n super(message);\n this.methodId = opts.methodId;\n this.originalError = opts.originalError;\n }\n}\n\nexport class RPCTimeoutError extends NetworkError {}\n\n// ---------------------------------------------------------------------------\n// RPC Protocol (after connection established)\n// ---------------------------------------------------------------------------\n\nexport class RPCError extends NotebookLMError {\n readonly methodId?: string;\n readonly rawResponse?: string;\n readonly rpcCode?: string | number;\n readonly foundIds: string[];\n\n constructor(\n message: string,\n opts: {\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n foundIds?: string[];\n } = {},\n ) {\n super(message);\n this.methodId = opts.methodId;\n this.rawResponse = opts.rawResponse ? opts.rawResponse.slice(0, 500) : undefined;\n this.rpcCode = opts.rpcCode;\n this.foundIds = opts.foundIds ?? [];\n }\n}\n\nexport class AuthError extends RPCError {}\n\nexport class RateLimitError extends RPCError {\n readonly retryAfter?: number;\n\n constructor(\n message: string,\n opts: {\n retryAfter?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n foundIds?: string[];\n } = {},\n ) {\n super(message, opts);\n this.retryAfter = opts.retryAfter;\n }\n}\n\nexport class ServerError extends RPCError {\n readonly statusCode?: number;\n\n constructor(\n message: string,\n opts: {\n statusCode?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n } = {},\n ) {\n super(message, opts);\n this.statusCode = opts.statusCode;\n }\n}\n\nexport class ClientError extends RPCError {\n readonly statusCode?: number;\n\n constructor(\n message: string,\n opts: {\n statusCode?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n } = {},\n ) {\n super(message, opts);\n this.statusCode = opts.statusCode;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Notebooks\n// ---------------------------------------------------------------------------\n\nexport class NotebookError extends NotebookLMError {}\n\nexport class NotebookNotFoundError extends NotebookError {\n readonly notebookId: string;\n\n constructor(notebookId: string) {\n super(`Notebook not found: ${notebookId}`);\n this.notebookId = notebookId;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Sources\n// ---------------------------------------------------------------------------\n\nexport class SourceError extends NotebookLMError {}\n\nexport class SourceNotFoundError extends SourceError {\n readonly sourceId: string;\n\n constructor(sourceId: string) {\n super(`Source not found: ${sourceId}`);\n this.sourceId = sourceId;\n }\n}\n\nexport class SourceAddError extends SourceError {\n readonly url: string;\n readonly cause?: Error;\n\n constructor(url: string, opts: { cause?: Error; message?: string } = {}) {\n super(\n opts.message ??\n `Failed to add source: ${url}\\n` +\n \"Possible causes:\\n\" +\n \" - URL is invalid or inaccessible\\n\" +\n \" - Content is behind a paywall or requires authentication\\n\" +\n \" - Rate limiting or quota exceeded\",\n );\n this.url = url;\n this.cause = opts.cause;\n }\n}\n\nexport class SourceProcessingError extends SourceError {\n readonly sourceId: string;\n readonly status: number;\n\n constructor(sourceId: string, status = 3, message?: string) {\n super(message ?? `Source ${sourceId} failed to process`);\n this.sourceId = sourceId;\n this.status = status;\n }\n}\n\nexport class SourceTimeoutError extends SourceError {\n readonly sourceId: string;\n readonly timeout: number;\n readonly lastStatus?: number;\n\n constructor(sourceId: string, timeout: number, lastStatus?: number) {\n const statusInfo = lastStatus != null ? ` (last status: ${lastStatus})` : \"\";\n super(`Source ${sourceId} not ready after ${timeout.toFixed(1)}s${statusInfo}`);\n this.sourceId = sourceId;\n this.timeout = timeout;\n this.lastStatus = lastStatus;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Artifacts\n// ---------------------------------------------------------------------------\n\nexport class ArtifactError extends NotebookLMError {}\n\nexport class ArtifactNotFoundError extends ArtifactError {\n readonly artifactId: string;\n readonly artifactType?: string;\n\n constructor(artifactId: string, artifactType?: string) {\n const typeInfo = artifactType ? ` ${artifactType}` : \"\";\n super(`${typeInfo.trim() || \"Artifact\"} ${artifactId} not found`);\n this.artifactId = artifactId;\n this.artifactType = artifactType;\n }\n}\n\nexport class ArtifactNotReadyError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly status?: string;\n\n constructor(artifactType: string, opts: { artifactId?: string; status?: string } = {}) {\n const base = opts.artifactId\n ? `${artifactType} artifact ${opts.artifactId} is not ready`\n : `No completed ${artifactType} found`;\n const statusInfo = opts.status ? ` (status: ${opts.status})` : \"\";\n super(`${base}${statusInfo}`);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.status = opts.status;\n }\n}\n\nexport class ArtifactParseError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly details?: string;\n readonly cause?: Error;\n\n constructor(\n artifactType: string,\n opts: { details?: string; artifactId?: string; cause?: Error } = {},\n ) {\n let msg = `Failed to parse ${artifactType} artifact`;\n if (opts.artifactId) msg += ` ${opts.artifactId}`;\n if (opts.details) msg += `: ${opts.details}`;\n super(msg);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.details = opts.details;\n this.cause = opts.cause;\n }\n}\n\nexport class ArtifactDownloadError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly details?: string;\n readonly cause?: Error;\n\n constructor(\n artifactType: string,\n opts: { details?: string; artifactId?: string; cause?: Error } = {},\n ) {\n let msg = `Failed to download ${artifactType} artifact`;\n if (opts.artifactId) msg += ` ${opts.artifactId}`;\n if (opts.details) msg += `: ${opts.details}`;\n super(msg);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.details = opts.details;\n this.cause = opts.cause;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Chat\n// ---------------------------------------------------------------------------\n\nexport class ChatError extends NotebookLMError {}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { AuthError } from \"./types/errors.js\";\n\n/** Default session file written by `npx notebooklm-sdk login`. */\nconst DEFAULT_SESSION_FILE = join(homedir(), \".notebooklm\", \"session.json\");\n\nexport interface CookieMap {\n [key: string]: string;\n}\n\nexport interface AuthTokens {\n cookies: CookieMap;\n csrfToken: string;\n sessionId: string;\n cookieHeader: string;\n /** Cookie header containing only .google.com domain cookies — for media downloads */\n googleCookieHeader: string;\n}\n\n// ---------------------------------------------------------------------------\n// Cookie loading\n// ---------------------------------------------------------------------------\n\n/** Load cookies from a Playwright storage_state.json file. */\nexport function loadCookiesFromFile(filePath: string): CookieMap {\n let raw: string;\n try {\n raw = readFileSync(filePath, \"utf-8\");\n } catch {\n throw new AuthError(\n `Session file not found: ${filePath}\\nRun: npx notebooklm-sdk login`,\n );\n }\n return extractCookiesFromStorageState(JSON.parse(raw));\n}\n\n/** Load cookies from a raw Playwright storage state object. */\nexport function loadCookiesFromObject(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): CookieMap {\n return extractCookiesFromStorageState(storageState);\n}\n\n/** Build a cookie header containing only .google.com domain cookies (for media downloads). */\nexport function buildGoogleCookieHeader(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): string {\n const map: CookieMap = {};\n for (const c of storageState.cookies ?? []) {\n if (c.domain === \".google.com\" && c.name && c.value) {\n map[c.name] = map[c.name] ?? c.value;\n }\n }\n return buildCookieHeader(map);\n}\n\n/** Load cookies from a flat cookie map (already parsed). */\nexport function loadCookiesFromMap(map: CookieMap): CookieMap {\n return { ...map };\n}\n\n/** Load cookies from a \"; \"-separated cookie string (e.g. process.env.NOTEBOOKLM_COOKIES). */\nexport function loadCookiesFromString(cookieStr: string): CookieMap {\n const map: CookieMap = {};\n for (const part of cookieStr.split(/;\\s*/)) {\n const idx = part.indexOf(\"=\");\n if (idx > 0) {\n const name = part.slice(0, idx).trim();\n const value = part.slice(idx + 1).trim();\n if (name) map[name] = value;\n }\n }\n return map;\n}\n\nfunction extractCookiesFromStorageState(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): CookieMap {\n const cookies: CookieMap = {};\n const domainTrack: Record<string, string> = {};\n\n for (const cookie of storageState.cookies ?? []) {\n const { domain, name, value } = cookie;\n if (!isAllowedDomain(domain) || !name) continue;\n\n const isBase = domain === \".google.com\";\n if (!(name in cookies) || isBase) {\n cookies[name] = value;\n domainTrack[name] = domain;\n }\n }\n\n if (!cookies[\"SID\"]) {\n throw new AuthError(\n \"Missing required cookie: SID. Session may be invalid or expired.\\nRun: npx notebooklm-sdk login\",\n );\n }\n return cookies;\n}\n\nfunction isAllowedDomain(domain: string): boolean {\n if (\n domain === \".google.com\" ||\n domain === \"notebooklm.google.com\" ||\n domain === \".googleusercontent.com\"\n ) {\n return true;\n }\n if (domain.startsWith(\".google.\")) {\n return true; // Allow all regional Google domains\n }\n return false;\n}\n\nexport function buildCookieHeader(cookies: CookieMap): string {\n return Object.entries(cookies)\n .map(([k, v]) => `${k}=${v}`)\n .join(\"; \");\n}\n\n// ---------------------------------------------------------------------------\n// Token fetching\n// ---------------------------------------------------------------------------\n\nconst NOTEBOOKLM_URL = \"https://notebooklm.google.com/\";\n\nexport async function fetchTokens(\n cookies: CookieMap,\n): Promise<{ csrfToken: string; sessionId: string }> {\n const cookieHeader = buildCookieHeader(cookies);\n\n const response = await fetch(NOTEBOOKLM_URL, {\n headers: { Cookie: cookieHeader },\n redirect: \"follow\",\n });\n\n if (!response.ok) {\n throw new AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);\n }\n\n const finalUrl = response.url;\n if (isGoogleAuthRedirect(finalUrl)) {\n throw new AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);\n }\n\n const html = await response.text();\n const csrfToken = extractCsrfToken(html, finalUrl);\n const sessionId = extractSessionId(html, finalUrl);\n\n return { csrfToken, sessionId };\n}\n\nfunction extractCsrfToken(html: string, finalUrl: string): string {\n const match = /\"SNlM0e\"\\s*:\\s*\"([^\"]+)\"/.exec(html);\n if (!match?.[1]) {\n if (isGoogleAuthRedirect(finalUrl) || html.includes(\"accounts.google.com\")) {\n throw new AuthError(\"Session expired or invalid.\\nRun: npx notebooklm-sdk login\");\n }\n throw new AuthError(\"CSRF token (SNlM0e) not found in NotebookLM page HTML.\");\n }\n return match[1];\n}\n\nfunction extractSessionId(html: string, finalUrl: string): string {\n const match = /\"FdrFJe\"\\s*:\\s*\"([^\"]+)\"/.exec(html);\n if (!match?.[1]) {\n if (isGoogleAuthRedirect(finalUrl) || html.includes(\"accounts.google.com\")) {\n throw new AuthError(\"Session expired or invalid.\\nRun: npx notebooklm-sdk login\");\n }\n throw new AuthError(\"Session ID (FdrFJe) not found in NotebookLM page HTML.\");\n }\n return match[1];\n}\n\nfunction isGoogleAuthRedirect(url: string): boolean {\n return url.includes(\"accounts.google.com\") || url.includes(\"signin\");\n}\n\n// ---------------------------------------------------------------------------\n// Connect options\n// ---------------------------------------------------------------------------\n\nexport interface ConnectOptions {\n /** \"; \"-separated cookie string (e.g. \"SID=abc; HSID=xyz\") */\n cookies?: string;\n /** Path to Playwright storage_state.json */\n cookiesFile?: string;\n /** Pre-parsed cookie map */\n cookiesObject?: CookieMap | { cookies?: Array<{ name: string; value: string; domain: string }> };\n}\n\nexport async function connect(opts: ConnectOptions = {}): Promise<AuthTokens> {\n let cookieMap: CookieMap;\n let googleCookieHeader: string | null = null;\n\n if (opts.cookies) {\n cookieMap = loadCookiesFromString(opts.cookies);\n } else if (opts.cookiesFile) {\n cookieMap = loadCookiesFromFile(opts.cookiesFile);\n } else if (opts.cookiesObject) {\n if (\"cookies\" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {\n const storageState = opts.cookiesObject as {\n cookies: Array<{ name: string; value: string; domain: string }>;\n };\n cookieMap = loadCookiesFromObject(storageState);\n googleCookieHeader = buildGoogleCookieHeader(storageState);\n } else {\n cookieMap = loadCookiesFromMap(opts.cookiesObject as CookieMap);\n }\n } else {\n // Auto-discovery: ~/.notebooklm/session.json → ./storage_state.json → env var\n const envCookies = process.env[\"NOTEBOOKLM_COOKIES\"];\n const envFile = process.env[\"NOTEBOOKLM_COOKIES_FILE\"];\n if (envFile) {\n cookieMap = loadCookiesFromFile(envFile);\n } else if (existsSync(DEFAULT_SESSION_FILE)) {\n const raw = readFileSync(DEFAULT_SESSION_FILE, \"utf-8\");\n const storageState = JSON.parse(raw);\n cookieMap = loadCookiesFromObject(storageState);\n googleCookieHeader = buildGoogleCookieHeader(storageState);\n } else if (existsSync(\"storage_state.json\")) {\n const raw = readFileSync(\"storage_state.json\", \"utf-8\");\n const storageState = JSON.parse(raw);\n cookieMap = loadCookiesFromObject(storageState);\n googleCookieHeader = buildGoogleCookieHeader(storageState);\n } else if (envCookies) {\n cookieMap = loadCookiesFromString(envCookies);\n } else {\n throw new AuthError(\n \"No session found. Run: npx notebooklm-sdk login\",\n );\n }\n }\n\n const { csrfToken, sessionId } = await fetchTokens(cookieMap);\n const cookieHeader = buildCookieHeader(cookieMap);\n\n return {\n cookies: cookieMap,\n csrfToken,\n sessionId,\n cookieHeader,\n googleCookieHeader: googleCookieHeader ?? cookieHeader,\n };\n}\n","import { existsSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { type BrowserContext, chromium, type Page } from \"playwright\";\nimport { type CookieMap, loadCookiesFromObject } from \"../auth.js\";\n\n/** Default directory for storing NotebookLM session files (~/.notebooklm). */\nexport const DEFAULT_SESSION_DIR = join(homedir(), \".notebooklm\");\n/** Default session file path (~/.notebooklm/session.json). */\nexport const DEFAULT_SESSION_FILE = join(DEFAULT_SESSION_DIR, \"session.json\");\n\nexport interface LoginOptions {\n /**\n * Path to a directory for a persistent browser profile.\n * Defaults to ~/.notebooklm/.auth_profile.\n */\n persistFolder?: string;\n /**\n * Browser type to use. Default is \"chromium\".\n */\n browserType?: \"chromium\" | \"msedge\";\n /**\n * Whether to run the browser in headless mode. Default is false.\n * Headless login is usually blocked by Google, so this is mostly for testing or refreshes.\n */\n headless?: boolean;\n}\n\nconst NOTEBOOKLM_URL = \"https://notebooklm.google.com/\";\nconst GOOGLE_ACCOUNTS_URL = \"https://accounts.google.com/\";\n\n/**\n * Log in to NotebookLM via a headful browser window.\n *\n * Flow:\n * 1. Opens browser to NotebookLM.\n * 2. If already logged in (via persistFolder), it proceeds.\n * 3. If not, it waits for the user to reach the home page.\n * 4. Captures cookies and returns them.\n */\nexport async function login(opts: LoginOptions = {}): Promise<{\n cookies: CookieMap;\n storageState: any;\n cookieHeader: string;\n}> {\n const {\n persistFolder = join(DEFAULT_SESSION_DIR, \".auth_profile\"),\n headless = false,\n browserType = \"chromium\",\n } = opts;\n\n // Pre-flight: verify Playwright browser is installed\n try {\n chromium.executablePath();\n } catch {\n throw new Error(\n \"Playwright browser not found. Run: npx playwright install chromium\",\n );\n }\n\n // Ensure the session directory exists\n if (!existsSync(DEFAULT_SESSION_DIR)) {\n mkdirSync(DEFAULT_SESSION_DIR, { recursive: true });\n }\n\n let context: BrowserContext;\n\n const launchOptions = {\n headless,\n args: [\"--disable-blink-features=AutomationControlled\"],\n };\n\n context = await chromium.launchPersistentContext(persistFolder, {\n ...launchOptions,\n channel: browserType === \"msedge\" ? \"msedge\" : undefined,\n });\n\n const page = context.pages()[0] || (await context.newPage());\n await page.goto(NOTEBOOKLM_URL);\n\n // Check if we are on the login page\n if (page.url().includes(\"accounts.google.com\")) {\n console.log(\"Please log in to Google in the browser window...\");\n\n // Wait for navigation back to NotebookLM or successful login indicator\n // We poll until the URL includes notebooklm.google.com and it's not a generic landing page\n await page.waitForURL(\n (url) => {\n return url.hostname === \"notebooklm.google.com\" && !url.pathname.includes(\"/login\");\n },\n { timeout: 0 },\n ); // No timeout, wait for user\n }\n\n // Ensure we are fully loaded on the accounts domain too to capture those cookies\n await page.goto(GOOGLE_ACCOUNTS_URL, { waitUntil: \"load\" });\n await page.goto(NOTEBOOKLM_URL, { waitUntil: \"load\" });\n\n const storageState = await context.storageState();\n const cookies = loadCookiesFromObject(storageState as any);\n\n await context.close();\n\n return {\n cookies,\n storageState,\n cookieHeader: Object.entries(cookies)\n .map(([k, v]) => `${k}=${v}`)\n .join(\"; \"),\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/types/errors.ts","../src/auth.ts","../src/auth/browser.ts"],"names":["join","homedir","readFileSync","existsSync","DEFAULT_SESSION_FILE","NOTEBOOKLM_URL","chromium","mkdirSync"],"mappings":"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","file":"auth.cjs","sourcesContent":["/**\n * All exceptions for notebooklm-sdk.\n *\n * All errors extend NotebookLMError so you can catch everything with:\n * try { ... } catch (e) { if (e instanceof NotebookLMError) ... }\n */\n\nexport class NotebookLMError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Network (transport-level, before RPC processing)\n// ---------------------------------------------------------------------------\n\nexport class NetworkError extends NotebookLMError {\n readonly methodId?: string;\n readonly originalError?: Error;\n\n constructor(message: string, opts: { methodId?: string; originalError?: Error } = {}) {\n super(message);\n this.methodId = opts.methodId;\n this.originalError = opts.originalError;\n }\n}\n\nexport class RPCTimeoutError extends NetworkError {}\n\n// ---------------------------------------------------------------------------\n// RPC Protocol (after connection established)\n// ---------------------------------------------------------------------------\n\nexport class RPCError extends NotebookLMError {\n readonly methodId?: string;\n readonly rawResponse?: string;\n readonly rpcCode?: string | number;\n readonly foundIds: string[];\n\n constructor(\n message: string,\n opts: {\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n foundIds?: string[];\n } = {},\n ) {\n super(message);\n this.methodId = opts.methodId;\n this.rawResponse = opts.rawResponse ? opts.rawResponse.slice(0, 500) : undefined;\n this.rpcCode = opts.rpcCode;\n this.foundIds = opts.foundIds ?? [];\n }\n}\n\nexport class AuthError extends RPCError {}\n\nexport class RateLimitError extends RPCError {\n readonly retryAfter?: number;\n\n constructor(\n message: string,\n opts: {\n retryAfter?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n foundIds?: string[];\n } = {},\n ) {\n super(message, opts);\n this.retryAfter = opts.retryAfter;\n }\n}\n\nexport class ServerError extends RPCError {\n readonly statusCode?: number;\n\n constructor(\n message: string,\n opts: {\n statusCode?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n } = {},\n ) {\n super(message, opts);\n this.statusCode = opts.statusCode;\n }\n}\n\nexport class ClientError extends RPCError {\n readonly statusCode?: number;\n\n constructor(\n message: string,\n opts: {\n statusCode?: number;\n methodId?: string;\n rawResponse?: string;\n rpcCode?: string | number;\n } = {},\n ) {\n super(message, opts);\n this.statusCode = opts.statusCode;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Notebooks\n// ---------------------------------------------------------------------------\n\nexport class NotebookError extends NotebookLMError {}\n\nexport class NotebookNotFoundError extends NotebookError {\n readonly notebookId: string;\n\n constructor(notebookId: string) {\n super(`Notebook not found: ${notebookId}`);\n this.notebookId = notebookId;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Sources\n// ---------------------------------------------------------------------------\n\nexport class SourceError extends NotebookLMError {}\n\nexport class SourceNotFoundError extends SourceError {\n readonly sourceId: string;\n\n constructor(sourceId: string) {\n super(`Source not found: ${sourceId}`);\n this.sourceId = sourceId;\n }\n}\n\nexport class SourceAddError extends SourceError {\n readonly url: string;\n readonly cause?: Error;\n\n constructor(url: string, opts: { cause?: Error; message?: string } = {}) {\n super(\n opts.message ??\n `Failed to add source: ${url}\\n` +\n \"Possible causes:\\n\" +\n \" - URL is invalid or inaccessible\\n\" +\n \" - Content is behind a paywall or requires authentication\\n\" +\n \" - Rate limiting or quota exceeded\",\n );\n this.url = url;\n this.cause = opts.cause;\n }\n}\n\nexport class SourceProcessingError extends SourceError {\n readonly sourceId: string;\n readonly status: number;\n\n constructor(sourceId: string, status = 3, message?: string) {\n super(message ?? `Source ${sourceId} failed to process`);\n this.sourceId = sourceId;\n this.status = status;\n }\n}\n\nexport class SourceTimeoutError extends SourceError {\n readonly sourceId: string;\n readonly timeout: number;\n readonly lastStatus?: number;\n\n constructor(sourceId: string, timeout: number, lastStatus?: number) {\n const statusInfo = lastStatus != null ? ` (last status: ${lastStatus})` : \"\";\n super(`Source ${sourceId} not ready after ${timeout.toFixed(1)}s${statusInfo}`);\n this.sourceId = sourceId;\n this.timeout = timeout;\n this.lastStatus = lastStatus;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Artifacts\n// ---------------------------------------------------------------------------\n\nexport class ArtifactError extends NotebookLMError {}\n\nexport class ArtifactNotFoundError extends ArtifactError {\n readonly artifactId: string;\n readonly artifactType?: string;\n\n constructor(artifactId: string, artifactType?: string) {\n const typeInfo = artifactType ? ` ${artifactType}` : \"\";\n super(`${typeInfo.trim() || \"Artifact\"} ${artifactId} not found`);\n this.artifactId = artifactId;\n this.artifactType = artifactType;\n }\n}\n\nexport class ArtifactNotReadyError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly status?: string;\n\n constructor(artifactType: string, opts: { artifactId?: string; status?: string } = {}) {\n const base = opts.artifactId\n ? `${artifactType} artifact ${opts.artifactId} is not ready`\n : `No completed ${artifactType} found`;\n const statusInfo = opts.status ? ` (status: ${opts.status})` : \"\";\n super(`${base}${statusInfo}`);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.status = opts.status;\n }\n}\n\nexport class ArtifactParseError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly details?: string;\n readonly cause?: Error;\n\n constructor(\n artifactType: string,\n opts: { details?: string; artifactId?: string; cause?: Error } = {},\n ) {\n let msg = `Failed to parse ${artifactType} artifact`;\n if (opts.artifactId) msg += ` ${opts.artifactId}`;\n if (opts.details) msg += `: ${opts.details}`;\n super(msg);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.details = opts.details;\n this.cause = opts.cause;\n }\n}\n\nexport class ArtifactDownloadError extends ArtifactError {\n readonly artifactType: string;\n readonly artifactId?: string;\n readonly details?: string;\n readonly cause?: Error;\n\n constructor(\n artifactType: string,\n opts: { details?: string; artifactId?: string; cause?: Error } = {},\n ) {\n let msg = `Failed to download ${artifactType} artifact`;\n if (opts.artifactId) msg += ` ${opts.artifactId}`;\n if (opts.details) msg += `: ${opts.details}`;\n super(msg);\n this.artifactType = artifactType;\n this.artifactId = opts.artifactId;\n this.details = opts.details;\n this.cause = opts.cause;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Domain: Chat\n// ---------------------------------------------------------------------------\n\nexport class ChatError extends NotebookLMError {}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { AuthError } from \"./types/errors.js\";\n\n/** Default session file written by `npx notebooklm-sdk login`. */\nconst DEFAULT_SESSION_FILE = join(homedir(), \".notebooklm\", \"session.json\");\n\nexport interface CookieMap {\n [key: string]: string;\n}\n\nexport interface AuthTokens {\n cookies: CookieMap;\n csrfToken: string;\n sessionId: string;\n cookieHeader: string;\n /** Cookie header containing only .google.com domain cookies — for media downloads */\n googleCookieHeader: string;\n}\n\n// ---------------------------------------------------------------------------\n// Cookie loading\n// ---------------------------------------------------------------------------\n\n/** Load cookies from a Playwright storage_state.json file. */\nexport function loadCookiesFromFile(filePath: string): CookieMap {\n let raw: string;\n try {\n raw = readFileSync(filePath, \"utf-8\");\n } catch {\n throw new AuthError(`Session file not found: ${filePath}\\nRun: npx notebooklm-sdk login`);\n }\n return extractCookiesFromStorageState(JSON.parse(raw));\n}\n\n/** Load cookies from a raw Playwright storage state object. */\nexport function loadCookiesFromObject(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): CookieMap {\n return extractCookiesFromStorageState(storageState);\n}\n\n/** Build a cookie header containing only .google.com domain cookies (for media downloads). */\nexport function buildGoogleCookieHeader(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): string {\n const map: CookieMap = {};\n for (const c of storageState.cookies ?? []) {\n if (c.domain === \".google.com\" && c.name && c.value) {\n map[c.name] = map[c.name] ?? c.value;\n }\n }\n return buildCookieHeader(map);\n}\n\n/** Load cookies from a flat cookie map (already parsed). */\nexport function loadCookiesFromMap(map: CookieMap): CookieMap {\n return { ...map };\n}\n\n/** Load cookies from a \"; \"-separated cookie string (e.g. process.env.NOTEBOOKLM_COOKIES). */\nexport function loadCookiesFromString(cookieStr: string): CookieMap {\n const map: CookieMap = {};\n for (const part of cookieStr.split(/;\\s*/)) {\n const idx = part.indexOf(\"=\");\n if (idx > 0) {\n const name = part.slice(0, idx).trim();\n const value = part.slice(idx + 1).trim();\n if (name) map[name] = value;\n }\n }\n return map;\n}\n\nfunction extractCookiesFromStorageState(storageState: {\n cookies?: Array<{ name: string; value: string; domain: string }>;\n}): CookieMap {\n const cookies: CookieMap = {};\n const domainTrack: Record<string, string> = {};\n\n for (const cookie of storageState.cookies ?? []) {\n const { domain, name, value } = cookie;\n if (!isAllowedDomain(domain) || !name) continue;\n\n const isBase = domain === \".google.com\";\n if (!(name in cookies) || isBase) {\n cookies[name] = value;\n domainTrack[name] = domain;\n }\n }\n\n if (!cookies[\"SID\"]) {\n throw new AuthError(\n \"Missing required cookie: SID. Session may be invalid or expired.\\nRun: npx notebooklm-sdk login\",\n );\n }\n return cookies;\n}\n\nfunction isAllowedDomain(domain: string): boolean {\n if (\n domain === \".google.com\" ||\n domain === \"notebooklm.google.com\" ||\n domain === \".googleusercontent.com\"\n ) {\n return true;\n }\n if (domain.startsWith(\".google.\")) {\n return true; // Allow all regional Google domains\n }\n return false;\n}\n\nexport function buildCookieHeader(cookies: CookieMap): string {\n return Object.entries(cookies)\n .map(([k, v]) => `${k}=${v}`)\n .join(\"; \");\n}\n\n// ---------------------------------------------------------------------------\n// Token fetching\n// ---------------------------------------------------------------------------\n\nconst NOTEBOOKLM_URL = \"https://notebooklm.google.com/\";\n\nexport async function fetchTokens(\n cookies: CookieMap,\n): Promise<{ csrfToken: string; sessionId: string }> {\n const cookieHeader = buildCookieHeader(cookies);\n\n const response = await fetch(NOTEBOOKLM_URL, {\n headers: { Cookie: cookieHeader },\n redirect: \"follow\",\n });\n\n if (!response.ok) {\n throw new AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);\n }\n\n const finalUrl = response.url;\n if (isGoogleAuthRedirect(finalUrl)) {\n throw new AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);\n }\n\n const html = await response.text();\n const csrfToken = extractCsrfToken(html, finalUrl);\n const sessionId = extractSessionId(html, finalUrl);\n\n return { csrfToken, sessionId };\n}\n\nfunction extractCsrfToken(html: string, finalUrl: string): string {\n const match = /\"SNlM0e\"\\s*:\\s*\"([^\"]+)\"/.exec(html);\n if (!match?.[1]) {\n if (isGoogleAuthRedirect(finalUrl) || html.includes(\"accounts.google.com\")) {\n throw new AuthError(\"Session expired or invalid.\\nRun: npx notebooklm-sdk login\");\n }\n throw new AuthError(\"CSRF token (SNlM0e) not found in NotebookLM page HTML.\");\n }\n return match[1];\n}\n\nfunction extractSessionId(html: string, finalUrl: string): string {\n const match = /\"FdrFJe\"\\s*:\\s*\"([^\"]+)\"/.exec(html);\n if (!match?.[1]) {\n if (isGoogleAuthRedirect(finalUrl) || html.includes(\"accounts.google.com\")) {\n throw new AuthError(\"Session expired or invalid.\\nRun: npx notebooklm-sdk login\");\n }\n throw new AuthError(\"Session ID (FdrFJe) not found in NotebookLM page HTML.\");\n }\n return match[1];\n}\n\nfunction isGoogleAuthRedirect(url: string): boolean {\n return url.includes(\"accounts.google.com\") || url.includes(\"signin\");\n}\n\n// ---------------------------------------------------------------------------\n// Connect options\n// ---------------------------------------------------------------------------\n\nexport interface ConnectOptions {\n /** \"; \"-separated cookie string (e.g. \"SID=abc; HSID=xyz\") */\n cookies?: string;\n /** Path to Playwright storage_state.json */\n cookiesFile?: string;\n /** Pre-parsed cookie map */\n cookiesObject?: CookieMap | { cookies?: Array<{ name: string; value: string; domain: string }> };\n}\n\nexport async function connect(opts: ConnectOptions = {}): Promise<AuthTokens> {\n let cookieMap: CookieMap;\n let googleCookieHeader: string | null = null;\n\n if (opts.cookies) {\n cookieMap = loadCookiesFromString(opts.cookies);\n } else if (opts.cookiesFile) {\n cookieMap = loadCookiesFromFile(opts.cookiesFile);\n } else if (opts.cookiesObject) {\n if (\"cookies\" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {\n const storageState = opts.cookiesObject as {\n cookies: Array<{ name: string; value: string; domain: string }>;\n };\n cookieMap = loadCookiesFromObject(storageState);\n googleCookieHeader = buildGoogleCookieHeader(storageState);\n } else {\n cookieMap = loadCookiesFromMap(opts.cookiesObject as CookieMap);\n }\n } else {\n // Auto-discovery: ~/.notebooklm/session.json → ./storage_state.json → env var\n const envCookies = process.env[\"NOTEBOOKLM_COOKIES\"];\n const envFile = process.env[\"NOTEBOOKLM_COOKIES_FILE\"];\n if (envFile) {\n cookieMap = loadCookiesFromFile(envFile);\n } else if (existsSync(DEFAULT_SESSION_FILE)) {\n const raw = readFileSync(DEFAULT_SESSION_FILE, \"utf-8\");\n const storageState = JSON.parse(raw);\n cookieMap = loadCookiesFromObject(storageState);\n googleCookieHeader = buildGoogleCookieHeader(storageState);\n } else if (existsSync(\"storage_state.json\")) {\n const raw = readFileSync(\"storage_state.json\", \"utf-8\");\n const storageState = JSON.parse(raw);\n cookieMap = loadCookiesFromObject(storageState);\n googleCookieHeader = buildGoogleCookieHeader(storageState);\n } else if (envCookies) {\n cookieMap = loadCookiesFromString(envCookies);\n } else {\n throw new AuthError(\"No session found. Run: npx notebooklm-sdk login\");\n }\n }\n\n const { csrfToken, sessionId } = await fetchTokens(cookieMap);\n const cookieHeader = buildCookieHeader(cookieMap);\n\n return {\n cookies: cookieMap,\n csrfToken,\n sessionId,\n cookieHeader,\n googleCookieHeader: googleCookieHeader ?? cookieHeader,\n };\n}\n","import { existsSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { type BrowserContext, chromium, type Page } from \"playwright\";\nimport { type CookieMap, loadCookiesFromObject } from \"../auth.js\";\n\n/** Default directory for storing NotebookLM session files (~/.notebooklm). */\nexport const DEFAULT_SESSION_DIR = join(homedir(), \".notebooklm\");\n/** Default session file path (~/.notebooklm/session.json). */\nexport const DEFAULT_SESSION_FILE = join(DEFAULT_SESSION_DIR, \"session.json\");\n\nexport interface LoginOptions {\n /**\n * Path to a directory for a persistent browser profile.\n * Defaults to ~/.notebooklm/.auth_profile.\n */\n persistFolder?: string;\n /**\n * Browser type to use. Default is \"chromium\".\n */\n browserType?: \"chromium\" | \"msedge\";\n /**\n * Whether to run the browser in headless mode. Default is false.\n * Headless login is usually blocked by Google, so this is mostly for testing or refreshes.\n */\n headless?: boolean;\n}\n\nconst NOTEBOOKLM_URL = \"https://notebooklm.google.com/\";\nconst GOOGLE_ACCOUNTS_URL = \"https://accounts.google.com/\";\n\n/**\n * Log in to NotebookLM via a headful browser window.\n *\n * Flow:\n * 1. Opens browser to NotebookLM.\n * 2. If already logged in (via persistFolder), it proceeds.\n * 3. If not, it waits for the user to reach the home page.\n * 4. Captures cookies and returns them.\n */\nexport async function login(opts: LoginOptions = {}): Promise<{\n cookies: CookieMap;\n storageState: any;\n cookieHeader: string;\n}> {\n const {\n persistFolder = join(DEFAULT_SESSION_DIR, \".auth_profile\"),\n headless = false,\n browserType = \"chromium\",\n } = opts;\n\n // Pre-flight: verify Playwright browser is installed\n try {\n chromium.executablePath();\n } catch {\n throw new Error(\"Playwright browser not found. Run: npx playwright install chromium\");\n }\n\n // Ensure the session directory exists\n if (!existsSync(DEFAULT_SESSION_DIR)) {\n mkdirSync(DEFAULT_SESSION_DIR, { recursive: true });\n }\n\n let context: BrowserContext;\n\n const launchOptions = {\n headless,\n args: [\"--disable-blink-features=AutomationControlled\"],\n };\n\n context = await chromium.launchPersistentContext(persistFolder, {\n ...launchOptions,\n channel: browserType === \"msedge\" ? \"msedge\" : undefined,\n });\n\n const page = context.pages()[0] || (await context.newPage());\n await page.goto(NOTEBOOKLM_URL);\n\n // Check if we are on the login page\n if (page.url().includes(\"accounts.google.com\")) {\n console.log(\"Please log in to Google in the browser window...\");\n\n // Wait for navigation back to NotebookLM or successful login indicator\n // We poll until the URL includes notebooklm.google.com and it's not a generic landing page\n await page.waitForURL(\n (url) => {\n return url.hostname === \"notebooklm.google.com\" && !url.pathname.includes(\"/login\");\n },\n { timeout: 0 },\n ); // No timeout, wait for user\n }\n\n // Ensure we are fully loaded on the accounts domain too to capture those cookies\n await page.goto(GOOGLE_ACCOUNTS_URL, { waitUntil: \"load\" });\n await page.goto(NOTEBOOKLM_URL, { waitUntil: \"load\" });\n\n const storageState = await context.storageState();\n const cookies = loadCookiesFromObject(storageState as any);\n\n await context.close();\n\n return {\n cookies,\n storageState,\n cookieHeader: Object.entries(cookies)\n .map(([k, v]) => `${k}=${v}`)\n .join(\"; \"),\n };\n}\n"]}
package/dist/auth.js CHANGED
@@ -35,10 +35,8 @@ function loadCookiesFromFile(filePath) {
35
35
  try {
36
36
  raw = readFileSync(filePath, "utf-8");
37
37
  } catch {
38
- throw new AuthError(
39
- `Session file not found: ${filePath}
40
- Run: npx notebooklm-sdk login`
41
- );
38
+ throw new AuthError(`Session file not found: ${filePath}
39
+ Run: npx notebooklm-sdk login`);
42
40
  }
43
41
  return extractCookiesFromStorageState(JSON.parse(raw));
44
42
  }
@@ -173,9 +171,7 @@ async function connect(opts = {}) {
173
171
  } else if (envCookies) {
174
172
  cookieMap = loadCookiesFromString(envCookies);
175
173
  } else {
176
- throw new AuthError(
177
- "No session found. Run: npx notebooklm-sdk login"
178
- );
174
+ throw new AuthError("No session found. Run: npx notebooklm-sdk login");
179
175
  }
180
176
  }
181
177
  const { csrfToken, sessionId } = await fetchTokens(cookieMap);
@@ -201,9 +197,7 @@ async function login(opts = {}) {
201
197
  try {
202
198
  chromium.executablePath();
203
199
  } catch {
204
- throw new Error(
205
- "Playwright browser not found. Run: npx playwright install chromium"
206
- );
200
+ throw new Error("Playwright browser not found. Run: npx playwright install chromium");
207
201
  }
208
202
  if (!existsSync(DEFAULT_SESSION_DIR)) {
209
203
  mkdirSync(DEFAULT_SESSION_DIR, { recursive: true });