notebooklm-sdk 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -311,356 +311,176 @@ var init_enums = __esm({
311
311
  }
312
312
  });
313
313
 
314
+ // src/api/artifacts.ts
315
+ init_enums();
316
+
314
317
  // src/types/errors.ts
315
- exports.NotebookLMError = void 0; exports.NetworkError = void 0; exports.RPCTimeoutError = void 0; exports.RPCError = void 0; exports.AuthError = void 0; exports.RateLimitError = void 0; exports.ServerError = void 0; exports.ClientError = void 0; exports.NotebookError = void 0; exports.NotebookNotFoundError = void 0; exports.SourceError = void 0; exports.SourceNotFoundError = void 0; exports.SourceAddError = void 0; exports.SourceProcessingError = void 0; exports.SourceTimeoutError = void 0; exports.ArtifactError = void 0; exports.ArtifactNotFoundError = void 0; exports.ArtifactNotReadyError = void 0; exports.ArtifactParseError = void 0; exports.ArtifactDownloadError = void 0; exports.ChatError = void 0;
316
- var init_errors = __esm({
317
- "src/types/errors.ts"() {
318
- exports.NotebookLMError = class extends Error {
319
- constructor(message) {
320
- super(message);
321
- this.name = this.constructor.name;
322
- }
323
- };
324
- exports.NetworkError = class extends exports.NotebookLMError {
325
- methodId;
326
- originalError;
327
- constructor(message, opts = {}) {
328
- super(message);
329
- this.methodId = opts.methodId;
330
- this.originalError = opts.originalError;
331
- }
332
- };
333
- exports.RPCTimeoutError = class extends exports.NetworkError {
334
- };
335
- exports.RPCError = class extends exports.NotebookLMError {
336
- methodId;
337
- rawResponse;
338
- rpcCode;
339
- foundIds;
340
- constructor(message, opts = {}) {
341
- super(message);
342
- this.methodId = opts.methodId;
343
- this.rawResponse = opts.rawResponse ? opts.rawResponse.slice(0, 500) : void 0;
344
- this.rpcCode = opts.rpcCode;
345
- this.foundIds = opts.foundIds ?? [];
346
- }
347
- };
348
- exports.AuthError = class extends exports.RPCError {
349
- };
350
- exports.RateLimitError = class extends exports.RPCError {
351
- retryAfter;
352
- constructor(message, opts = {}) {
353
- super(message, opts);
354
- this.retryAfter = opts.retryAfter;
355
- }
356
- };
357
- exports.ServerError = class extends exports.RPCError {
358
- statusCode;
359
- constructor(message, opts = {}) {
360
- super(message, opts);
361
- this.statusCode = opts.statusCode;
362
- }
363
- };
364
- exports.ClientError = class extends exports.RPCError {
365
- statusCode;
366
- constructor(message, opts = {}) {
367
- super(message, opts);
368
- this.statusCode = opts.statusCode;
369
- }
370
- };
371
- exports.NotebookError = class extends exports.NotebookLMError {
372
- };
373
- exports.NotebookNotFoundError = class extends exports.NotebookError {
374
- notebookId;
375
- constructor(notebookId) {
376
- super(`Notebook not found: ${notebookId}`);
377
- this.notebookId = notebookId;
378
- }
379
- };
380
- exports.SourceError = class extends exports.NotebookLMError {
381
- };
382
- exports.SourceNotFoundError = class extends exports.SourceError {
383
- sourceId;
384
- constructor(sourceId) {
385
- super(`Source not found: ${sourceId}`);
386
- this.sourceId = sourceId;
387
- }
388
- };
389
- exports.SourceAddError = class extends exports.SourceError {
390
- url;
391
- cause;
392
- constructor(url, opts = {}) {
393
- super(
394
- opts.message ?? `Failed to add source: ${url}
395
- Possible causes:
396
- - URL is invalid or inaccessible
397
- - Content is behind a paywall or requires authentication
398
- - Rate limiting or quota exceeded`
399
- );
400
- this.url = url;
401
- this.cause = opts.cause;
402
- }
403
- };
404
- exports.SourceProcessingError = class extends exports.SourceError {
405
- sourceId;
406
- status;
407
- constructor(sourceId, status = 3, message) {
408
- super(message ?? `Source ${sourceId} failed to process`);
409
- this.sourceId = sourceId;
410
- this.status = status;
411
- }
412
- };
413
- exports.SourceTimeoutError = class extends exports.SourceError {
414
- sourceId;
415
- timeout;
416
- lastStatus;
417
- constructor(sourceId, timeout, lastStatus) {
418
- const statusInfo = lastStatus != null ? ` (last status: ${lastStatus})` : "";
419
- super(`Source ${sourceId} not ready after ${timeout.toFixed(1)}s${statusInfo}`);
420
- this.sourceId = sourceId;
421
- this.timeout = timeout;
422
- this.lastStatus = lastStatus;
423
- }
424
- };
425
- exports.ArtifactError = class extends exports.NotebookLMError {
426
- };
427
- exports.ArtifactNotFoundError = class extends exports.ArtifactError {
428
- artifactId;
429
- artifactType;
430
- constructor(artifactId, artifactType) {
431
- const typeInfo = artifactType ? ` ${artifactType}` : "";
432
- super(`${typeInfo.trim() || "Artifact"} ${artifactId} not found`);
433
- this.artifactId = artifactId;
434
- this.artifactType = artifactType;
435
- }
436
- };
437
- exports.ArtifactNotReadyError = class extends exports.ArtifactError {
438
- artifactType;
439
- artifactId;
440
- status;
441
- constructor(artifactType, opts = {}) {
442
- const base = opts.artifactId ? `${artifactType} artifact ${opts.artifactId} is not ready` : `No completed ${artifactType} found`;
443
- const statusInfo = opts.status ? ` (status: ${opts.status})` : "";
444
- super(`${base}${statusInfo}`);
445
- this.artifactType = artifactType;
446
- this.artifactId = opts.artifactId;
447
- this.status = opts.status;
448
- }
449
- };
450
- exports.ArtifactParseError = class extends exports.ArtifactError {
451
- artifactType;
452
- artifactId;
453
- details;
454
- cause;
455
- constructor(artifactType, opts = {}) {
456
- let msg = `Failed to parse ${artifactType} artifact`;
457
- if (opts.artifactId) msg += ` ${opts.artifactId}`;
458
- if (opts.details) msg += `: ${opts.details}`;
459
- super(msg);
460
- this.artifactType = artifactType;
461
- this.artifactId = opts.artifactId;
462
- this.details = opts.details;
463
- this.cause = opts.cause;
464
- }
465
- };
466
- exports.ArtifactDownloadError = class extends exports.ArtifactError {
467
- artifactType;
468
- artifactId;
469
- details;
470
- cause;
471
- constructor(artifactType, opts = {}) {
472
- let msg = `Failed to download ${artifactType} artifact`;
473
- if (opts.artifactId) msg += ` ${opts.artifactId}`;
474
- if (opts.details) msg += `: ${opts.details}`;
475
- super(msg);
476
- this.artifactType = artifactType;
477
- this.artifactId = opts.artifactId;
478
- this.details = opts.details;
479
- this.cause = opts.cause;
480
- }
481
- };
482
- exports.ChatError = class extends exports.NotebookLMError {
483
- };
318
+ var NotebookLMError = class extends Error {
319
+ constructor(message) {
320
+ super(message);
321
+ this.name = this.constructor.name;
484
322
  }
485
- });
486
-
487
- // src/auth.ts
488
- var auth_exports = {};
489
- __export(auth_exports, {
490
- buildCookieHeader: () => buildCookieHeader,
491
- buildGoogleCookieHeader: () => buildGoogleCookieHeader,
492
- connect: () => connect,
493
- fetchTokens: () => fetchTokens,
494
- loadCookiesFromFile: () => loadCookiesFromFile,
495
- loadCookiesFromMap: () => loadCookiesFromMap,
496
- loadCookiesFromObject: () => loadCookiesFromObject,
497
- loadCookiesFromString: () => loadCookiesFromString
498
- });
499
- function loadCookiesFromFile(filePath) {
500
- let raw;
501
- try {
502
- raw = fs.readFileSync(filePath, "utf-8");
503
- } catch {
504
- throw new exports.AuthError(`Session file not found: ${filePath}
505
- Run: npx notebooklm-sdk login`);
323
+ };
324
+ var NetworkError = class extends NotebookLMError {
325
+ methodId;
326
+ originalError;
327
+ constructor(message, opts = {}) {
328
+ super(message);
329
+ this.methodId = opts.methodId;
330
+ this.originalError = opts.originalError;
506
331
  }
507
- return extractCookiesFromStorageState(JSON.parse(raw));
508
- }
509
- function loadCookiesFromObject(storageState) {
510
- return extractCookiesFromStorageState(storageState);
511
- }
512
- function buildGoogleCookieHeader(storageState) {
513
- const map = {};
514
- for (const c of storageState.cookies ?? []) {
515
- if (c.domain === ".google.com" && c.name && c.value) {
516
- map[c.name] = map[c.name] ?? c.value;
517
- }
332
+ };
333
+ var RPCTimeoutError = class extends NetworkError {
334
+ };
335
+ var RPCError = class extends NotebookLMError {
336
+ methodId;
337
+ rawResponse;
338
+ rpcCode;
339
+ foundIds;
340
+ constructor(message, opts = {}) {
341
+ super(message);
342
+ this.methodId = opts.methodId;
343
+ this.rawResponse = opts.rawResponse ? opts.rawResponse.slice(0, 500) : void 0;
344
+ this.rpcCode = opts.rpcCode;
345
+ this.foundIds = opts.foundIds ?? [];
518
346
  }
519
- return buildCookieHeader(map);
520
- }
521
- function loadCookiesFromMap(map) {
522
- return { ...map };
523
- }
524
- function loadCookiesFromString(cookieStr) {
525
- const map = {};
526
- for (const part of cookieStr.split(/;\s*/)) {
527
- const idx = part.indexOf("=");
528
- if (idx > 0) {
529
- const name = part.slice(0, idx).trim();
530
- const value = part.slice(idx + 1).trim();
531
- if (name) map[name] = value;
532
- }
347
+ };
348
+ var AuthError = class extends RPCError {
349
+ };
350
+ var RateLimitError = class extends RPCError {
351
+ retryAfter;
352
+ constructor(message, opts = {}) {
353
+ super(message, opts);
354
+ this.retryAfter = opts.retryAfter;
533
355
  }
534
- return map;
535
- }
536
- function extractCookiesFromStorageState(storageState) {
537
- const cookies = {};
538
- for (const cookie of storageState.cookies ?? []) {
539
- const { domain, name, value } = cookie;
540
- if (!isAllowedDomain(domain) || !name) continue;
541
- const isBase = domain === ".google.com";
542
- if (!(name in cookies) || isBase) {
543
- cookies[name] = value;
544
- }
356
+ };
357
+ var ServerError = class extends RPCError {
358
+ statusCode;
359
+ constructor(message, opts = {}) {
360
+ super(message, opts);
361
+ this.statusCode = opts.statusCode;
545
362
  }
546
- if (!cookies["SID"]) {
547
- throw new exports.AuthError(
548
- "Missing required cookie: SID. Session may be invalid or expired.\nRun: npx notebooklm-sdk login"
549
- );
363
+ };
364
+ var ClientError = class extends RPCError {
365
+ statusCode;
366
+ constructor(message, opts = {}) {
367
+ super(message, opts);
368
+ this.statusCode = opts.statusCode;
550
369
  }
551
- return cookies;
552
- }
553
- function isAllowedDomain(domain) {
554
- if (domain === ".google.com" || domain === "notebooklm.google.com" || domain === ".googleusercontent.com") {
555
- return true;
370
+ };
371
+ var NotebookError = class extends NotebookLMError {
372
+ };
373
+ var NotebookNotFoundError = class extends NotebookError {
374
+ notebookId;
375
+ constructor(notebookId) {
376
+ super(`Notebook not found: ${notebookId}`);
377
+ this.notebookId = notebookId;
556
378
  }
557
- if (domain.startsWith(".google.")) {
558
- return true;
379
+ };
380
+ var SourceError = class extends NotebookLMError {
381
+ };
382
+ var SourceNotFoundError = class extends SourceError {
383
+ sourceId;
384
+ constructor(sourceId) {
385
+ super(`Source not found: ${sourceId}`);
386
+ this.sourceId = sourceId;
559
387
  }
560
- return false;
561
- }
562
- function buildCookieHeader(cookies) {
563
- return Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join("; ");
564
- }
565
- async function fetchTokens(cookies) {
566
- const cookieHeader = buildCookieHeader(cookies);
567
- const response = await fetch(NOTEBOOKLM_URL, {
568
- headers: { Cookie: cookieHeader },
569
- redirect: "follow"
570
- });
571
- if (!response.ok) {
572
- throw new exports.AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);
388
+ };
389
+ var SourceAddError = class extends SourceError {
390
+ url;
391
+ cause;
392
+ constructor(url, opts = {}) {
393
+ super(
394
+ opts.message ?? `Failed to add source: ${url}
395
+ Possible causes:
396
+ - URL is invalid or inaccessible
397
+ - Content is behind a paywall or requires authentication
398
+ - Rate limiting or quota exceeded`
399
+ );
400
+ this.url = url;
401
+ this.cause = opts.cause;
573
402
  }
574
- const finalUrl = response.url;
575
- if (isGoogleAuthRedirect(finalUrl)) {
576
- throw new exports.AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);
403
+ };
404
+ var SourceProcessingError = class extends SourceError {
405
+ sourceId;
406
+ status;
407
+ constructor(sourceId, status = 3, message) {
408
+ super(message ?? `Source ${sourceId} failed to process`);
409
+ this.sourceId = sourceId;
410
+ this.status = status;
577
411
  }
578
- const html = await response.text();
579
- const csrfToken = extractCsrfToken(html, finalUrl);
580
- const sessionId = extractSessionId(html, finalUrl);
581
- return { csrfToken, sessionId };
582
- }
583
- function extractCsrfToken(html, finalUrl) {
584
- const match = /"SNlM0e"\s*:\s*"([^"]+)"/.exec(html);
585
- if (!match?.[1]) {
586
- if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
587
- throw new exports.AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
588
- }
589
- throw new exports.AuthError("CSRF token (SNlM0e) not found in NotebookLM page HTML.");
412
+ };
413
+ var SourceTimeoutError = class extends SourceError {
414
+ sourceId;
415
+ timeout;
416
+ lastStatus;
417
+ constructor(sourceId, timeout, lastStatus) {
418
+ const statusInfo = lastStatus != null ? ` (last status: ${lastStatus})` : "";
419
+ super(`Source ${sourceId} not ready after ${timeout.toFixed(1)}s${statusInfo}`);
420
+ this.sourceId = sourceId;
421
+ this.timeout = timeout;
422
+ this.lastStatus = lastStatus;
590
423
  }
591
- return match[1];
592
- }
593
- function extractSessionId(html, finalUrl) {
594
- const match = /"FdrFJe"\s*:\s*"([^"]+)"/.exec(html);
595
- if (!match?.[1]) {
596
- if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
597
- throw new exports.AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
598
- }
599
- throw new exports.AuthError("Session ID (FdrFJe) not found in NotebookLM page HTML.");
424
+ };
425
+ var ArtifactError = class extends NotebookLMError {
426
+ };
427
+ var ArtifactNotFoundError = class extends ArtifactError {
428
+ artifactId;
429
+ artifactType;
430
+ constructor(artifactId, artifactType) {
431
+ const typeInfo = artifactType ? ` ${artifactType}` : "";
432
+ super(`${typeInfo.trim() || "Artifact"} ${artifactId} not found`);
433
+ this.artifactId = artifactId;
434
+ this.artifactType = artifactType;
600
435
  }
601
- return match[1];
602
- }
603
- function isGoogleAuthRedirect(url) {
604
- return url.includes("accounts.google.com") || url.includes("signin");
605
- }
606
- async function connect(opts = {}) {
607
- let cookieMap;
608
- let googleCookieHeader = null;
609
- if (opts.cookies) {
610
- cookieMap = loadCookiesFromString(opts.cookies);
611
- } else if (opts.cookiesFile) {
612
- cookieMap = loadCookiesFromFile(opts.cookiesFile);
613
- } else if (opts.cookiesObject) {
614
- if ("cookies" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {
615
- const storageState = opts.cookiesObject;
616
- cookieMap = loadCookiesFromObject(storageState);
617
- googleCookieHeader = buildGoogleCookieHeader(storageState);
618
- } else {
619
- cookieMap = loadCookiesFromMap(opts.cookiesObject);
620
- }
621
- } else {
622
- const envCookies = process.env["NOTEBOOKLM_COOKIES"];
623
- const envFile = process.env["NOTEBOOKLM_COOKIES_FILE"];
624
- if (envFile) {
625
- cookieMap = loadCookiesFromFile(envFile);
626
- } else if (fs.existsSync(DEFAULT_SESSION_FILE)) {
627
- const raw = fs.readFileSync(DEFAULT_SESSION_FILE, "utf-8");
628
- const storageState = JSON.parse(raw);
629
- cookieMap = loadCookiesFromObject(storageState);
630
- googleCookieHeader = buildGoogleCookieHeader(storageState);
631
- } else if (fs.existsSync("storage_state.json")) {
632
- const raw = fs.readFileSync("storage_state.json", "utf-8");
633
- const storageState = JSON.parse(raw);
634
- cookieMap = loadCookiesFromObject(storageState);
635
- googleCookieHeader = buildGoogleCookieHeader(storageState);
636
- } else if (envCookies) {
637
- cookieMap = loadCookiesFromString(envCookies);
638
- } else {
639
- throw new exports.AuthError("No session found. Run: npx notebooklm-sdk login");
640
- }
436
+ };
437
+ var ArtifactNotReadyError = class extends ArtifactError {
438
+ artifactType;
439
+ artifactId;
440
+ status;
441
+ constructor(artifactType, opts = {}) {
442
+ const base = opts.artifactId ? `${artifactType} artifact ${opts.artifactId} is not ready` : `No completed ${artifactType} found`;
443
+ const statusInfo = opts.status ? ` (status: ${opts.status})` : "";
444
+ super(`${base}${statusInfo}`);
445
+ this.artifactType = artifactType;
446
+ this.artifactId = opts.artifactId;
447
+ this.status = opts.status;
641
448
  }
642
- const { csrfToken, sessionId } = await fetchTokens(cookieMap);
643
- const cookieHeader = buildCookieHeader(cookieMap);
644
- return {
645
- cookies: cookieMap,
646
- csrfToken,
647
- sessionId,
648
- cookieHeader,
649
- googleCookieHeader: googleCookieHeader ?? cookieHeader
650
- };
651
- }
652
- var DEFAULT_SESSION_FILE, NOTEBOOKLM_URL;
653
- var init_auth = __esm({
654
- "src/auth.ts"() {
655
- init_errors();
656
- DEFAULT_SESSION_FILE = path.join(os.homedir(), ".notebooklm", "session.json");
657
- NOTEBOOKLM_URL = "https://notebooklm.google.com/";
449
+ };
450
+ var ArtifactParseError = class extends ArtifactError {
451
+ artifactType;
452
+ artifactId;
453
+ details;
454
+ cause;
455
+ constructor(artifactType, opts = {}) {
456
+ let msg = `Failed to parse ${artifactType} artifact`;
457
+ if (opts.artifactId) msg += ` ${opts.artifactId}`;
458
+ if (opts.details) msg += `: ${opts.details}`;
459
+ super(msg);
460
+ this.artifactType = artifactType;
461
+ this.artifactId = opts.artifactId;
462
+ this.details = opts.details;
463
+ this.cause = opts.cause;
658
464
  }
659
- });
660
-
661
- // src/api/artifacts.ts
662
- init_enums();
663
- init_errors();
465
+ };
466
+ var ArtifactDownloadError = class extends ArtifactError {
467
+ artifactType;
468
+ artifactId;
469
+ details;
470
+ cause;
471
+ constructor(artifactType, opts = {}) {
472
+ let msg = `Failed to download ${artifactType} artifact`;
473
+ if (opts.artifactId) msg += ` ${opts.artifactId}`;
474
+ if (opts.details) msg += `: ${opts.details}`;
475
+ super(msg);
476
+ this.artifactType = artifactType;
477
+ this.artifactId = opts.artifactId;
478
+ this.details = opts.details;
479
+ this.cause = opts.cause;
480
+ }
481
+ };
482
+ var ChatError = class extends NotebookLMError {
483
+ };
664
484
 
665
485
  // src/types/models.ts
666
486
  init_enums();
@@ -1120,34 +940,77 @@ ${opts.extraInstructions}` : cfg.prompt;
1120
940
  // Polling / download
1121
941
  // ---------------------------------------------------------------------------
1122
942
  /** Poll until artifact reaches completed/failed status. */
1123
- async waitUntilReady(notebookId, artifactId, timeout = 300, pollInterval = 3) {
943
+ async waitUntilReady(notebookId, artifactId, timeout = 1800, pollInterval = 3) {
944
+ return this.pollUntilReady(notebookId, artifactId, {
945
+ timeoutSecs: timeout,
946
+ intervalSecs: pollInterval
947
+ });
948
+ }
949
+ /** Poll until artifact is fully ready, with optional progress hooks and cancellation. */
950
+ async pollUntilReady(notebookId, artifactId, opts = {}) {
951
+ const timeout = opts.timeoutSecs ?? 1800;
952
+ const pollInterval = opts.intervalSecs ?? 3;
1124
953
  const deadline = Date.now() + timeout * 1e3;
954
+ let lastStatus = null;
1125
955
  while (Date.now() < deadline) {
956
+ this.throwIfAborted(opts.signal);
957
+ const status = await this.pollStatus(notebookId, artifactId);
958
+ lastStatus = status;
959
+ if (opts.onTick) await opts.onTick(status);
1126
960
  const artifact = await this.get(notebookId, artifactId);
1127
- if (artifact?.status === "completed") return artifact;
1128
- if (artifact?.status === "failed") {
1129
- throw new exports.ArtifactNotReadyError(artifact.kind, { artifactId, status: "failed" });
961
+ if (artifact?.status === "completed") {
962
+ if (artifact.kind === "audio" && !artifact.audioUrl || artifact.kind === "video" && !artifact.videoUrl) {
963
+ await sleep(pollInterval * 1e3);
964
+ continue;
965
+ }
966
+ return artifact;
967
+ }
968
+ if (artifact?.status === "failed" || status.status === "failed") {
969
+ throw new ArtifactNotReadyError(artifact?.kind ?? "artifact", {
970
+ artifactId,
971
+ status: "failed"
972
+ });
1130
973
  }
1131
974
  await sleep(pollInterval * 1e3);
1132
975
  }
1133
- throw new exports.ArtifactNotReadyError("artifact", { artifactId, status: "timeout" });
976
+ throw new ArtifactNotReadyError("artifact", {
977
+ artifactId,
978
+ status: lastStatus?.status ?? "timeout"
979
+ });
980
+ }
981
+ throwIfAborted(signal) {
982
+ if (!signal?.aborted) return;
983
+ throw new Error("Artifact polling aborted");
984
+ }
985
+ /** Get the current status of a generated artifact without waiting. */
986
+ async pollStatus(notebookId, artifactId) {
987
+ const rawList = await this._listRaw(notebookId);
988
+ for (const item of rawList) {
989
+ if (!Array.isArray(item) || item[0] !== artifactId) continue;
990
+ const statusCode = typeof item[4] === "number" ? item[4] : null;
991
+ return {
992
+ artifactId,
993
+ status: statusCode != null ? artifactStatusFromCode(statusCode) : "pending"
994
+ };
995
+ }
996
+ return { artifactId, status: "pending" };
1134
997
  }
1135
998
  /** Download audio content as a Buffer. */
1136
999
  async downloadAudio(notebookId, artifactId) {
1137
1000
  const artifact = await this.get(notebookId, artifactId);
1138
1001
  if (!artifact || artifact.status !== "completed") {
1139
- throw new exports.ArtifactNotReadyError("audio", { artifactId, status: artifact?.status });
1002
+ throw new ArtifactNotReadyError("audio", { artifactId, status: artifact?.status });
1140
1003
  }
1141
- if (!artifact.audioUrl) throw new exports.ArtifactNotReadyError("audio", { artifactId });
1004
+ if (!artifact.audioUrl) throw new ArtifactNotReadyError("audio", { artifactId });
1142
1005
  return this._fetchMediaWithCookies(artifact.audioUrl);
1143
1006
  }
1144
1007
  /** Download video content as a Buffer. */
1145
1008
  async downloadVideo(notebookId, artifactId) {
1146
1009
  const artifact = await this.get(notebookId, artifactId);
1147
1010
  if (!artifact || artifact.status !== "completed") {
1148
- throw new exports.ArtifactNotReadyError("video", { artifactId, status: artifact?.status });
1011
+ throw new ArtifactNotReadyError("video", { artifactId, status: artifact?.status });
1149
1012
  }
1150
- if (!artifact.videoUrl) throw new exports.ArtifactNotReadyError("video", { artifactId });
1013
+ if (!artifact.videoUrl) throw new ArtifactNotReadyError("video", { artifactId });
1151
1014
  return this._fetchMediaWithCookies(artifact.videoUrl);
1152
1015
  }
1153
1016
  /** Get markdown content for a completed report artifact. */
@@ -1176,12 +1039,12 @@ ${opts.extraInstructions}` : cfg.prompt;
1176
1039
  const raw = rawList.find(
1177
1040
  (a) => a[0] === artifactId && a[2] === exports.ArtifactTypeCode.SLIDE_DECK
1178
1041
  );
1179
- if (!raw) throw new exports.ArtifactNotReadyError("slide_deck", { artifactId });
1042
+ if (!raw) throw new ArtifactNotReadyError("slide_deck", { artifactId });
1180
1043
  const metadata = raw[16];
1181
- if (!Array.isArray(metadata)) throw new exports.ArtifactNotReadyError("slide_deck", { artifactId });
1044
+ if (!Array.isArray(metadata)) throw new ArtifactNotReadyError("slide_deck", { artifactId });
1182
1045
  const url = format === "pptx" ? metadata[4] : metadata[3];
1183
1046
  if (typeof url !== "string" || !url.startsWith("http")) {
1184
- throw new exports.ArtifactNotReadyError("slide_deck", { artifactId, status: `no ${format} url` });
1047
+ throw new ArtifactNotReadyError("slide_deck", { artifactId, status: `no ${format} url` });
1185
1048
  }
1186
1049
  return this._fetchMediaWithCookies(url);
1187
1050
  }
@@ -1191,7 +1054,7 @@ ${opts.extraInstructions}` : cfg.prompt;
1191
1054
  const raw = rawList.find(
1192
1055
  (a) => a[0] === artifactId && a[2] === exports.ArtifactTypeCode.INFOGRAPHIC
1193
1056
  );
1194
- if (!raw) throw new exports.ArtifactNotReadyError("infographic", { artifactId });
1057
+ if (!raw) throw new ArtifactNotReadyError("infographic", { artifactId });
1195
1058
  let url = null;
1196
1059
  for (let i = raw.length - 1; i >= 0; i--) {
1197
1060
  const item = raw[i];
@@ -1200,7 +1063,7 @@ ${opts.extraInstructions}` : cfg.prompt;
1200
1063
  break;
1201
1064
  }
1202
1065
  }
1203
- if (!url) throw new exports.ArtifactNotReadyError("infographic", { artifactId });
1066
+ if (!url) throw new ArtifactNotReadyError("infographic", { artifactId });
1204
1067
  return this._fetchMediaWithCookies(url);
1205
1068
  }
1206
1069
  /** Get AI-suggested report formats based on notebook content. */
@@ -1382,14 +1245,14 @@ function isTrustedDomain(url) {
1382
1245
 
1383
1246
  // src/api/chat.ts
1384
1247
  init_enums();
1385
- init_errors();
1386
1248
  var QUERY_URL = "https://notebooklm.google.com/_/LabsTailwindUi/data/google.internal.labs.tailwind.orchestration.v1.LabsTailwindOrchestrationService/GenerateFreeFormStreamed";
1387
1249
  var DEFAULT_BL = "boq_labs-tailwind-frontend_20260301.03_p0";
1388
1250
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1389
1251
  var ChatAPI = class {
1390
- constructor(rpc, auth) {
1252
+ constructor(rpc, auth, refreshAuth) {
1391
1253
  this.rpc = rpc;
1392
1254
  this.auth = auth;
1255
+ this.refreshAuth = refreshAuth;
1393
1256
  }
1394
1257
  conversationCache = /* @__PURE__ */ new Map();
1395
1258
  reqid = Math.floor(Math.random() * 9e5) + 1e5;
@@ -1419,20 +1282,7 @@ var ChatAPI = class {
1419
1282
  const bodyParts = [`f.req=${encodeURIComponent(fReq)}`];
1420
1283
  if (this.auth.csrfToken) bodyParts.push(`at=${encodeURIComponent(this.auth.csrfToken)}`);
1421
1284
  const body = bodyParts.join("&") + "&";
1422
- let response;
1423
- try {
1424
- response = await fetch(`${QUERY_URL}?${urlParams.toString()}`, {
1425
- method: "POST",
1426
- headers: {
1427
- "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
1428
- Cookie: this.auth.cookieHeader
1429
- },
1430
- body
1431
- });
1432
- } catch (e) {
1433
- throw new exports.ChatError(`Chat request failed: ${String(e)}`);
1434
- }
1435
- if (!response.ok) throw new exports.ChatError(`Chat request failed: HTTP ${response.status}`);
1285
+ const response = await this._postChatRequest(`${QUERY_URL}?${urlParams.toString()}`, body);
1436
1286
  const text = await response.text();
1437
1287
  const { answer, conversationId: serverConvId, references } = parseStreamingResponse(text);
1438
1288
  const finalConvId = serverConvId ?? conversationId;
@@ -1492,12 +1342,47 @@ var ChatAPI = class {
1492
1342
  }
1493
1343
  return null;
1494
1344
  }
1345
+ async getHistory(notebookId, limit = 100, conversationId) {
1346
+ const convId = conversationId ?? await this.getLastConversationId(notebookId);
1347
+ if (!convId) return [];
1348
+ const params = [[], null, null, convId, limit];
1349
+ const result = await this.rpc.call(exports.RPCMethod.GET_CONVERSATION_TURNS, params, {
1350
+ sourcePath: `/notebook/${notebookId}`,
1351
+ allowNull: true
1352
+ });
1353
+ if (!Array.isArray(result) || !Array.isArray(result[0])) return [];
1354
+ const rawTurns = [...result[0]].reverse();
1355
+ const history = [];
1356
+ let i = 0;
1357
+ while (i < rawTurns.length) {
1358
+ const turn = rawTurns[i];
1359
+ if (!Array.isArray(turn) || turn.length < 3) {
1360
+ i++;
1361
+ continue;
1362
+ }
1363
+ if (turn[2] === 1 && turn.length > 3) {
1364
+ const query = typeof turn[3] === "string" ? turn[3] : "";
1365
+ let answer = "";
1366
+ const next = rawTurns[i + 1];
1367
+ if (Array.isArray(next) && next.length > 4 && next[2] === 2) {
1368
+ try {
1369
+ answer = String(next[4][0][0] ?? "");
1370
+ } catch {
1371
+ }
1372
+ i++;
1373
+ }
1374
+ history.push([query, answer]);
1375
+ }
1376
+ i++;
1377
+ }
1378
+ return history;
1379
+ }
1495
1380
  /**
1496
1381
  * Low-level chat configuration. Set goal, response length, and optional
1497
1382
  * custom instructions directly. Persists on the server per notebook.
1498
1383
  * Use `setMode()` for preset combinations instead.
1499
1384
  */
1500
- async configure(notebookId, goal, length, customPrompt) {
1385
+ async configure(notebookId, goal = exports.ChatGoal.DEFAULT, length = exports.ChatResponseLength.DEFAULT, customPrompt) {
1501
1386
  if (goal === exports.ChatGoal.CUSTOM && !customPrompt) {
1502
1387
  throw new Error("customPrompt is required when goal is ChatGoal.CUSTOM");
1503
1388
  }
@@ -1529,6 +1414,14 @@ var ChatAPI = class {
1529
1414
  this.conversationCache.clear();
1530
1415
  }
1531
1416
  }
1417
+ getCachedTurns(conversationId) {
1418
+ const turns = this.conversationCache.get(conversationId) ?? [];
1419
+ return turns.map((turn) => ({
1420
+ query: turn.query,
1421
+ answer: turn.answer,
1422
+ turnNumber: turn.turnNumber
1423
+ }));
1424
+ }
1532
1425
  _buildHistory(conversationId) {
1533
1426
  const turns = this.conversationCache.get(conversationId) ?? [];
1534
1427
  if (!turns.length) return null;
@@ -1539,6 +1432,27 @@ var ChatAPI = class {
1539
1432
  }
1540
1433
  return history;
1541
1434
  }
1435
+ async _postChatRequest(url, body, retried = false) {
1436
+ let response;
1437
+ try {
1438
+ response = await fetch(url, {
1439
+ method: "POST",
1440
+ headers: {
1441
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
1442
+ Cookie: this.auth.cookieHeader
1443
+ },
1444
+ body
1445
+ });
1446
+ } catch (e) {
1447
+ throw new ChatError(`Chat request failed: ${String(e)}`);
1448
+ }
1449
+ if ((response.status === 401 || response.status === 403) && !retried && this.refreshAuth) {
1450
+ await this.refreshAuth();
1451
+ return this._postChatRequest(url, body, true);
1452
+ }
1453
+ if (!response.ok) throw new ChatError(`Chat request failed: HTTP ${response.status}`);
1454
+ return response;
1455
+ }
1542
1456
  };
1543
1457
  function parseStreamingResponse(rawText) {
1544
1458
  let text = rawText;
@@ -1695,6 +1609,12 @@ var NotebooksAPI = class {
1695
1609
  async removeFromRecent(notebookId) {
1696
1610
  await this.rpc.call(exports.RPCMethod.REMOVE_RECENTLY_VIEWED, [notebookId], { allowNull: true });
1697
1611
  }
1612
+ async getRaw(notebookId) {
1613
+ const params = [notebookId, null, [2], null, 0];
1614
+ return this.rpc.call(exports.RPCMethod.GET_NOTEBOOK, params, {
1615
+ sourcePath: `/notebook/${notebookId}`
1616
+ });
1617
+ }
1698
1618
  async getDescription(notebookId) {
1699
1619
  const params = [notebookId, [2]];
1700
1620
  const result = await this.rpc.call(exports.RPCMethod.SUMMARIZE, params, {
@@ -1722,6 +1642,41 @@ var NotebooksAPI = class {
1722
1642
  }
1723
1643
  return { summary, suggestedTopics };
1724
1644
  }
1645
+ async share(notebookId, publicAccess = true, artifactId) {
1646
+ const shareOptions = publicAccess ? [1] : [0];
1647
+ const params = artifactId ? [shareOptions, notebookId, artifactId] : [shareOptions, notebookId];
1648
+ await this.rpc.call(exports.RPCMethod.SHARE_ARTIFACT, params, {
1649
+ sourcePath: `/notebook/${notebookId}`,
1650
+ allowNull: true
1651
+ });
1652
+ return {
1653
+ public: publicAccess,
1654
+ url: publicAccess ? this.getShareUrl(notebookId, artifactId) : null,
1655
+ artifactId: artifactId ?? null
1656
+ };
1657
+ }
1658
+ getShareUrl(notebookId, artifactId) {
1659
+ const baseUrl = `https://notebooklm.google.com/notebook/${notebookId}`;
1660
+ return artifactId ? `${baseUrl}?artifactId=${artifactId}` : baseUrl;
1661
+ }
1662
+ async getMetadata(notebookId) {
1663
+ const raw = await this.getRaw(notebookId);
1664
+ const notebookData = Array.isArray(raw) && raw.length > 0 && Array.isArray(raw[0]) ? raw[0] : [];
1665
+ const notebook = parseNotebook(notebookData);
1666
+ const sourcesRaw = Array.isArray(notebookData[1]) ? notebookData[1] : [];
1667
+ const sources = sourcesRaw.filter((source) => Array.isArray(source) && source.length > 0).map((source) => parseSource(source));
1668
+ return {
1669
+ id: notebook.id,
1670
+ title: notebook.title,
1671
+ createdAt: notebook.createdAt,
1672
+ isOwner: notebook.isOwner,
1673
+ sources: sources.map((source) => ({
1674
+ kind: source.kind,
1675
+ title: source.title,
1676
+ url: source.url
1677
+ }))
1678
+ };
1679
+ }
1725
1680
  };
1726
1681
 
1727
1682
  // src/api/notes.ts
@@ -1734,6 +1689,10 @@ var NotesAPI = class {
1734
1689
  const all = await this._fetchAll(notebookId);
1735
1690
  return all.filter((n) => !this._isMindMap(n.content));
1736
1691
  }
1692
+ async get(notebookId, noteId) {
1693
+ const all = await this._fetchAll(notebookId);
1694
+ return all.find((note) => note.id === noteId) ?? null;
1695
+ }
1737
1696
  async listMindMaps(notebookId) {
1738
1697
  const all = await this._fetchAll(notebookId);
1739
1698
  return all.filter((n) => this._isMindMap(n.content));
@@ -1765,6 +1724,9 @@ var NotesAPI = class {
1765
1724
  });
1766
1725
  return true;
1767
1726
  }
1727
+ async deleteMindMap(notebookId, mindMapId) {
1728
+ return this.delete(notebookId, mindMapId);
1729
+ }
1768
1730
  async _fetchAll(notebookId) {
1769
1731
  const result = await this.rpc.call(exports.RPCMethod.GET_NOTES_AND_MIND_MAPS, [notebookId], {
1770
1732
  sourcePath: `/notebook/${notebookId}`,
@@ -2141,7 +2103,6 @@ function parseShareStatus(data, notebookId) {
2141
2103
 
2142
2104
  // src/api/sources.ts
2143
2105
  init_enums();
2144
- init_errors();
2145
2106
  var UPLOAD_URL = "https://notebooklm.google.com/upload/_/";
2146
2107
  var SourcesAPI = class {
2147
2108
  constructor(rpc, auth) {
@@ -2424,6 +2385,29 @@ var SourcesAPI = class {
2424
2385
  });
2425
2386
  return true;
2426
2387
  }
2388
+ async rename(notebookId, sourceId, newTitle) {
2389
+ const params = [null, [sourceId], [[[newTitle]]]];
2390
+ const result = await this.rpc.call(exports.RPCMethod.UPDATE_SOURCE, params, {
2391
+ sourcePath: `/notebook/${notebookId}`,
2392
+ allowNull: true
2393
+ });
2394
+ if (Array.isArray(result) && result.length > 0) {
2395
+ try {
2396
+ const parsed = parseSource(result);
2397
+ return parsed.title ? parsed : { ...parsed, title: newTitle };
2398
+ } catch {
2399
+ }
2400
+ }
2401
+ return {
2402
+ id: sourceId,
2403
+ title: newTitle,
2404
+ url: null,
2405
+ kind: "unknown",
2406
+ createdAt: null,
2407
+ status: "ready",
2408
+ _typeCode: null
2409
+ };
2410
+ }
2427
2411
  async waitUntilReady(notebookId, sourceId, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
2428
2412
  const deadline = Date.now() + timeout * 1e3;
2429
2413
  let interval = initialInterval;
@@ -2433,14 +2417,28 @@ var SourcesAPI = class {
2433
2417
  if (source) {
2434
2418
  if (source.status === "ready") return source;
2435
2419
  if (source.status === "error") {
2436
- throw new exports.SourceProcessingError(sourceId, 3);
2420
+ throw new SourceProcessingError(sourceId, 3);
2437
2421
  }
2438
2422
  lastStatus = source._typeCode ?? void 0;
2439
2423
  }
2440
2424
  await sleep2(interval * 1e3);
2441
2425
  interval = Math.min(interval * backoffFactor, maxInterval);
2442
2426
  }
2443
- throw new exports.SourceTimeoutError(sourceId, timeout, lastStatus);
2427
+ throw new SourceTimeoutError(sourceId, timeout, lastStatus);
2428
+ }
2429
+ async waitForSources(notebookId, sourceIds, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
2430
+ return Promise.all(
2431
+ sourceIds.map(
2432
+ (sourceId) => this.waitUntilReady(
2433
+ notebookId,
2434
+ sourceId,
2435
+ timeout,
2436
+ initialInterval,
2437
+ maxInterval,
2438
+ backoffFactor
2439
+ )
2440
+ )
2441
+ );
2444
2442
  }
2445
2443
  };
2446
2444
  function extractSourceId(result) {
@@ -2474,18 +2472,169 @@ function extractAllText(data, maxDepth = 100) {
2474
2472
  function sleep2(ms) {
2475
2473
  return new Promise((resolve) => setTimeout(resolve, ms));
2476
2474
  }
2477
-
2478
- // src/index.ts
2479
- init_auth();
2480
-
2481
- // src/client.ts
2482
- init_auth();
2483
-
2484
- // src/rpc/core.ts
2485
- init_errors();
2475
+ var DEFAULT_SESSION_FILE = path.join(os.homedir(), ".notebooklm", "session.json");
2476
+ function loadCookiesFromFile(filePath) {
2477
+ let raw;
2478
+ try {
2479
+ raw = fs.readFileSync(filePath, "utf-8");
2480
+ } catch {
2481
+ throw new AuthError(`Session file not found: ${filePath}
2482
+ Run: npx notebooklm-sdk login`);
2483
+ }
2484
+ return extractCookiesFromStorageState(JSON.parse(raw));
2485
+ }
2486
+ function loadCookiesFromObject(storageState) {
2487
+ return extractCookiesFromStorageState(storageState);
2488
+ }
2489
+ function buildGoogleCookieHeader(storageState) {
2490
+ const map = {};
2491
+ for (const c of storageState.cookies ?? []) {
2492
+ if (c.domain === ".google.com" && c.name && c.value) {
2493
+ map[c.name] = map[c.name] ?? c.value;
2494
+ }
2495
+ }
2496
+ return buildCookieHeader(map);
2497
+ }
2498
+ function loadCookiesFromMap(map) {
2499
+ return { ...map };
2500
+ }
2501
+ function loadCookiesFromString(cookieStr) {
2502
+ const map = {};
2503
+ for (const part of cookieStr.split(/;\s*/)) {
2504
+ const idx = part.indexOf("=");
2505
+ if (idx > 0) {
2506
+ const name = part.slice(0, idx).trim();
2507
+ const value = part.slice(idx + 1).trim();
2508
+ if (name) map[name] = value;
2509
+ }
2510
+ }
2511
+ return map;
2512
+ }
2513
+ function extractCookiesFromStorageState(storageState) {
2514
+ const cookies = {};
2515
+ for (const cookie of storageState.cookies ?? []) {
2516
+ const { domain, name, value } = cookie;
2517
+ if (!isAllowedDomain(domain) || !name) continue;
2518
+ const isBase = domain === ".google.com";
2519
+ if (!(name in cookies) || isBase) {
2520
+ cookies[name] = value;
2521
+ }
2522
+ }
2523
+ if (!cookies["SID"]) {
2524
+ throw new AuthError(
2525
+ "Missing required cookie: SID. Session may be invalid or expired.\nRun: npx notebooklm-sdk login"
2526
+ );
2527
+ }
2528
+ return cookies;
2529
+ }
2530
+ function isAllowedDomain(domain) {
2531
+ if (domain === ".google.com" || domain === "notebooklm.google.com" || domain === ".googleusercontent.com") {
2532
+ return true;
2533
+ }
2534
+ if (domain.startsWith(".google.")) {
2535
+ return true;
2536
+ }
2537
+ return false;
2538
+ }
2539
+ function buildCookieHeader(cookies) {
2540
+ return Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join("; ");
2541
+ }
2542
+ var NOTEBOOKLM_URL = "https://notebooklm.google.com/";
2543
+ async function fetchTokens(cookies) {
2544
+ const cookieHeader = buildCookieHeader(cookies);
2545
+ const response = await fetch(NOTEBOOKLM_URL, {
2546
+ headers: { Cookie: cookieHeader },
2547
+ redirect: "follow"
2548
+ });
2549
+ if (!response.ok) {
2550
+ throw new AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);
2551
+ }
2552
+ const finalUrl = response.url;
2553
+ if (isGoogleAuthRedirect(finalUrl)) {
2554
+ throw new AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);
2555
+ }
2556
+ const html = await response.text();
2557
+ const csrfToken = extractCsrfToken(html, finalUrl);
2558
+ const sessionId = extractSessionId(html, finalUrl);
2559
+ return { csrfToken, sessionId };
2560
+ }
2561
+ async function refreshAuthTokens(auth) {
2562
+ const { csrfToken, sessionId } = await fetchTokens(auth.cookies);
2563
+ auth.csrfToken = csrfToken;
2564
+ auth.sessionId = sessionId;
2565
+ return auth;
2566
+ }
2567
+ function extractCsrfToken(html, finalUrl) {
2568
+ const match = /"SNlM0e"\s*:\s*"([^"]+)"/.exec(html);
2569
+ if (!match?.[1]) {
2570
+ if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
2571
+ throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
2572
+ }
2573
+ throw new AuthError("CSRF token (SNlM0e) not found in NotebookLM page HTML.");
2574
+ }
2575
+ return match[1];
2576
+ }
2577
+ function extractSessionId(html, finalUrl) {
2578
+ const match = /"FdrFJe"\s*:\s*"([^"]+)"/.exec(html);
2579
+ if (!match?.[1]) {
2580
+ if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
2581
+ throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
2582
+ }
2583
+ throw new AuthError("Session ID (FdrFJe) not found in NotebookLM page HTML.");
2584
+ }
2585
+ return match[1];
2586
+ }
2587
+ function isGoogleAuthRedirect(url) {
2588
+ return url.includes("accounts.google.com") || url.includes("signin");
2589
+ }
2590
+ async function connect(opts = {}) {
2591
+ let cookieMap;
2592
+ let googleCookieHeader = null;
2593
+ if (opts.cookies) {
2594
+ cookieMap = loadCookiesFromString(opts.cookies);
2595
+ } else if (opts.cookiesFile) {
2596
+ cookieMap = loadCookiesFromFile(opts.cookiesFile);
2597
+ } else if (opts.cookiesObject) {
2598
+ if ("cookies" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {
2599
+ const storageState = opts.cookiesObject;
2600
+ cookieMap = loadCookiesFromObject(storageState);
2601
+ googleCookieHeader = buildGoogleCookieHeader(storageState);
2602
+ } else {
2603
+ cookieMap = loadCookiesFromMap(opts.cookiesObject);
2604
+ }
2605
+ } else {
2606
+ const envCookies = process.env["NOTEBOOKLM_COOKIES"];
2607
+ const envFile = process.env["NOTEBOOKLM_COOKIES_FILE"];
2608
+ if (envFile) {
2609
+ cookieMap = loadCookiesFromFile(envFile);
2610
+ } else if (fs.existsSync(DEFAULT_SESSION_FILE)) {
2611
+ const raw = fs.readFileSync(DEFAULT_SESSION_FILE, "utf-8");
2612
+ const storageState = JSON.parse(raw);
2613
+ cookieMap = loadCookiesFromObject(storageState);
2614
+ googleCookieHeader = buildGoogleCookieHeader(storageState);
2615
+ } else if (fs.existsSync("storage_state.json")) {
2616
+ const raw = fs.readFileSync("storage_state.json", "utf-8");
2617
+ const storageState = JSON.parse(raw);
2618
+ cookieMap = loadCookiesFromObject(storageState);
2619
+ googleCookieHeader = buildGoogleCookieHeader(storageState);
2620
+ } else if (envCookies) {
2621
+ cookieMap = loadCookiesFromString(envCookies);
2622
+ } else {
2623
+ throw new AuthError("No session found. Run: npx notebooklm-sdk login");
2624
+ }
2625
+ }
2626
+ const { csrfToken, sessionId } = await fetchTokens(cookieMap);
2627
+ const cookieHeader = buildCookieHeader(cookieMap);
2628
+ return {
2629
+ cookies: cookieMap,
2630
+ csrfToken,
2631
+ sessionId,
2632
+ cookieHeader,
2633
+ googleCookieHeader: googleCookieHeader ?? cookieHeader
2634
+ };
2635
+ }
2486
2636
 
2487
2637
  // src/rpc/decoder.ts
2488
- init_errors();
2489
2638
  function stripAntiXSSI(response) {
2490
2639
  if (response.startsWith(")]}'")) {
2491
2640
  const match = /\)\]\}'\r?\n/.exec(response);
@@ -2529,7 +2678,7 @@ function parseChunkedResponse(response) {
2529
2678
  if (skippedCount > 0 && lines.length > 0) {
2530
2679
  const errorRate = skippedCount / lines.length;
2531
2680
  if (errorRate > 0.1) {
2532
- throw new exports.RPCError(
2681
+ throw new RPCError(
2533
2682
  `Response parsing failed: ${skippedCount} of ${lines.length} chunks malformed`,
2534
2683
  { rawResponse: response.slice(0, 500) }
2535
2684
  );
@@ -2577,13 +2726,13 @@ function extractRPCResult(chunks, rpcId) {
2577
2726
  } else if (typeof code === "string") {
2578
2727
  msg = code;
2579
2728
  }
2580
- throw new exports.RPCError(msg, { methodId: rpcId, rpcCode: code });
2729
+ throw new RPCError(msg, { methodId: rpcId, rpcCode: code });
2581
2730
  }
2582
2731
  if (item[0] === "wrb.fr" && item[1] === rpcId) {
2583
2732
  const resultData = item[2];
2584
2733
  if (resultData === null && item.length > 5 && item[5] != null) {
2585
2734
  if (containsUserDisplayableError(item[5])) {
2586
- throw new exports.RateLimitError(
2735
+ throw new RateLimitError(
2587
2736
  "API rate limit or quota exceeded. Please wait before retrying.",
2588
2737
  { methodId: rpcId, rpcCode: "USER_DISPLAYABLE_ERROR" }
2589
2738
  );
@@ -2617,8 +2766,8 @@ function decodeResponse(rawResponse, rpcId, allowNull = false) {
2617
2766
  try {
2618
2767
  result = extractRPCResult(chunks, rpcId);
2619
2768
  } catch (e) {
2620
- if (e instanceof exports.RPCError && e.foundIds.length === 0) {
2621
- throw new exports.RPCError(e.message, {
2769
+ if (e instanceof RPCError && e.foundIds.length === 0) {
2770
+ throw new RPCError(e.message, {
2622
2771
  methodId: e.methodId,
2623
2772
  rpcCode: e.rpcCode,
2624
2773
  foundIds,
@@ -2629,12 +2778,12 @@ function decodeResponse(rawResponse, rpcId, allowNull = false) {
2629
2778
  }
2630
2779
  if (result === void 0 && !allowNull) {
2631
2780
  if (foundIds.length > 0 && !foundIds.includes(rpcId)) {
2632
- throw new exports.RPCError(
2781
+ throw new RPCError(
2633
2782
  `No result for RPC ID '${rpcId}'. Response has IDs: ${foundIds.join(", ")}. Method ID may have changed.`,
2634
2783
  { methodId: rpcId, foundIds, rawResponse: responsePreview }
2635
2784
  );
2636
2785
  }
2637
- throw new exports.RPCError(`No result found for RPC ID: ${rpcId} (${chunks.length} chunks parsed)`, {
2786
+ throw new RPCError(`No result found for RPC ID: ${rpcId} (${chunks.length} chunks parsed)`, {
2638
2787
  methodId: rpcId,
2639
2788
  foundIds,
2640
2789
  rawResponse: responsePreview
@@ -2669,11 +2818,13 @@ var DEFAULT_TIMEOUT_MS = 3e4;
2669
2818
  var RPCCore = class {
2670
2819
  auth;
2671
2820
  timeoutMs;
2672
- constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS) {
2821
+ refreshAuth;
2822
+ constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS, refreshAuth) {
2673
2823
  this.auth = auth;
2674
2824
  this.timeoutMs = timeoutMs;
2825
+ this.refreshAuth = refreshAuth;
2675
2826
  }
2676
- async call(methodId, params, opts = {}) {
2827
+ async call(methodId, params, opts = {}, retried = false) {
2677
2828
  const sourcePath = opts.sourcePath ?? "/";
2678
2829
  const allowNull = opts.allowNull ?? false;
2679
2830
  const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
@@ -2697,12 +2848,12 @@ var RPCCore = class {
2697
2848
  } catch (e) {
2698
2849
  clearTimeout(timer);
2699
2850
  if (e instanceof Error && e.name === "AbortError") {
2700
- throw new exports.RPCTimeoutError(`Request timed out calling ${methodId}`, {
2851
+ throw new RPCTimeoutError(`Request timed out calling ${methodId}`, {
2701
2852
  methodId,
2702
2853
  originalError: e
2703
2854
  });
2704
2855
  }
2705
- throw new exports.NetworkError(`Request failed calling ${methodId}: ${String(e)}`, {
2856
+ throw new NetworkError(`Request failed calling ${methodId}: ${String(e)}`, {
2706
2857
  methodId,
2707
2858
  originalError: e instanceof Error ? e : void 0
2708
2859
  });
@@ -2714,29 +2865,33 @@ var RPCCore = class {
2714
2865
  if (status === 429) {
2715
2866
  const retryAfterHeader = response.headers.get("retry-after");
2716
2867
  const retryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : void 0;
2717
- throw new exports.RateLimitError(`API rate limit exceeded calling ${methodId}`, {
2868
+ throw new RateLimitError(`API rate limit exceeded calling ${methodId}`, {
2718
2869
  methodId,
2719
2870
  retryAfter: isNaN(retryAfter ?? NaN) ? void 0 : retryAfter
2720
2871
  });
2721
2872
  }
2722
2873
  if (status === 401 || status === 403) {
2723
- throw new exports.AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
2874
+ if (!retried && this.refreshAuth) {
2875
+ await this.refreshAuth();
2876
+ return this.call(methodId, params, opts, true);
2877
+ }
2878
+ throw new AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
2724
2879
  methodId
2725
2880
  });
2726
2881
  }
2727
2882
  if (status >= 500) {
2728
- throw new exports.ServerError(`Server error ${status} calling ${methodId}`, {
2883
+ throw new ServerError(`Server error ${status} calling ${methodId}`, {
2729
2884
  methodId,
2730
2885
  statusCode: status
2731
2886
  });
2732
2887
  }
2733
2888
  if (status >= 400) {
2734
- throw new exports.ClientError(`Client error ${status} calling ${methodId}`, {
2889
+ throw new ClientError(`Client error ${status} calling ${methodId}`, {
2735
2890
  methodId,
2736
2891
  statusCode: status
2737
2892
  });
2738
2893
  }
2739
- throw new exports.RPCError(`HTTP ${status} calling ${methodId}`, { methodId });
2894
+ throw new RPCError(`HTTP ${status} calling ${methodId}`, { methodId });
2740
2895
  }
2741
2896
  const text = await response.text();
2742
2897
  return decodeResponse(text, methodId, allowNull);
@@ -2772,12 +2927,13 @@ var RPCCore = class {
2772
2927
  var NotebookLMClient = class _NotebookLMClient {
2773
2928
  constructor(auth, opts = {}) {
2774
2929
  this.auth = auth;
2775
- const rpc = new RPCCore(auth, opts.timeoutMs);
2930
+ const refreshAuth = this.refreshTokens.bind(this);
2931
+ const rpc = new RPCCore(auth, opts.timeoutMs, refreshAuth);
2776
2932
  this.notebooks = new NotebooksAPI(rpc);
2777
2933
  this.sources = new SourcesAPI(rpc, auth);
2778
2934
  this.notes = new NotesAPI(rpc);
2779
2935
  this.artifacts = new ArtifactsAPI(rpc, auth, this.notes);
2780
- this.chat = new ChatAPI(rpc, auth);
2936
+ this.chat = new ChatAPI(rpc, auth, refreshAuth);
2781
2937
  this.research = new ResearchAPI(rpc);
2782
2938
  this.settings = new SettingsAPI(rpc);
2783
2939
  this.sharing = new SharingAPI(rpc);
@@ -2790,6 +2946,7 @@ var NotebookLMClient = class _NotebookLMClient {
2790
2946
  research;
2791
2947
  settings;
2792
2948
  sharing;
2949
+ refreshPromise = null;
2793
2950
  /**
2794
2951
  * Connect to NotebookLM using cookies.
2795
2952
  * Fetches CSRF and session tokens from the NotebookLM homepage.
@@ -2802,25 +2959,47 @@ var NotebookLMClient = class _NotebookLMClient {
2802
2959
  * Refresh CSRF and session tokens (e.g. if they expire mid-session).
2803
2960
  */
2804
2961
  async refreshTokens() {
2805
- const { fetchTokens: fetchTokens2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
2806
- const { csrfToken, sessionId } = await fetchTokens2(this.auth.cookies);
2807
- this.auth.csrfToken = csrfToken;
2808
- this.auth.sessionId = sessionId;
2962
+ if (!this.refreshPromise) {
2963
+ this.refreshPromise = refreshAuthTokens(this.auth).then(() => void 0).finally(() => {
2964
+ this.refreshPromise = null;
2965
+ });
2966
+ }
2967
+ await this.refreshPromise;
2809
2968
  }
2810
2969
  };
2811
2970
 
2812
2971
  // src/index.ts
2813
2972
  init_enums();
2814
- init_errors();
2815
2973
 
2974
+ exports.ArtifactDownloadError = ArtifactDownloadError;
2975
+ exports.ArtifactError = ArtifactError;
2976
+ exports.ArtifactNotFoundError = ArtifactNotFoundError;
2977
+ exports.ArtifactNotReadyError = ArtifactNotReadyError;
2978
+ exports.ArtifactParseError = ArtifactParseError;
2816
2979
  exports.ArtifactsAPI = ArtifactsAPI;
2980
+ exports.AuthError = AuthError;
2817
2981
  exports.ChatAPI = ChatAPI;
2982
+ exports.ChatError = ChatError;
2983
+ exports.ClientError = ClientError;
2984
+ exports.NetworkError = NetworkError;
2985
+ exports.NotebookError = NotebookError;
2818
2986
  exports.NotebookLMClient = NotebookLMClient;
2987
+ exports.NotebookLMError = NotebookLMError;
2988
+ exports.NotebookNotFoundError = NotebookNotFoundError;
2819
2989
  exports.NotebooksAPI = NotebooksAPI;
2820
2990
  exports.NotesAPI = NotesAPI;
2991
+ exports.RPCError = RPCError;
2992
+ exports.RPCTimeoutError = RPCTimeoutError;
2993
+ exports.RateLimitError = RateLimitError;
2821
2994
  exports.ResearchAPI = ResearchAPI;
2995
+ exports.ServerError = ServerError;
2822
2996
  exports.SettingsAPI = SettingsAPI;
2823
2997
  exports.SharingAPI = SharingAPI;
2998
+ exports.SourceAddError = SourceAddError;
2999
+ exports.SourceError = SourceError;
3000
+ exports.SourceNotFoundError = SourceNotFoundError;
3001
+ exports.SourceProcessingError = SourceProcessingError;
3002
+ exports.SourceTimeoutError = SourceTimeoutError;
2824
3003
  exports.SourcesAPI = SourcesAPI;
2825
3004
  exports.connect = connect;
2826
3005
  //# sourceMappingURL=index.cjs.map