notebooklm-sdk 0.3.2 → 0.3.3

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();
@@ -1124,30 +944,49 @@ ${opts.extraInstructions}` : cfg.prompt;
1124
944
  const deadline = Date.now() + timeout * 1e3;
1125
945
  while (Date.now() < deadline) {
1126
946
  const artifact = await this.get(notebookId, artifactId);
1127
- if (artifact?.status === "completed") return artifact;
947
+ if (artifact?.status === "completed") {
948
+ if (artifact.kind === "audio" && !artifact.audioUrl || artifact.kind === "video" && !artifact.videoUrl) {
949
+ await sleep(pollInterval * 1e3);
950
+ continue;
951
+ }
952
+ return artifact;
953
+ }
1128
954
  if (artifact?.status === "failed") {
1129
- throw new exports.ArtifactNotReadyError(artifact.kind, { artifactId, status: "failed" });
955
+ throw new ArtifactNotReadyError(artifact.kind, { artifactId, status: "failed" });
1130
956
  }
1131
957
  await sleep(pollInterval * 1e3);
1132
958
  }
1133
- throw new exports.ArtifactNotReadyError("artifact", { artifactId, status: "timeout" });
959
+ throw new ArtifactNotReadyError("artifact", { artifactId, status: "timeout" });
960
+ }
961
+ /** Get the current status of a generated artifact without waiting. */
962
+ async pollStatus(notebookId, artifactId) {
963
+ const rawList = await this._listRaw(notebookId);
964
+ for (const item of rawList) {
965
+ if (!Array.isArray(item) || item[0] !== artifactId) continue;
966
+ const statusCode = typeof item[4] === "number" ? item[4] : null;
967
+ return {
968
+ artifactId,
969
+ status: statusCode != null ? artifactStatusFromCode(statusCode) : "pending"
970
+ };
971
+ }
972
+ return { artifactId, status: "pending" };
1134
973
  }
1135
974
  /** Download audio content as a Buffer. */
1136
975
  async downloadAudio(notebookId, artifactId) {
1137
976
  const artifact = await this.get(notebookId, artifactId);
1138
977
  if (!artifact || artifact.status !== "completed") {
1139
- throw new exports.ArtifactNotReadyError("audio", { artifactId, status: artifact?.status });
978
+ throw new ArtifactNotReadyError("audio", { artifactId, status: artifact?.status });
1140
979
  }
1141
- if (!artifact.audioUrl) throw new exports.ArtifactNotReadyError("audio", { artifactId });
980
+ if (!artifact.audioUrl) throw new ArtifactNotReadyError("audio", { artifactId });
1142
981
  return this._fetchMediaWithCookies(artifact.audioUrl);
1143
982
  }
1144
983
  /** Download video content as a Buffer. */
1145
984
  async downloadVideo(notebookId, artifactId) {
1146
985
  const artifact = await this.get(notebookId, artifactId);
1147
986
  if (!artifact || artifact.status !== "completed") {
1148
- throw new exports.ArtifactNotReadyError("video", { artifactId, status: artifact?.status });
987
+ throw new ArtifactNotReadyError("video", { artifactId, status: artifact?.status });
1149
988
  }
1150
- if (!artifact.videoUrl) throw new exports.ArtifactNotReadyError("video", { artifactId });
989
+ if (!artifact.videoUrl) throw new ArtifactNotReadyError("video", { artifactId });
1151
990
  return this._fetchMediaWithCookies(artifact.videoUrl);
1152
991
  }
1153
992
  /** Get markdown content for a completed report artifact. */
@@ -1176,12 +1015,12 @@ ${opts.extraInstructions}` : cfg.prompt;
1176
1015
  const raw = rawList.find(
1177
1016
  (a) => a[0] === artifactId && a[2] === exports.ArtifactTypeCode.SLIDE_DECK
1178
1017
  );
1179
- if (!raw) throw new exports.ArtifactNotReadyError("slide_deck", { artifactId });
1018
+ if (!raw) throw new ArtifactNotReadyError("slide_deck", { artifactId });
1180
1019
  const metadata = raw[16];
1181
- if (!Array.isArray(metadata)) throw new exports.ArtifactNotReadyError("slide_deck", { artifactId });
1020
+ if (!Array.isArray(metadata)) throw new ArtifactNotReadyError("slide_deck", { artifactId });
1182
1021
  const url = format === "pptx" ? metadata[4] : metadata[3];
1183
1022
  if (typeof url !== "string" || !url.startsWith("http")) {
1184
- throw new exports.ArtifactNotReadyError("slide_deck", { artifactId, status: `no ${format} url` });
1023
+ throw new ArtifactNotReadyError("slide_deck", { artifactId, status: `no ${format} url` });
1185
1024
  }
1186
1025
  return this._fetchMediaWithCookies(url);
1187
1026
  }
@@ -1191,7 +1030,7 @@ ${opts.extraInstructions}` : cfg.prompt;
1191
1030
  const raw = rawList.find(
1192
1031
  (a) => a[0] === artifactId && a[2] === exports.ArtifactTypeCode.INFOGRAPHIC
1193
1032
  );
1194
- if (!raw) throw new exports.ArtifactNotReadyError("infographic", { artifactId });
1033
+ if (!raw) throw new ArtifactNotReadyError("infographic", { artifactId });
1195
1034
  let url = null;
1196
1035
  for (let i = raw.length - 1; i >= 0; i--) {
1197
1036
  const item = raw[i];
@@ -1200,7 +1039,7 @@ ${opts.extraInstructions}` : cfg.prompt;
1200
1039
  break;
1201
1040
  }
1202
1041
  }
1203
- if (!url) throw new exports.ArtifactNotReadyError("infographic", { artifactId });
1042
+ if (!url) throw new ArtifactNotReadyError("infographic", { artifactId });
1204
1043
  return this._fetchMediaWithCookies(url);
1205
1044
  }
1206
1045
  /** Get AI-suggested report formats based on notebook content. */
@@ -1382,14 +1221,14 @@ function isTrustedDomain(url) {
1382
1221
 
1383
1222
  // src/api/chat.ts
1384
1223
  init_enums();
1385
- init_errors();
1386
1224
  var QUERY_URL = "https://notebooklm.google.com/_/LabsTailwindUi/data/google.internal.labs.tailwind.orchestration.v1.LabsTailwindOrchestrationService/GenerateFreeFormStreamed";
1387
1225
  var DEFAULT_BL = "boq_labs-tailwind-frontend_20260301.03_p0";
1388
1226
  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
1227
  var ChatAPI = class {
1390
- constructor(rpc, auth) {
1228
+ constructor(rpc, auth, refreshAuth) {
1391
1229
  this.rpc = rpc;
1392
1230
  this.auth = auth;
1231
+ this.refreshAuth = refreshAuth;
1393
1232
  }
1394
1233
  conversationCache = /* @__PURE__ */ new Map();
1395
1234
  reqid = Math.floor(Math.random() * 9e5) + 1e5;
@@ -1419,20 +1258,7 @@ var ChatAPI = class {
1419
1258
  const bodyParts = [`f.req=${encodeURIComponent(fReq)}`];
1420
1259
  if (this.auth.csrfToken) bodyParts.push(`at=${encodeURIComponent(this.auth.csrfToken)}`);
1421
1260
  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}`);
1261
+ const response = await this._postChatRequest(`${QUERY_URL}?${urlParams.toString()}`, body);
1436
1262
  const text = await response.text();
1437
1263
  const { answer, conversationId: serverConvId, references } = parseStreamingResponse(text);
1438
1264
  const finalConvId = serverConvId ?? conversationId;
@@ -1492,12 +1318,47 @@ var ChatAPI = class {
1492
1318
  }
1493
1319
  return null;
1494
1320
  }
1321
+ async getHistory(notebookId, limit = 100, conversationId) {
1322
+ const convId = conversationId ?? await this.getLastConversationId(notebookId);
1323
+ if (!convId) return [];
1324
+ const params = [[], null, null, convId, limit];
1325
+ const result = await this.rpc.call(exports.RPCMethod.GET_CONVERSATION_TURNS, params, {
1326
+ sourcePath: `/notebook/${notebookId}`,
1327
+ allowNull: true
1328
+ });
1329
+ if (!Array.isArray(result) || !Array.isArray(result[0])) return [];
1330
+ const rawTurns = [...result[0]].reverse();
1331
+ const history = [];
1332
+ let i = 0;
1333
+ while (i < rawTurns.length) {
1334
+ const turn = rawTurns[i];
1335
+ if (!Array.isArray(turn) || turn.length < 3) {
1336
+ i++;
1337
+ continue;
1338
+ }
1339
+ if (turn[2] === 1 && turn.length > 3) {
1340
+ const query = typeof turn[3] === "string" ? turn[3] : "";
1341
+ let answer = "";
1342
+ const next = rawTurns[i + 1];
1343
+ if (Array.isArray(next) && next.length > 4 && next[2] === 2) {
1344
+ try {
1345
+ answer = String(next[4][0][0] ?? "");
1346
+ } catch {
1347
+ }
1348
+ i++;
1349
+ }
1350
+ history.push([query, answer]);
1351
+ }
1352
+ i++;
1353
+ }
1354
+ return history;
1355
+ }
1495
1356
  /**
1496
1357
  * Low-level chat configuration. Set goal, response length, and optional
1497
1358
  * custom instructions directly. Persists on the server per notebook.
1498
1359
  * Use `setMode()` for preset combinations instead.
1499
1360
  */
1500
- async configure(notebookId, goal, length, customPrompt) {
1361
+ async configure(notebookId, goal = exports.ChatGoal.DEFAULT, length = exports.ChatResponseLength.DEFAULT, customPrompt) {
1501
1362
  if (goal === exports.ChatGoal.CUSTOM && !customPrompt) {
1502
1363
  throw new Error("customPrompt is required when goal is ChatGoal.CUSTOM");
1503
1364
  }
@@ -1529,6 +1390,14 @@ var ChatAPI = class {
1529
1390
  this.conversationCache.clear();
1530
1391
  }
1531
1392
  }
1393
+ getCachedTurns(conversationId) {
1394
+ const turns = this.conversationCache.get(conversationId) ?? [];
1395
+ return turns.map((turn) => ({
1396
+ query: turn.query,
1397
+ answer: turn.answer,
1398
+ turnNumber: turn.turnNumber
1399
+ }));
1400
+ }
1532
1401
  _buildHistory(conversationId) {
1533
1402
  const turns = this.conversationCache.get(conversationId) ?? [];
1534
1403
  if (!turns.length) return null;
@@ -1539,6 +1408,27 @@ var ChatAPI = class {
1539
1408
  }
1540
1409
  return history;
1541
1410
  }
1411
+ async _postChatRequest(url, body, retried = false) {
1412
+ let response;
1413
+ try {
1414
+ response = await fetch(url, {
1415
+ method: "POST",
1416
+ headers: {
1417
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
1418
+ Cookie: this.auth.cookieHeader
1419
+ },
1420
+ body
1421
+ });
1422
+ } catch (e) {
1423
+ throw new ChatError(`Chat request failed: ${String(e)}`);
1424
+ }
1425
+ if ((response.status === 401 || response.status === 403) && !retried && this.refreshAuth) {
1426
+ await this.refreshAuth();
1427
+ return this._postChatRequest(url, body, true);
1428
+ }
1429
+ if (!response.ok) throw new ChatError(`Chat request failed: HTTP ${response.status}`);
1430
+ return response;
1431
+ }
1542
1432
  };
1543
1433
  function parseStreamingResponse(rawText) {
1544
1434
  let text = rawText;
@@ -1695,6 +1585,12 @@ var NotebooksAPI = class {
1695
1585
  async removeFromRecent(notebookId) {
1696
1586
  await this.rpc.call(exports.RPCMethod.REMOVE_RECENTLY_VIEWED, [notebookId], { allowNull: true });
1697
1587
  }
1588
+ async getRaw(notebookId) {
1589
+ const params = [notebookId, null, [2], null, 0];
1590
+ return this.rpc.call(exports.RPCMethod.GET_NOTEBOOK, params, {
1591
+ sourcePath: `/notebook/${notebookId}`
1592
+ });
1593
+ }
1698
1594
  async getDescription(notebookId) {
1699
1595
  const params = [notebookId, [2]];
1700
1596
  const result = await this.rpc.call(exports.RPCMethod.SUMMARIZE, params, {
@@ -1722,6 +1618,41 @@ var NotebooksAPI = class {
1722
1618
  }
1723
1619
  return { summary, suggestedTopics };
1724
1620
  }
1621
+ async share(notebookId, publicAccess = true, artifactId) {
1622
+ const shareOptions = publicAccess ? [1] : [0];
1623
+ const params = artifactId ? [shareOptions, notebookId, artifactId] : [shareOptions, notebookId];
1624
+ await this.rpc.call(exports.RPCMethod.SHARE_ARTIFACT, params, {
1625
+ sourcePath: `/notebook/${notebookId}`,
1626
+ allowNull: true
1627
+ });
1628
+ return {
1629
+ public: publicAccess,
1630
+ url: publicAccess ? this.getShareUrl(notebookId, artifactId) : null,
1631
+ artifactId: artifactId ?? null
1632
+ };
1633
+ }
1634
+ getShareUrl(notebookId, artifactId) {
1635
+ const baseUrl = `https://notebooklm.google.com/notebook/${notebookId}`;
1636
+ return artifactId ? `${baseUrl}?artifactId=${artifactId}` : baseUrl;
1637
+ }
1638
+ async getMetadata(notebookId) {
1639
+ const raw = await this.getRaw(notebookId);
1640
+ const notebookData = Array.isArray(raw) && raw.length > 0 && Array.isArray(raw[0]) ? raw[0] : [];
1641
+ const notebook = parseNotebook(notebookData);
1642
+ const sourcesRaw = Array.isArray(notebookData[1]) ? notebookData[1] : [];
1643
+ const sources = sourcesRaw.filter((source) => Array.isArray(source) && source.length > 0).map((source) => parseSource(source));
1644
+ return {
1645
+ id: notebook.id,
1646
+ title: notebook.title,
1647
+ createdAt: notebook.createdAt,
1648
+ isOwner: notebook.isOwner,
1649
+ sources: sources.map((source) => ({
1650
+ kind: source.kind,
1651
+ title: source.title,
1652
+ url: source.url
1653
+ }))
1654
+ };
1655
+ }
1725
1656
  };
1726
1657
 
1727
1658
  // src/api/notes.ts
@@ -1734,6 +1665,10 @@ var NotesAPI = class {
1734
1665
  const all = await this._fetchAll(notebookId);
1735
1666
  return all.filter((n) => !this._isMindMap(n.content));
1736
1667
  }
1668
+ async get(notebookId, noteId) {
1669
+ const all = await this._fetchAll(notebookId);
1670
+ return all.find((note) => note.id === noteId) ?? null;
1671
+ }
1737
1672
  async listMindMaps(notebookId) {
1738
1673
  const all = await this._fetchAll(notebookId);
1739
1674
  return all.filter((n) => this._isMindMap(n.content));
@@ -1765,6 +1700,9 @@ var NotesAPI = class {
1765
1700
  });
1766
1701
  return true;
1767
1702
  }
1703
+ async deleteMindMap(notebookId, mindMapId) {
1704
+ return this.delete(notebookId, mindMapId);
1705
+ }
1768
1706
  async _fetchAll(notebookId) {
1769
1707
  const result = await this.rpc.call(exports.RPCMethod.GET_NOTES_AND_MIND_MAPS, [notebookId], {
1770
1708
  sourcePath: `/notebook/${notebookId}`,
@@ -2141,7 +2079,6 @@ function parseShareStatus(data, notebookId) {
2141
2079
 
2142
2080
  // src/api/sources.ts
2143
2081
  init_enums();
2144
- init_errors();
2145
2082
  var UPLOAD_URL = "https://notebooklm.google.com/upload/_/";
2146
2083
  var SourcesAPI = class {
2147
2084
  constructor(rpc, auth) {
@@ -2424,6 +2361,29 @@ var SourcesAPI = class {
2424
2361
  });
2425
2362
  return true;
2426
2363
  }
2364
+ async rename(notebookId, sourceId, newTitle) {
2365
+ const params = [null, [sourceId], [[[newTitle]]]];
2366
+ const result = await this.rpc.call(exports.RPCMethod.UPDATE_SOURCE, params, {
2367
+ sourcePath: `/notebook/${notebookId}`,
2368
+ allowNull: true
2369
+ });
2370
+ if (Array.isArray(result) && result.length > 0) {
2371
+ try {
2372
+ const parsed = parseSource(result);
2373
+ return parsed.title ? parsed : { ...parsed, title: newTitle };
2374
+ } catch {
2375
+ }
2376
+ }
2377
+ return {
2378
+ id: sourceId,
2379
+ title: newTitle,
2380
+ url: null,
2381
+ kind: "unknown",
2382
+ createdAt: null,
2383
+ status: "ready",
2384
+ _typeCode: null
2385
+ };
2386
+ }
2427
2387
  async waitUntilReady(notebookId, sourceId, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
2428
2388
  const deadline = Date.now() + timeout * 1e3;
2429
2389
  let interval = initialInterval;
@@ -2433,14 +2393,28 @@ var SourcesAPI = class {
2433
2393
  if (source) {
2434
2394
  if (source.status === "ready") return source;
2435
2395
  if (source.status === "error") {
2436
- throw new exports.SourceProcessingError(sourceId, 3);
2396
+ throw new SourceProcessingError(sourceId, 3);
2437
2397
  }
2438
2398
  lastStatus = source._typeCode ?? void 0;
2439
2399
  }
2440
2400
  await sleep2(interval * 1e3);
2441
2401
  interval = Math.min(interval * backoffFactor, maxInterval);
2442
2402
  }
2443
- throw new exports.SourceTimeoutError(sourceId, timeout, lastStatus);
2403
+ throw new SourceTimeoutError(sourceId, timeout, lastStatus);
2404
+ }
2405
+ async waitForSources(notebookId, sourceIds, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
2406
+ return Promise.all(
2407
+ sourceIds.map(
2408
+ (sourceId) => this.waitUntilReady(
2409
+ notebookId,
2410
+ sourceId,
2411
+ timeout,
2412
+ initialInterval,
2413
+ maxInterval,
2414
+ backoffFactor
2415
+ )
2416
+ )
2417
+ );
2444
2418
  }
2445
2419
  };
2446
2420
  function extractSourceId(result) {
@@ -2474,18 +2448,169 @@ function extractAllText(data, maxDepth = 100) {
2474
2448
  function sleep2(ms) {
2475
2449
  return new Promise((resolve) => setTimeout(resolve, ms));
2476
2450
  }
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();
2451
+ var DEFAULT_SESSION_FILE = path.join(os.homedir(), ".notebooklm", "session.json");
2452
+ function loadCookiesFromFile(filePath) {
2453
+ let raw;
2454
+ try {
2455
+ raw = fs.readFileSync(filePath, "utf-8");
2456
+ } catch {
2457
+ throw new AuthError(`Session file not found: ${filePath}
2458
+ Run: npx notebooklm-sdk login`);
2459
+ }
2460
+ return extractCookiesFromStorageState(JSON.parse(raw));
2461
+ }
2462
+ function loadCookiesFromObject(storageState) {
2463
+ return extractCookiesFromStorageState(storageState);
2464
+ }
2465
+ function buildGoogleCookieHeader(storageState) {
2466
+ const map = {};
2467
+ for (const c of storageState.cookies ?? []) {
2468
+ if (c.domain === ".google.com" && c.name && c.value) {
2469
+ map[c.name] = map[c.name] ?? c.value;
2470
+ }
2471
+ }
2472
+ return buildCookieHeader(map);
2473
+ }
2474
+ function loadCookiesFromMap(map) {
2475
+ return { ...map };
2476
+ }
2477
+ function loadCookiesFromString(cookieStr) {
2478
+ const map = {};
2479
+ for (const part of cookieStr.split(/;\s*/)) {
2480
+ const idx = part.indexOf("=");
2481
+ if (idx > 0) {
2482
+ const name = part.slice(0, idx).trim();
2483
+ const value = part.slice(idx + 1).trim();
2484
+ if (name) map[name] = value;
2485
+ }
2486
+ }
2487
+ return map;
2488
+ }
2489
+ function extractCookiesFromStorageState(storageState) {
2490
+ const cookies = {};
2491
+ for (const cookie of storageState.cookies ?? []) {
2492
+ const { domain, name, value } = cookie;
2493
+ if (!isAllowedDomain(domain) || !name) continue;
2494
+ const isBase = domain === ".google.com";
2495
+ if (!(name in cookies) || isBase) {
2496
+ cookies[name] = value;
2497
+ }
2498
+ }
2499
+ if (!cookies["SID"]) {
2500
+ throw new AuthError(
2501
+ "Missing required cookie: SID. Session may be invalid or expired.\nRun: npx notebooklm-sdk login"
2502
+ );
2503
+ }
2504
+ return cookies;
2505
+ }
2506
+ function isAllowedDomain(domain) {
2507
+ if (domain === ".google.com" || domain === "notebooklm.google.com" || domain === ".googleusercontent.com") {
2508
+ return true;
2509
+ }
2510
+ if (domain.startsWith(".google.")) {
2511
+ return true;
2512
+ }
2513
+ return false;
2514
+ }
2515
+ function buildCookieHeader(cookies) {
2516
+ return Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join("; ");
2517
+ }
2518
+ var NOTEBOOKLM_URL = "https://notebooklm.google.com/";
2519
+ async function fetchTokens(cookies) {
2520
+ const cookieHeader = buildCookieHeader(cookies);
2521
+ const response = await fetch(NOTEBOOKLM_URL, {
2522
+ headers: { Cookie: cookieHeader },
2523
+ redirect: "follow"
2524
+ });
2525
+ if (!response.ok) {
2526
+ throw new AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);
2527
+ }
2528
+ const finalUrl = response.url;
2529
+ if (isGoogleAuthRedirect(finalUrl)) {
2530
+ throw new AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);
2531
+ }
2532
+ const html = await response.text();
2533
+ const csrfToken = extractCsrfToken(html, finalUrl);
2534
+ const sessionId = extractSessionId(html, finalUrl);
2535
+ return { csrfToken, sessionId };
2536
+ }
2537
+ async function refreshAuthTokens(auth) {
2538
+ const { csrfToken, sessionId } = await fetchTokens(auth.cookies);
2539
+ auth.csrfToken = csrfToken;
2540
+ auth.sessionId = sessionId;
2541
+ return auth;
2542
+ }
2543
+ function extractCsrfToken(html, finalUrl) {
2544
+ const match = /"SNlM0e"\s*:\s*"([^"]+)"/.exec(html);
2545
+ if (!match?.[1]) {
2546
+ if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
2547
+ throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
2548
+ }
2549
+ throw new AuthError("CSRF token (SNlM0e) not found in NotebookLM page HTML.");
2550
+ }
2551
+ return match[1];
2552
+ }
2553
+ function extractSessionId(html, finalUrl) {
2554
+ const match = /"FdrFJe"\s*:\s*"([^"]+)"/.exec(html);
2555
+ if (!match?.[1]) {
2556
+ if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
2557
+ throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
2558
+ }
2559
+ throw new AuthError("Session ID (FdrFJe) not found in NotebookLM page HTML.");
2560
+ }
2561
+ return match[1];
2562
+ }
2563
+ function isGoogleAuthRedirect(url) {
2564
+ return url.includes("accounts.google.com") || url.includes("signin");
2565
+ }
2566
+ async function connect(opts = {}) {
2567
+ let cookieMap;
2568
+ let googleCookieHeader = null;
2569
+ if (opts.cookies) {
2570
+ cookieMap = loadCookiesFromString(opts.cookies);
2571
+ } else if (opts.cookiesFile) {
2572
+ cookieMap = loadCookiesFromFile(opts.cookiesFile);
2573
+ } else if (opts.cookiesObject) {
2574
+ if ("cookies" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {
2575
+ const storageState = opts.cookiesObject;
2576
+ cookieMap = loadCookiesFromObject(storageState);
2577
+ googleCookieHeader = buildGoogleCookieHeader(storageState);
2578
+ } else {
2579
+ cookieMap = loadCookiesFromMap(opts.cookiesObject);
2580
+ }
2581
+ } else {
2582
+ const envCookies = process.env["NOTEBOOKLM_COOKIES"];
2583
+ const envFile = process.env["NOTEBOOKLM_COOKIES_FILE"];
2584
+ if (envFile) {
2585
+ cookieMap = loadCookiesFromFile(envFile);
2586
+ } else if (fs.existsSync(DEFAULT_SESSION_FILE)) {
2587
+ const raw = fs.readFileSync(DEFAULT_SESSION_FILE, "utf-8");
2588
+ const storageState = JSON.parse(raw);
2589
+ cookieMap = loadCookiesFromObject(storageState);
2590
+ googleCookieHeader = buildGoogleCookieHeader(storageState);
2591
+ } else if (fs.existsSync("storage_state.json")) {
2592
+ const raw = fs.readFileSync("storage_state.json", "utf-8");
2593
+ const storageState = JSON.parse(raw);
2594
+ cookieMap = loadCookiesFromObject(storageState);
2595
+ googleCookieHeader = buildGoogleCookieHeader(storageState);
2596
+ } else if (envCookies) {
2597
+ cookieMap = loadCookiesFromString(envCookies);
2598
+ } else {
2599
+ throw new AuthError("No session found. Run: npx notebooklm-sdk login");
2600
+ }
2601
+ }
2602
+ const { csrfToken, sessionId } = await fetchTokens(cookieMap);
2603
+ const cookieHeader = buildCookieHeader(cookieMap);
2604
+ return {
2605
+ cookies: cookieMap,
2606
+ csrfToken,
2607
+ sessionId,
2608
+ cookieHeader,
2609
+ googleCookieHeader: googleCookieHeader ?? cookieHeader
2610
+ };
2611
+ }
2486
2612
 
2487
2613
  // src/rpc/decoder.ts
2488
- init_errors();
2489
2614
  function stripAntiXSSI(response) {
2490
2615
  if (response.startsWith(")]}'")) {
2491
2616
  const match = /\)\]\}'\r?\n/.exec(response);
@@ -2529,7 +2654,7 @@ function parseChunkedResponse(response) {
2529
2654
  if (skippedCount > 0 && lines.length > 0) {
2530
2655
  const errorRate = skippedCount / lines.length;
2531
2656
  if (errorRate > 0.1) {
2532
- throw new exports.RPCError(
2657
+ throw new RPCError(
2533
2658
  `Response parsing failed: ${skippedCount} of ${lines.length} chunks malformed`,
2534
2659
  { rawResponse: response.slice(0, 500) }
2535
2660
  );
@@ -2577,13 +2702,13 @@ function extractRPCResult(chunks, rpcId) {
2577
2702
  } else if (typeof code === "string") {
2578
2703
  msg = code;
2579
2704
  }
2580
- throw new exports.RPCError(msg, { methodId: rpcId, rpcCode: code });
2705
+ throw new RPCError(msg, { methodId: rpcId, rpcCode: code });
2581
2706
  }
2582
2707
  if (item[0] === "wrb.fr" && item[1] === rpcId) {
2583
2708
  const resultData = item[2];
2584
2709
  if (resultData === null && item.length > 5 && item[5] != null) {
2585
2710
  if (containsUserDisplayableError(item[5])) {
2586
- throw new exports.RateLimitError(
2711
+ throw new RateLimitError(
2587
2712
  "API rate limit or quota exceeded. Please wait before retrying.",
2588
2713
  { methodId: rpcId, rpcCode: "USER_DISPLAYABLE_ERROR" }
2589
2714
  );
@@ -2617,8 +2742,8 @@ function decodeResponse(rawResponse, rpcId, allowNull = false) {
2617
2742
  try {
2618
2743
  result = extractRPCResult(chunks, rpcId);
2619
2744
  } catch (e) {
2620
- if (e instanceof exports.RPCError && e.foundIds.length === 0) {
2621
- throw new exports.RPCError(e.message, {
2745
+ if (e instanceof RPCError && e.foundIds.length === 0) {
2746
+ throw new RPCError(e.message, {
2622
2747
  methodId: e.methodId,
2623
2748
  rpcCode: e.rpcCode,
2624
2749
  foundIds,
@@ -2629,12 +2754,12 @@ function decodeResponse(rawResponse, rpcId, allowNull = false) {
2629
2754
  }
2630
2755
  if (result === void 0 && !allowNull) {
2631
2756
  if (foundIds.length > 0 && !foundIds.includes(rpcId)) {
2632
- throw new exports.RPCError(
2757
+ throw new RPCError(
2633
2758
  `No result for RPC ID '${rpcId}'. Response has IDs: ${foundIds.join(", ")}. Method ID may have changed.`,
2634
2759
  { methodId: rpcId, foundIds, rawResponse: responsePreview }
2635
2760
  );
2636
2761
  }
2637
- throw new exports.RPCError(`No result found for RPC ID: ${rpcId} (${chunks.length} chunks parsed)`, {
2762
+ throw new RPCError(`No result found for RPC ID: ${rpcId} (${chunks.length} chunks parsed)`, {
2638
2763
  methodId: rpcId,
2639
2764
  foundIds,
2640
2765
  rawResponse: responsePreview
@@ -2669,11 +2794,13 @@ var DEFAULT_TIMEOUT_MS = 3e4;
2669
2794
  var RPCCore = class {
2670
2795
  auth;
2671
2796
  timeoutMs;
2672
- constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS) {
2797
+ refreshAuth;
2798
+ constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS, refreshAuth) {
2673
2799
  this.auth = auth;
2674
2800
  this.timeoutMs = timeoutMs;
2801
+ this.refreshAuth = refreshAuth;
2675
2802
  }
2676
- async call(methodId, params, opts = {}) {
2803
+ async call(methodId, params, opts = {}, retried = false) {
2677
2804
  const sourcePath = opts.sourcePath ?? "/";
2678
2805
  const allowNull = opts.allowNull ?? false;
2679
2806
  const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
@@ -2697,12 +2824,12 @@ var RPCCore = class {
2697
2824
  } catch (e) {
2698
2825
  clearTimeout(timer);
2699
2826
  if (e instanceof Error && e.name === "AbortError") {
2700
- throw new exports.RPCTimeoutError(`Request timed out calling ${methodId}`, {
2827
+ throw new RPCTimeoutError(`Request timed out calling ${methodId}`, {
2701
2828
  methodId,
2702
2829
  originalError: e
2703
2830
  });
2704
2831
  }
2705
- throw new exports.NetworkError(`Request failed calling ${methodId}: ${String(e)}`, {
2832
+ throw new NetworkError(`Request failed calling ${methodId}: ${String(e)}`, {
2706
2833
  methodId,
2707
2834
  originalError: e instanceof Error ? e : void 0
2708
2835
  });
@@ -2714,29 +2841,33 @@ var RPCCore = class {
2714
2841
  if (status === 429) {
2715
2842
  const retryAfterHeader = response.headers.get("retry-after");
2716
2843
  const retryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : void 0;
2717
- throw new exports.RateLimitError(`API rate limit exceeded calling ${methodId}`, {
2844
+ throw new RateLimitError(`API rate limit exceeded calling ${methodId}`, {
2718
2845
  methodId,
2719
2846
  retryAfter: isNaN(retryAfter ?? NaN) ? void 0 : retryAfter
2720
2847
  });
2721
2848
  }
2722
2849
  if (status === 401 || status === 403) {
2723
- throw new exports.AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
2850
+ if (!retried && this.refreshAuth) {
2851
+ await this.refreshAuth();
2852
+ return this.call(methodId, params, opts, true);
2853
+ }
2854
+ throw new AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
2724
2855
  methodId
2725
2856
  });
2726
2857
  }
2727
2858
  if (status >= 500) {
2728
- throw new exports.ServerError(`Server error ${status} calling ${methodId}`, {
2859
+ throw new ServerError(`Server error ${status} calling ${methodId}`, {
2729
2860
  methodId,
2730
2861
  statusCode: status
2731
2862
  });
2732
2863
  }
2733
2864
  if (status >= 400) {
2734
- throw new exports.ClientError(`Client error ${status} calling ${methodId}`, {
2865
+ throw new ClientError(`Client error ${status} calling ${methodId}`, {
2735
2866
  methodId,
2736
2867
  statusCode: status
2737
2868
  });
2738
2869
  }
2739
- throw new exports.RPCError(`HTTP ${status} calling ${methodId}`, { methodId });
2870
+ throw new RPCError(`HTTP ${status} calling ${methodId}`, { methodId });
2740
2871
  }
2741
2872
  const text = await response.text();
2742
2873
  return decodeResponse(text, methodId, allowNull);
@@ -2772,12 +2903,13 @@ var RPCCore = class {
2772
2903
  var NotebookLMClient = class _NotebookLMClient {
2773
2904
  constructor(auth, opts = {}) {
2774
2905
  this.auth = auth;
2775
- const rpc = new RPCCore(auth, opts.timeoutMs);
2906
+ const refreshAuth = this.refreshTokens.bind(this);
2907
+ const rpc = new RPCCore(auth, opts.timeoutMs, refreshAuth);
2776
2908
  this.notebooks = new NotebooksAPI(rpc);
2777
2909
  this.sources = new SourcesAPI(rpc, auth);
2778
2910
  this.notes = new NotesAPI(rpc);
2779
2911
  this.artifacts = new ArtifactsAPI(rpc, auth, this.notes);
2780
- this.chat = new ChatAPI(rpc, auth);
2912
+ this.chat = new ChatAPI(rpc, auth, refreshAuth);
2781
2913
  this.research = new ResearchAPI(rpc);
2782
2914
  this.settings = new SettingsAPI(rpc);
2783
2915
  this.sharing = new SharingAPI(rpc);
@@ -2790,6 +2922,7 @@ var NotebookLMClient = class _NotebookLMClient {
2790
2922
  research;
2791
2923
  settings;
2792
2924
  sharing;
2925
+ refreshPromise = null;
2793
2926
  /**
2794
2927
  * Connect to NotebookLM using cookies.
2795
2928
  * Fetches CSRF and session tokens from the NotebookLM homepage.
@@ -2802,25 +2935,47 @@ var NotebookLMClient = class _NotebookLMClient {
2802
2935
  * Refresh CSRF and session tokens (e.g. if they expire mid-session).
2803
2936
  */
2804
2937
  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;
2938
+ if (!this.refreshPromise) {
2939
+ this.refreshPromise = refreshAuthTokens(this.auth).then(() => void 0).finally(() => {
2940
+ this.refreshPromise = null;
2941
+ });
2942
+ }
2943
+ await this.refreshPromise;
2809
2944
  }
2810
2945
  };
2811
2946
 
2812
2947
  // src/index.ts
2813
2948
  init_enums();
2814
- init_errors();
2815
2949
 
2950
+ exports.ArtifactDownloadError = ArtifactDownloadError;
2951
+ exports.ArtifactError = ArtifactError;
2952
+ exports.ArtifactNotFoundError = ArtifactNotFoundError;
2953
+ exports.ArtifactNotReadyError = ArtifactNotReadyError;
2954
+ exports.ArtifactParseError = ArtifactParseError;
2816
2955
  exports.ArtifactsAPI = ArtifactsAPI;
2956
+ exports.AuthError = AuthError;
2817
2957
  exports.ChatAPI = ChatAPI;
2958
+ exports.ChatError = ChatError;
2959
+ exports.ClientError = ClientError;
2960
+ exports.NetworkError = NetworkError;
2961
+ exports.NotebookError = NotebookError;
2818
2962
  exports.NotebookLMClient = NotebookLMClient;
2963
+ exports.NotebookLMError = NotebookLMError;
2964
+ exports.NotebookNotFoundError = NotebookNotFoundError;
2819
2965
  exports.NotebooksAPI = NotebooksAPI;
2820
2966
  exports.NotesAPI = NotesAPI;
2967
+ exports.RPCError = RPCError;
2968
+ exports.RPCTimeoutError = RPCTimeoutError;
2969
+ exports.RateLimitError = RateLimitError;
2821
2970
  exports.ResearchAPI = ResearchAPI;
2971
+ exports.ServerError = ServerError;
2822
2972
  exports.SettingsAPI = SettingsAPI;
2823
2973
  exports.SharingAPI = SharingAPI;
2974
+ exports.SourceAddError = SourceAddError;
2975
+ exports.SourceError = SourceError;
2976
+ exports.SourceNotFoundError = SourceNotFoundError;
2977
+ exports.SourceProcessingError = SourceProcessingError;
2978
+ exports.SourceTimeoutError = SourceTimeoutError;
2824
2979
  exports.SourcesAPI = SourcesAPI;
2825
2980
  exports.connect = connect;
2826
2981
  //# sourceMappingURL=index.cjs.map