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.js CHANGED
@@ -309,356 +309,176 @@ var init_enums = __esm({
309
309
  }
310
310
  });
311
311
 
312
+ // src/api/artifacts.ts
313
+ init_enums();
314
+
312
315
  // src/types/errors.ts
313
- var NotebookLMError, NetworkError, RPCTimeoutError, RPCError, AuthError, RateLimitError, ServerError, ClientError, NotebookError, NotebookNotFoundError, SourceError, SourceNotFoundError, SourceAddError, SourceProcessingError, SourceTimeoutError, ArtifactError, ArtifactNotFoundError, ArtifactNotReadyError, ArtifactParseError, ArtifactDownloadError, ChatError;
314
- var init_errors = __esm({
315
- "src/types/errors.ts"() {
316
- NotebookLMError = class extends Error {
317
- constructor(message) {
318
- super(message);
319
- this.name = this.constructor.name;
320
- }
321
- };
322
- NetworkError = class extends NotebookLMError {
323
- methodId;
324
- originalError;
325
- constructor(message, opts = {}) {
326
- super(message);
327
- this.methodId = opts.methodId;
328
- this.originalError = opts.originalError;
329
- }
330
- };
331
- RPCTimeoutError = class extends NetworkError {
332
- };
333
- RPCError = class extends NotebookLMError {
334
- methodId;
335
- rawResponse;
336
- rpcCode;
337
- foundIds;
338
- constructor(message, opts = {}) {
339
- super(message);
340
- this.methodId = opts.methodId;
341
- this.rawResponse = opts.rawResponse ? opts.rawResponse.slice(0, 500) : void 0;
342
- this.rpcCode = opts.rpcCode;
343
- this.foundIds = opts.foundIds ?? [];
344
- }
345
- };
346
- AuthError = class extends RPCError {
347
- };
348
- RateLimitError = class extends RPCError {
349
- retryAfter;
350
- constructor(message, opts = {}) {
351
- super(message, opts);
352
- this.retryAfter = opts.retryAfter;
353
- }
354
- };
355
- ServerError = class extends RPCError {
356
- statusCode;
357
- constructor(message, opts = {}) {
358
- super(message, opts);
359
- this.statusCode = opts.statusCode;
360
- }
361
- };
362
- ClientError = class extends RPCError {
363
- statusCode;
364
- constructor(message, opts = {}) {
365
- super(message, opts);
366
- this.statusCode = opts.statusCode;
367
- }
368
- };
369
- NotebookError = class extends NotebookLMError {
370
- };
371
- NotebookNotFoundError = class extends NotebookError {
372
- notebookId;
373
- constructor(notebookId) {
374
- super(`Notebook not found: ${notebookId}`);
375
- this.notebookId = notebookId;
376
- }
377
- };
378
- SourceError = class extends NotebookLMError {
379
- };
380
- SourceNotFoundError = class extends SourceError {
381
- sourceId;
382
- constructor(sourceId) {
383
- super(`Source not found: ${sourceId}`);
384
- this.sourceId = sourceId;
385
- }
386
- };
387
- SourceAddError = class extends SourceError {
388
- url;
389
- cause;
390
- constructor(url, opts = {}) {
391
- super(
392
- opts.message ?? `Failed to add source: ${url}
393
- Possible causes:
394
- - URL is invalid or inaccessible
395
- - Content is behind a paywall or requires authentication
396
- - Rate limiting or quota exceeded`
397
- );
398
- this.url = url;
399
- this.cause = opts.cause;
400
- }
401
- };
402
- SourceProcessingError = class extends SourceError {
403
- sourceId;
404
- status;
405
- constructor(sourceId, status = 3, message) {
406
- super(message ?? `Source ${sourceId} failed to process`);
407
- this.sourceId = sourceId;
408
- this.status = status;
409
- }
410
- };
411
- SourceTimeoutError = class extends SourceError {
412
- sourceId;
413
- timeout;
414
- lastStatus;
415
- constructor(sourceId, timeout, lastStatus) {
416
- const statusInfo = lastStatus != null ? ` (last status: ${lastStatus})` : "";
417
- super(`Source ${sourceId} not ready after ${timeout.toFixed(1)}s${statusInfo}`);
418
- this.sourceId = sourceId;
419
- this.timeout = timeout;
420
- this.lastStatus = lastStatus;
421
- }
422
- };
423
- ArtifactError = class extends NotebookLMError {
424
- };
425
- ArtifactNotFoundError = class extends ArtifactError {
426
- artifactId;
427
- artifactType;
428
- constructor(artifactId, artifactType) {
429
- const typeInfo = artifactType ? ` ${artifactType}` : "";
430
- super(`${typeInfo.trim() || "Artifact"} ${artifactId} not found`);
431
- this.artifactId = artifactId;
432
- this.artifactType = artifactType;
433
- }
434
- };
435
- ArtifactNotReadyError = class extends ArtifactError {
436
- artifactType;
437
- artifactId;
438
- status;
439
- constructor(artifactType, opts = {}) {
440
- const base = opts.artifactId ? `${artifactType} artifact ${opts.artifactId} is not ready` : `No completed ${artifactType} found`;
441
- const statusInfo = opts.status ? ` (status: ${opts.status})` : "";
442
- super(`${base}${statusInfo}`);
443
- this.artifactType = artifactType;
444
- this.artifactId = opts.artifactId;
445
- this.status = opts.status;
446
- }
447
- };
448
- ArtifactParseError = class extends ArtifactError {
449
- artifactType;
450
- artifactId;
451
- details;
452
- cause;
453
- constructor(artifactType, opts = {}) {
454
- let msg = `Failed to parse ${artifactType} artifact`;
455
- if (opts.artifactId) msg += ` ${opts.artifactId}`;
456
- if (opts.details) msg += `: ${opts.details}`;
457
- super(msg);
458
- this.artifactType = artifactType;
459
- this.artifactId = opts.artifactId;
460
- this.details = opts.details;
461
- this.cause = opts.cause;
462
- }
463
- };
464
- ArtifactDownloadError = class extends ArtifactError {
465
- artifactType;
466
- artifactId;
467
- details;
468
- cause;
469
- constructor(artifactType, opts = {}) {
470
- let msg = `Failed to download ${artifactType} artifact`;
471
- if (opts.artifactId) msg += ` ${opts.artifactId}`;
472
- if (opts.details) msg += `: ${opts.details}`;
473
- super(msg);
474
- this.artifactType = artifactType;
475
- this.artifactId = opts.artifactId;
476
- this.details = opts.details;
477
- this.cause = opts.cause;
478
- }
479
- };
480
- ChatError = class extends NotebookLMError {
481
- };
316
+ var NotebookLMError = class extends Error {
317
+ constructor(message) {
318
+ super(message);
319
+ this.name = this.constructor.name;
482
320
  }
483
- });
484
-
485
- // src/auth.ts
486
- var auth_exports = {};
487
- __export(auth_exports, {
488
- buildCookieHeader: () => buildCookieHeader,
489
- buildGoogleCookieHeader: () => buildGoogleCookieHeader,
490
- connect: () => connect,
491
- fetchTokens: () => fetchTokens,
492
- loadCookiesFromFile: () => loadCookiesFromFile,
493
- loadCookiesFromMap: () => loadCookiesFromMap,
494
- loadCookiesFromObject: () => loadCookiesFromObject,
495
- loadCookiesFromString: () => loadCookiesFromString
496
- });
497
- function loadCookiesFromFile(filePath) {
498
- let raw;
499
- try {
500
- raw = readFileSync(filePath, "utf-8");
501
- } catch {
502
- throw new AuthError(`Session file not found: ${filePath}
503
- Run: npx notebooklm-sdk login`);
321
+ };
322
+ var NetworkError = class extends NotebookLMError {
323
+ methodId;
324
+ originalError;
325
+ constructor(message, opts = {}) {
326
+ super(message);
327
+ this.methodId = opts.methodId;
328
+ this.originalError = opts.originalError;
504
329
  }
505
- return extractCookiesFromStorageState(JSON.parse(raw));
506
- }
507
- function loadCookiesFromObject(storageState) {
508
- return extractCookiesFromStorageState(storageState);
509
- }
510
- function buildGoogleCookieHeader(storageState) {
511
- const map = {};
512
- for (const c of storageState.cookies ?? []) {
513
- if (c.domain === ".google.com" && c.name && c.value) {
514
- map[c.name] = map[c.name] ?? c.value;
515
- }
330
+ };
331
+ var RPCTimeoutError = class extends NetworkError {
332
+ };
333
+ var RPCError = class extends NotebookLMError {
334
+ methodId;
335
+ rawResponse;
336
+ rpcCode;
337
+ foundIds;
338
+ constructor(message, opts = {}) {
339
+ super(message);
340
+ this.methodId = opts.methodId;
341
+ this.rawResponse = opts.rawResponse ? opts.rawResponse.slice(0, 500) : void 0;
342
+ this.rpcCode = opts.rpcCode;
343
+ this.foundIds = opts.foundIds ?? [];
516
344
  }
517
- return buildCookieHeader(map);
518
- }
519
- function loadCookiesFromMap(map) {
520
- return { ...map };
521
- }
522
- function loadCookiesFromString(cookieStr) {
523
- const map = {};
524
- for (const part of cookieStr.split(/;\s*/)) {
525
- const idx = part.indexOf("=");
526
- if (idx > 0) {
527
- const name = part.slice(0, idx).trim();
528
- const value = part.slice(idx + 1).trim();
529
- if (name) map[name] = value;
530
- }
345
+ };
346
+ var AuthError = class extends RPCError {
347
+ };
348
+ var RateLimitError = class extends RPCError {
349
+ retryAfter;
350
+ constructor(message, opts = {}) {
351
+ super(message, opts);
352
+ this.retryAfter = opts.retryAfter;
531
353
  }
532
- return map;
533
- }
534
- function extractCookiesFromStorageState(storageState) {
535
- const cookies = {};
536
- for (const cookie of storageState.cookies ?? []) {
537
- const { domain, name, value } = cookie;
538
- if (!isAllowedDomain(domain) || !name) continue;
539
- const isBase = domain === ".google.com";
540
- if (!(name in cookies) || isBase) {
541
- cookies[name] = value;
542
- }
354
+ };
355
+ var ServerError = class extends RPCError {
356
+ statusCode;
357
+ constructor(message, opts = {}) {
358
+ super(message, opts);
359
+ this.statusCode = opts.statusCode;
543
360
  }
544
- if (!cookies["SID"]) {
545
- throw new AuthError(
546
- "Missing required cookie: SID. Session may be invalid or expired.\nRun: npx notebooklm-sdk login"
547
- );
361
+ };
362
+ var ClientError = class extends RPCError {
363
+ statusCode;
364
+ constructor(message, opts = {}) {
365
+ super(message, opts);
366
+ this.statusCode = opts.statusCode;
548
367
  }
549
- return cookies;
550
- }
551
- function isAllowedDomain(domain) {
552
- if (domain === ".google.com" || domain === "notebooklm.google.com" || domain === ".googleusercontent.com") {
553
- return true;
368
+ };
369
+ var NotebookError = class extends NotebookLMError {
370
+ };
371
+ var NotebookNotFoundError = class extends NotebookError {
372
+ notebookId;
373
+ constructor(notebookId) {
374
+ super(`Notebook not found: ${notebookId}`);
375
+ this.notebookId = notebookId;
554
376
  }
555
- if (domain.startsWith(".google.")) {
556
- return true;
377
+ };
378
+ var SourceError = class extends NotebookLMError {
379
+ };
380
+ var SourceNotFoundError = class extends SourceError {
381
+ sourceId;
382
+ constructor(sourceId) {
383
+ super(`Source not found: ${sourceId}`);
384
+ this.sourceId = sourceId;
557
385
  }
558
- return false;
559
- }
560
- function buildCookieHeader(cookies) {
561
- return Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join("; ");
562
- }
563
- async function fetchTokens(cookies) {
564
- const cookieHeader = buildCookieHeader(cookies);
565
- const response = await fetch(NOTEBOOKLM_URL, {
566
- headers: { Cookie: cookieHeader },
567
- redirect: "follow"
568
- });
569
- if (!response.ok) {
570
- throw new AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);
386
+ };
387
+ var SourceAddError = class extends SourceError {
388
+ url;
389
+ cause;
390
+ constructor(url, opts = {}) {
391
+ super(
392
+ opts.message ?? `Failed to add source: ${url}
393
+ Possible causes:
394
+ - URL is invalid or inaccessible
395
+ - Content is behind a paywall or requires authentication
396
+ - Rate limiting or quota exceeded`
397
+ );
398
+ this.url = url;
399
+ this.cause = opts.cause;
571
400
  }
572
- const finalUrl = response.url;
573
- if (isGoogleAuthRedirect(finalUrl)) {
574
- throw new AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);
401
+ };
402
+ var SourceProcessingError = class extends SourceError {
403
+ sourceId;
404
+ status;
405
+ constructor(sourceId, status = 3, message) {
406
+ super(message ?? `Source ${sourceId} failed to process`);
407
+ this.sourceId = sourceId;
408
+ this.status = status;
575
409
  }
576
- const html = await response.text();
577
- const csrfToken = extractCsrfToken(html, finalUrl);
578
- const sessionId = extractSessionId(html, finalUrl);
579
- return { csrfToken, sessionId };
580
- }
581
- function extractCsrfToken(html, finalUrl) {
582
- const match = /"SNlM0e"\s*:\s*"([^"]+)"/.exec(html);
583
- if (!match?.[1]) {
584
- if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
585
- throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
586
- }
587
- throw new AuthError("CSRF token (SNlM0e) not found in NotebookLM page HTML.");
410
+ };
411
+ var SourceTimeoutError = class extends SourceError {
412
+ sourceId;
413
+ timeout;
414
+ lastStatus;
415
+ constructor(sourceId, timeout, lastStatus) {
416
+ const statusInfo = lastStatus != null ? ` (last status: ${lastStatus})` : "";
417
+ super(`Source ${sourceId} not ready after ${timeout.toFixed(1)}s${statusInfo}`);
418
+ this.sourceId = sourceId;
419
+ this.timeout = timeout;
420
+ this.lastStatus = lastStatus;
588
421
  }
589
- return match[1];
590
- }
591
- function extractSessionId(html, finalUrl) {
592
- const match = /"FdrFJe"\s*:\s*"([^"]+)"/.exec(html);
593
- if (!match?.[1]) {
594
- if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
595
- throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
596
- }
597
- throw new AuthError("Session ID (FdrFJe) not found in NotebookLM page HTML.");
422
+ };
423
+ var ArtifactError = class extends NotebookLMError {
424
+ };
425
+ var ArtifactNotFoundError = class extends ArtifactError {
426
+ artifactId;
427
+ artifactType;
428
+ constructor(artifactId, artifactType) {
429
+ const typeInfo = artifactType ? ` ${artifactType}` : "";
430
+ super(`${typeInfo.trim() || "Artifact"} ${artifactId} not found`);
431
+ this.artifactId = artifactId;
432
+ this.artifactType = artifactType;
598
433
  }
599
- return match[1];
600
- }
601
- function isGoogleAuthRedirect(url) {
602
- return url.includes("accounts.google.com") || url.includes("signin");
603
- }
604
- async function connect(opts = {}) {
605
- let cookieMap;
606
- let googleCookieHeader = null;
607
- if (opts.cookies) {
608
- cookieMap = loadCookiesFromString(opts.cookies);
609
- } else if (opts.cookiesFile) {
610
- cookieMap = loadCookiesFromFile(opts.cookiesFile);
611
- } else if (opts.cookiesObject) {
612
- if ("cookies" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {
613
- const storageState = opts.cookiesObject;
614
- cookieMap = loadCookiesFromObject(storageState);
615
- googleCookieHeader = buildGoogleCookieHeader(storageState);
616
- } else {
617
- cookieMap = loadCookiesFromMap(opts.cookiesObject);
618
- }
619
- } else {
620
- const envCookies = process.env["NOTEBOOKLM_COOKIES"];
621
- const envFile = process.env["NOTEBOOKLM_COOKIES_FILE"];
622
- if (envFile) {
623
- cookieMap = loadCookiesFromFile(envFile);
624
- } else if (existsSync(DEFAULT_SESSION_FILE)) {
625
- const raw = readFileSync(DEFAULT_SESSION_FILE, "utf-8");
626
- const storageState = JSON.parse(raw);
627
- cookieMap = loadCookiesFromObject(storageState);
628
- googleCookieHeader = buildGoogleCookieHeader(storageState);
629
- } else if (existsSync("storage_state.json")) {
630
- const raw = readFileSync("storage_state.json", "utf-8");
631
- const storageState = JSON.parse(raw);
632
- cookieMap = loadCookiesFromObject(storageState);
633
- googleCookieHeader = buildGoogleCookieHeader(storageState);
634
- } else if (envCookies) {
635
- cookieMap = loadCookiesFromString(envCookies);
636
- } else {
637
- throw new AuthError("No session found. Run: npx notebooklm-sdk login");
638
- }
434
+ };
435
+ var ArtifactNotReadyError = class extends ArtifactError {
436
+ artifactType;
437
+ artifactId;
438
+ status;
439
+ constructor(artifactType, opts = {}) {
440
+ const base = opts.artifactId ? `${artifactType} artifact ${opts.artifactId} is not ready` : `No completed ${artifactType} found`;
441
+ const statusInfo = opts.status ? ` (status: ${opts.status})` : "";
442
+ super(`${base}${statusInfo}`);
443
+ this.artifactType = artifactType;
444
+ this.artifactId = opts.artifactId;
445
+ this.status = opts.status;
639
446
  }
640
- const { csrfToken, sessionId } = await fetchTokens(cookieMap);
641
- const cookieHeader = buildCookieHeader(cookieMap);
642
- return {
643
- cookies: cookieMap,
644
- csrfToken,
645
- sessionId,
646
- cookieHeader,
647
- googleCookieHeader: googleCookieHeader ?? cookieHeader
648
- };
649
- }
650
- var DEFAULT_SESSION_FILE, NOTEBOOKLM_URL;
651
- var init_auth = __esm({
652
- "src/auth.ts"() {
653
- init_errors();
654
- DEFAULT_SESSION_FILE = join(homedir(), ".notebooklm", "session.json");
655
- NOTEBOOKLM_URL = "https://notebooklm.google.com/";
447
+ };
448
+ var ArtifactParseError = class extends ArtifactError {
449
+ artifactType;
450
+ artifactId;
451
+ details;
452
+ cause;
453
+ constructor(artifactType, opts = {}) {
454
+ let msg = `Failed to parse ${artifactType} artifact`;
455
+ if (opts.artifactId) msg += ` ${opts.artifactId}`;
456
+ if (opts.details) msg += `: ${opts.details}`;
457
+ super(msg);
458
+ this.artifactType = artifactType;
459
+ this.artifactId = opts.artifactId;
460
+ this.details = opts.details;
461
+ this.cause = opts.cause;
656
462
  }
657
- });
658
-
659
- // src/api/artifacts.ts
660
- init_enums();
661
- init_errors();
463
+ };
464
+ var ArtifactDownloadError = class extends ArtifactError {
465
+ artifactType;
466
+ artifactId;
467
+ details;
468
+ cause;
469
+ constructor(artifactType, opts = {}) {
470
+ let msg = `Failed to download ${artifactType} artifact`;
471
+ if (opts.artifactId) msg += ` ${opts.artifactId}`;
472
+ if (opts.details) msg += `: ${opts.details}`;
473
+ super(msg);
474
+ this.artifactType = artifactType;
475
+ this.artifactId = opts.artifactId;
476
+ this.details = opts.details;
477
+ this.cause = opts.cause;
478
+ }
479
+ };
480
+ var ChatError = class extends NotebookLMError {
481
+ };
662
482
 
663
483
  // src/types/models.ts
664
484
  init_enums();
@@ -1122,7 +942,13 @@ ${opts.extraInstructions}` : cfg.prompt;
1122
942
  const deadline = Date.now() + timeout * 1e3;
1123
943
  while (Date.now() < deadline) {
1124
944
  const artifact = await this.get(notebookId, artifactId);
1125
- if (artifact?.status === "completed") return artifact;
945
+ if (artifact?.status === "completed") {
946
+ if (artifact.kind === "audio" && !artifact.audioUrl || artifact.kind === "video" && !artifact.videoUrl) {
947
+ await sleep(pollInterval * 1e3);
948
+ continue;
949
+ }
950
+ return artifact;
951
+ }
1126
952
  if (artifact?.status === "failed") {
1127
953
  throw new ArtifactNotReadyError(artifact.kind, { artifactId, status: "failed" });
1128
954
  }
@@ -1130,6 +956,19 @@ ${opts.extraInstructions}` : cfg.prompt;
1130
956
  }
1131
957
  throw new ArtifactNotReadyError("artifact", { artifactId, status: "timeout" });
1132
958
  }
959
+ /** Get the current status of a generated artifact without waiting. */
960
+ async pollStatus(notebookId, artifactId) {
961
+ const rawList = await this._listRaw(notebookId);
962
+ for (const item of rawList) {
963
+ if (!Array.isArray(item) || item[0] !== artifactId) continue;
964
+ const statusCode = typeof item[4] === "number" ? item[4] : null;
965
+ return {
966
+ artifactId,
967
+ status: statusCode != null ? artifactStatusFromCode(statusCode) : "pending"
968
+ };
969
+ }
970
+ return { artifactId, status: "pending" };
971
+ }
1133
972
  /** Download audio content as a Buffer. */
1134
973
  async downloadAudio(notebookId, artifactId) {
1135
974
  const artifact = await this.get(notebookId, artifactId);
@@ -1380,14 +1219,14 @@ function isTrustedDomain(url) {
1380
1219
 
1381
1220
  // src/api/chat.ts
1382
1221
  init_enums();
1383
- init_errors();
1384
1222
  var QUERY_URL = "https://notebooklm.google.com/_/LabsTailwindUi/data/google.internal.labs.tailwind.orchestration.v1.LabsTailwindOrchestrationService/GenerateFreeFormStreamed";
1385
1223
  var DEFAULT_BL = "boq_labs-tailwind-frontend_20260301.03_p0";
1386
1224
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1387
1225
  var ChatAPI = class {
1388
- constructor(rpc, auth) {
1226
+ constructor(rpc, auth, refreshAuth) {
1389
1227
  this.rpc = rpc;
1390
1228
  this.auth = auth;
1229
+ this.refreshAuth = refreshAuth;
1391
1230
  }
1392
1231
  conversationCache = /* @__PURE__ */ new Map();
1393
1232
  reqid = Math.floor(Math.random() * 9e5) + 1e5;
@@ -1417,20 +1256,7 @@ var ChatAPI = class {
1417
1256
  const bodyParts = [`f.req=${encodeURIComponent(fReq)}`];
1418
1257
  if (this.auth.csrfToken) bodyParts.push(`at=${encodeURIComponent(this.auth.csrfToken)}`);
1419
1258
  const body = bodyParts.join("&") + "&";
1420
- let response;
1421
- try {
1422
- response = await fetch(`${QUERY_URL}?${urlParams.toString()}`, {
1423
- method: "POST",
1424
- headers: {
1425
- "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
1426
- Cookie: this.auth.cookieHeader
1427
- },
1428
- body
1429
- });
1430
- } catch (e) {
1431
- throw new ChatError(`Chat request failed: ${String(e)}`);
1432
- }
1433
- if (!response.ok) throw new ChatError(`Chat request failed: HTTP ${response.status}`);
1259
+ const response = await this._postChatRequest(`${QUERY_URL}?${urlParams.toString()}`, body);
1434
1260
  const text = await response.text();
1435
1261
  const { answer, conversationId: serverConvId, references } = parseStreamingResponse(text);
1436
1262
  const finalConvId = serverConvId ?? conversationId;
@@ -1490,12 +1316,47 @@ var ChatAPI = class {
1490
1316
  }
1491
1317
  return null;
1492
1318
  }
1319
+ async getHistory(notebookId, limit = 100, conversationId) {
1320
+ const convId = conversationId ?? await this.getLastConversationId(notebookId);
1321
+ if (!convId) return [];
1322
+ const params = [[], null, null, convId, limit];
1323
+ const result = await this.rpc.call(RPCMethod.GET_CONVERSATION_TURNS, params, {
1324
+ sourcePath: `/notebook/${notebookId}`,
1325
+ allowNull: true
1326
+ });
1327
+ if (!Array.isArray(result) || !Array.isArray(result[0])) return [];
1328
+ const rawTurns = [...result[0]].reverse();
1329
+ const history = [];
1330
+ let i = 0;
1331
+ while (i < rawTurns.length) {
1332
+ const turn = rawTurns[i];
1333
+ if (!Array.isArray(turn) || turn.length < 3) {
1334
+ i++;
1335
+ continue;
1336
+ }
1337
+ if (turn[2] === 1 && turn.length > 3) {
1338
+ const query = typeof turn[3] === "string" ? turn[3] : "";
1339
+ let answer = "";
1340
+ const next = rawTurns[i + 1];
1341
+ if (Array.isArray(next) && next.length > 4 && next[2] === 2) {
1342
+ try {
1343
+ answer = String(next[4][0][0] ?? "");
1344
+ } catch {
1345
+ }
1346
+ i++;
1347
+ }
1348
+ history.push([query, answer]);
1349
+ }
1350
+ i++;
1351
+ }
1352
+ return history;
1353
+ }
1493
1354
  /**
1494
1355
  * Low-level chat configuration. Set goal, response length, and optional
1495
1356
  * custom instructions directly. Persists on the server per notebook.
1496
1357
  * Use `setMode()` for preset combinations instead.
1497
1358
  */
1498
- async configure(notebookId, goal, length, customPrompt) {
1359
+ async configure(notebookId, goal = ChatGoal.DEFAULT, length = ChatResponseLength.DEFAULT, customPrompt) {
1499
1360
  if (goal === ChatGoal.CUSTOM && !customPrompt) {
1500
1361
  throw new Error("customPrompt is required when goal is ChatGoal.CUSTOM");
1501
1362
  }
@@ -1527,6 +1388,14 @@ var ChatAPI = class {
1527
1388
  this.conversationCache.clear();
1528
1389
  }
1529
1390
  }
1391
+ getCachedTurns(conversationId) {
1392
+ const turns = this.conversationCache.get(conversationId) ?? [];
1393
+ return turns.map((turn) => ({
1394
+ query: turn.query,
1395
+ answer: turn.answer,
1396
+ turnNumber: turn.turnNumber
1397
+ }));
1398
+ }
1530
1399
  _buildHistory(conversationId) {
1531
1400
  const turns = this.conversationCache.get(conversationId) ?? [];
1532
1401
  if (!turns.length) return null;
@@ -1537,6 +1406,27 @@ var ChatAPI = class {
1537
1406
  }
1538
1407
  return history;
1539
1408
  }
1409
+ async _postChatRequest(url, body, retried = false) {
1410
+ let response;
1411
+ try {
1412
+ response = await fetch(url, {
1413
+ method: "POST",
1414
+ headers: {
1415
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
1416
+ Cookie: this.auth.cookieHeader
1417
+ },
1418
+ body
1419
+ });
1420
+ } catch (e) {
1421
+ throw new ChatError(`Chat request failed: ${String(e)}`);
1422
+ }
1423
+ if ((response.status === 401 || response.status === 403) && !retried && this.refreshAuth) {
1424
+ await this.refreshAuth();
1425
+ return this._postChatRequest(url, body, true);
1426
+ }
1427
+ if (!response.ok) throw new ChatError(`Chat request failed: HTTP ${response.status}`);
1428
+ return response;
1429
+ }
1540
1430
  };
1541
1431
  function parseStreamingResponse(rawText) {
1542
1432
  let text = rawText;
@@ -1693,6 +1583,12 @@ var NotebooksAPI = class {
1693
1583
  async removeFromRecent(notebookId) {
1694
1584
  await this.rpc.call(RPCMethod.REMOVE_RECENTLY_VIEWED, [notebookId], { allowNull: true });
1695
1585
  }
1586
+ async getRaw(notebookId) {
1587
+ const params = [notebookId, null, [2], null, 0];
1588
+ return this.rpc.call(RPCMethod.GET_NOTEBOOK, params, {
1589
+ sourcePath: `/notebook/${notebookId}`
1590
+ });
1591
+ }
1696
1592
  async getDescription(notebookId) {
1697
1593
  const params = [notebookId, [2]];
1698
1594
  const result = await this.rpc.call(RPCMethod.SUMMARIZE, params, {
@@ -1720,6 +1616,41 @@ var NotebooksAPI = class {
1720
1616
  }
1721
1617
  return { summary, suggestedTopics };
1722
1618
  }
1619
+ async share(notebookId, publicAccess = true, artifactId) {
1620
+ const shareOptions = publicAccess ? [1] : [0];
1621
+ const params = artifactId ? [shareOptions, notebookId, artifactId] : [shareOptions, notebookId];
1622
+ await this.rpc.call(RPCMethod.SHARE_ARTIFACT, params, {
1623
+ sourcePath: `/notebook/${notebookId}`,
1624
+ allowNull: true
1625
+ });
1626
+ return {
1627
+ public: publicAccess,
1628
+ url: publicAccess ? this.getShareUrl(notebookId, artifactId) : null,
1629
+ artifactId: artifactId ?? null
1630
+ };
1631
+ }
1632
+ getShareUrl(notebookId, artifactId) {
1633
+ const baseUrl = `https://notebooklm.google.com/notebook/${notebookId}`;
1634
+ return artifactId ? `${baseUrl}?artifactId=${artifactId}` : baseUrl;
1635
+ }
1636
+ async getMetadata(notebookId) {
1637
+ const raw = await this.getRaw(notebookId);
1638
+ const notebookData = Array.isArray(raw) && raw.length > 0 && Array.isArray(raw[0]) ? raw[0] : [];
1639
+ const notebook = parseNotebook(notebookData);
1640
+ const sourcesRaw = Array.isArray(notebookData[1]) ? notebookData[1] : [];
1641
+ const sources = sourcesRaw.filter((source) => Array.isArray(source) && source.length > 0).map((source) => parseSource(source));
1642
+ return {
1643
+ id: notebook.id,
1644
+ title: notebook.title,
1645
+ createdAt: notebook.createdAt,
1646
+ isOwner: notebook.isOwner,
1647
+ sources: sources.map((source) => ({
1648
+ kind: source.kind,
1649
+ title: source.title,
1650
+ url: source.url
1651
+ }))
1652
+ };
1653
+ }
1723
1654
  };
1724
1655
 
1725
1656
  // src/api/notes.ts
@@ -1732,6 +1663,10 @@ var NotesAPI = class {
1732
1663
  const all = await this._fetchAll(notebookId);
1733
1664
  return all.filter((n) => !this._isMindMap(n.content));
1734
1665
  }
1666
+ async get(notebookId, noteId) {
1667
+ const all = await this._fetchAll(notebookId);
1668
+ return all.find((note) => note.id === noteId) ?? null;
1669
+ }
1735
1670
  async listMindMaps(notebookId) {
1736
1671
  const all = await this._fetchAll(notebookId);
1737
1672
  return all.filter((n) => this._isMindMap(n.content));
@@ -1763,6 +1698,9 @@ var NotesAPI = class {
1763
1698
  });
1764
1699
  return true;
1765
1700
  }
1701
+ async deleteMindMap(notebookId, mindMapId) {
1702
+ return this.delete(notebookId, mindMapId);
1703
+ }
1766
1704
  async _fetchAll(notebookId) {
1767
1705
  const result = await this.rpc.call(RPCMethod.GET_NOTES_AND_MIND_MAPS, [notebookId], {
1768
1706
  sourcePath: `/notebook/${notebookId}`,
@@ -2139,7 +2077,6 @@ function parseShareStatus(data, notebookId) {
2139
2077
 
2140
2078
  // src/api/sources.ts
2141
2079
  init_enums();
2142
- init_errors();
2143
2080
  var UPLOAD_URL = "https://notebooklm.google.com/upload/_/";
2144
2081
  var SourcesAPI = class {
2145
2082
  constructor(rpc, auth) {
@@ -2422,6 +2359,29 @@ var SourcesAPI = class {
2422
2359
  });
2423
2360
  return true;
2424
2361
  }
2362
+ async rename(notebookId, sourceId, newTitle) {
2363
+ const params = [null, [sourceId], [[[newTitle]]]];
2364
+ const result = await this.rpc.call(RPCMethod.UPDATE_SOURCE, params, {
2365
+ sourcePath: `/notebook/${notebookId}`,
2366
+ allowNull: true
2367
+ });
2368
+ if (Array.isArray(result) && result.length > 0) {
2369
+ try {
2370
+ const parsed = parseSource(result);
2371
+ return parsed.title ? parsed : { ...parsed, title: newTitle };
2372
+ } catch {
2373
+ }
2374
+ }
2375
+ return {
2376
+ id: sourceId,
2377
+ title: newTitle,
2378
+ url: null,
2379
+ kind: "unknown",
2380
+ createdAt: null,
2381
+ status: "ready",
2382
+ _typeCode: null
2383
+ };
2384
+ }
2425
2385
  async waitUntilReady(notebookId, sourceId, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
2426
2386
  const deadline = Date.now() + timeout * 1e3;
2427
2387
  let interval = initialInterval;
@@ -2440,6 +2400,20 @@ var SourcesAPI = class {
2440
2400
  }
2441
2401
  throw new SourceTimeoutError(sourceId, timeout, lastStatus);
2442
2402
  }
2403
+ async waitForSources(notebookId, sourceIds, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
2404
+ return Promise.all(
2405
+ sourceIds.map(
2406
+ (sourceId) => this.waitUntilReady(
2407
+ notebookId,
2408
+ sourceId,
2409
+ timeout,
2410
+ initialInterval,
2411
+ maxInterval,
2412
+ backoffFactor
2413
+ )
2414
+ )
2415
+ );
2416
+ }
2443
2417
  };
2444
2418
  function extractSourceId(result) {
2445
2419
  if (Array.isArray(result)) {
@@ -2472,18 +2446,169 @@ function extractAllText(data, maxDepth = 100) {
2472
2446
  function sleep2(ms) {
2473
2447
  return new Promise((resolve) => setTimeout(resolve, ms));
2474
2448
  }
2475
-
2476
- // src/index.ts
2477
- init_auth();
2478
-
2479
- // src/client.ts
2480
- init_auth();
2481
-
2482
- // src/rpc/core.ts
2483
- init_errors();
2449
+ var DEFAULT_SESSION_FILE = join(homedir(), ".notebooklm", "session.json");
2450
+ function loadCookiesFromFile(filePath) {
2451
+ let raw;
2452
+ try {
2453
+ raw = readFileSync(filePath, "utf-8");
2454
+ } catch {
2455
+ throw new AuthError(`Session file not found: ${filePath}
2456
+ Run: npx notebooklm-sdk login`);
2457
+ }
2458
+ return extractCookiesFromStorageState(JSON.parse(raw));
2459
+ }
2460
+ function loadCookiesFromObject(storageState) {
2461
+ return extractCookiesFromStorageState(storageState);
2462
+ }
2463
+ function buildGoogleCookieHeader(storageState) {
2464
+ const map = {};
2465
+ for (const c of storageState.cookies ?? []) {
2466
+ if (c.domain === ".google.com" && c.name && c.value) {
2467
+ map[c.name] = map[c.name] ?? c.value;
2468
+ }
2469
+ }
2470
+ return buildCookieHeader(map);
2471
+ }
2472
+ function loadCookiesFromMap(map) {
2473
+ return { ...map };
2474
+ }
2475
+ function loadCookiesFromString(cookieStr) {
2476
+ const map = {};
2477
+ for (const part of cookieStr.split(/;\s*/)) {
2478
+ const idx = part.indexOf("=");
2479
+ if (idx > 0) {
2480
+ const name = part.slice(0, idx).trim();
2481
+ const value = part.slice(idx + 1).trim();
2482
+ if (name) map[name] = value;
2483
+ }
2484
+ }
2485
+ return map;
2486
+ }
2487
+ function extractCookiesFromStorageState(storageState) {
2488
+ const cookies = {};
2489
+ for (const cookie of storageState.cookies ?? []) {
2490
+ const { domain, name, value } = cookie;
2491
+ if (!isAllowedDomain(domain) || !name) continue;
2492
+ const isBase = domain === ".google.com";
2493
+ if (!(name in cookies) || isBase) {
2494
+ cookies[name] = value;
2495
+ }
2496
+ }
2497
+ if (!cookies["SID"]) {
2498
+ throw new AuthError(
2499
+ "Missing required cookie: SID. Session may be invalid or expired.\nRun: npx notebooklm-sdk login"
2500
+ );
2501
+ }
2502
+ return cookies;
2503
+ }
2504
+ function isAllowedDomain(domain) {
2505
+ if (domain === ".google.com" || domain === "notebooklm.google.com" || domain === ".googleusercontent.com") {
2506
+ return true;
2507
+ }
2508
+ if (domain.startsWith(".google.")) {
2509
+ return true;
2510
+ }
2511
+ return false;
2512
+ }
2513
+ function buildCookieHeader(cookies) {
2514
+ return Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join("; ");
2515
+ }
2516
+ var NOTEBOOKLM_URL = "https://notebooklm.google.com/";
2517
+ async function fetchTokens(cookies) {
2518
+ const cookieHeader = buildCookieHeader(cookies);
2519
+ const response = await fetch(NOTEBOOKLM_URL, {
2520
+ headers: { Cookie: cookieHeader },
2521
+ redirect: "follow"
2522
+ });
2523
+ if (!response.ok) {
2524
+ throw new AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);
2525
+ }
2526
+ const finalUrl = response.url;
2527
+ if (isGoogleAuthRedirect(finalUrl)) {
2528
+ throw new AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);
2529
+ }
2530
+ const html = await response.text();
2531
+ const csrfToken = extractCsrfToken(html, finalUrl);
2532
+ const sessionId = extractSessionId(html, finalUrl);
2533
+ return { csrfToken, sessionId };
2534
+ }
2535
+ async function refreshAuthTokens(auth) {
2536
+ const { csrfToken, sessionId } = await fetchTokens(auth.cookies);
2537
+ auth.csrfToken = csrfToken;
2538
+ auth.sessionId = sessionId;
2539
+ return auth;
2540
+ }
2541
+ function extractCsrfToken(html, finalUrl) {
2542
+ const match = /"SNlM0e"\s*:\s*"([^"]+)"/.exec(html);
2543
+ if (!match?.[1]) {
2544
+ if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
2545
+ throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
2546
+ }
2547
+ throw new AuthError("CSRF token (SNlM0e) not found in NotebookLM page HTML.");
2548
+ }
2549
+ return match[1];
2550
+ }
2551
+ function extractSessionId(html, finalUrl) {
2552
+ const match = /"FdrFJe"\s*:\s*"([^"]+)"/.exec(html);
2553
+ if (!match?.[1]) {
2554
+ if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
2555
+ throw new AuthError("Session expired or invalid.\nRun: npx notebooklm-sdk login");
2556
+ }
2557
+ throw new AuthError("Session ID (FdrFJe) not found in NotebookLM page HTML.");
2558
+ }
2559
+ return match[1];
2560
+ }
2561
+ function isGoogleAuthRedirect(url) {
2562
+ return url.includes("accounts.google.com") || url.includes("signin");
2563
+ }
2564
+ async function connect(opts = {}) {
2565
+ let cookieMap;
2566
+ let googleCookieHeader = null;
2567
+ if (opts.cookies) {
2568
+ cookieMap = loadCookiesFromString(opts.cookies);
2569
+ } else if (opts.cookiesFile) {
2570
+ cookieMap = loadCookiesFromFile(opts.cookiesFile);
2571
+ } else if (opts.cookiesObject) {
2572
+ if ("cookies" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {
2573
+ const storageState = opts.cookiesObject;
2574
+ cookieMap = loadCookiesFromObject(storageState);
2575
+ googleCookieHeader = buildGoogleCookieHeader(storageState);
2576
+ } else {
2577
+ cookieMap = loadCookiesFromMap(opts.cookiesObject);
2578
+ }
2579
+ } else {
2580
+ const envCookies = process.env["NOTEBOOKLM_COOKIES"];
2581
+ const envFile = process.env["NOTEBOOKLM_COOKIES_FILE"];
2582
+ if (envFile) {
2583
+ cookieMap = loadCookiesFromFile(envFile);
2584
+ } else if (existsSync(DEFAULT_SESSION_FILE)) {
2585
+ const raw = readFileSync(DEFAULT_SESSION_FILE, "utf-8");
2586
+ const storageState = JSON.parse(raw);
2587
+ cookieMap = loadCookiesFromObject(storageState);
2588
+ googleCookieHeader = buildGoogleCookieHeader(storageState);
2589
+ } else if (existsSync("storage_state.json")) {
2590
+ const raw = readFileSync("storage_state.json", "utf-8");
2591
+ const storageState = JSON.parse(raw);
2592
+ cookieMap = loadCookiesFromObject(storageState);
2593
+ googleCookieHeader = buildGoogleCookieHeader(storageState);
2594
+ } else if (envCookies) {
2595
+ cookieMap = loadCookiesFromString(envCookies);
2596
+ } else {
2597
+ throw new AuthError("No session found. Run: npx notebooklm-sdk login");
2598
+ }
2599
+ }
2600
+ const { csrfToken, sessionId } = await fetchTokens(cookieMap);
2601
+ const cookieHeader = buildCookieHeader(cookieMap);
2602
+ return {
2603
+ cookies: cookieMap,
2604
+ csrfToken,
2605
+ sessionId,
2606
+ cookieHeader,
2607
+ googleCookieHeader: googleCookieHeader ?? cookieHeader
2608
+ };
2609
+ }
2484
2610
 
2485
2611
  // src/rpc/decoder.ts
2486
- init_errors();
2487
2612
  function stripAntiXSSI(response) {
2488
2613
  if (response.startsWith(")]}'")) {
2489
2614
  const match = /\)\]\}'\r?\n/.exec(response);
@@ -2667,11 +2792,13 @@ var DEFAULT_TIMEOUT_MS = 3e4;
2667
2792
  var RPCCore = class {
2668
2793
  auth;
2669
2794
  timeoutMs;
2670
- constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS) {
2795
+ refreshAuth;
2796
+ constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS, refreshAuth) {
2671
2797
  this.auth = auth;
2672
2798
  this.timeoutMs = timeoutMs;
2799
+ this.refreshAuth = refreshAuth;
2673
2800
  }
2674
- async call(methodId, params, opts = {}) {
2801
+ async call(methodId, params, opts = {}, retried = false) {
2675
2802
  const sourcePath = opts.sourcePath ?? "/";
2676
2803
  const allowNull = opts.allowNull ?? false;
2677
2804
  const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
@@ -2718,6 +2845,10 @@ var RPCCore = class {
2718
2845
  });
2719
2846
  }
2720
2847
  if (status === 401 || status === 403) {
2848
+ if (!retried && this.refreshAuth) {
2849
+ await this.refreshAuth();
2850
+ return this.call(methodId, params, opts, true);
2851
+ }
2721
2852
  throw new AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
2722
2853
  methodId
2723
2854
  });
@@ -2770,12 +2901,13 @@ var RPCCore = class {
2770
2901
  var NotebookLMClient = class _NotebookLMClient {
2771
2902
  constructor(auth, opts = {}) {
2772
2903
  this.auth = auth;
2773
- const rpc = new RPCCore(auth, opts.timeoutMs);
2904
+ const refreshAuth = this.refreshTokens.bind(this);
2905
+ const rpc = new RPCCore(auth, opts.timeoutMs, refreshAuth);
2774
2906
  this.notebooks = new NotebooksAPI(rpc);
2775
2907
  this.sources = new SourcesAPI(rpc, auth);
2776
2908
  this.notes = new NotesAPI(rpc);
2777
2909
  this.artifacts = new ArtifactsAPI(rpc, auth, this.notes);
2778
- this.chat = new ChatAPI(rpc, auth);
2910
+ this.chat = new ChatAPI(rpc, auth, refreshAuth);
2779
2911
  this.research = new ResearchAPI(rpc);
2780
2912
  this.settings = new SettingsAPI(rpc);
2781
2913
  this.sharing = new SharingAPI(rpc);
@@ -2788,6 +2920,7 @@ var NotebookLMClient = class _NotebookLMClient {
2788
2920
  research;
2789
2921
  settings;
2790
2922
  sharing;
2923
+ refreshPromise = null;
2791
2924
  /**
2792
2925
  * Connect to NotebookLM using cookies.
2793
2926
  * Fetches CSRF and session tokens from the NotebookLM homepage.
@@ -2800,16 +2933,17 @@ var NotebookLMClient = class _NotebookLMClient {
2800
2933
  * Refresh CSRF and session tokens (e.g. if they expire mid-session).
2801
2934
  */
2802
2935
  async refreshTokens() {
2803
- const { fetchTokens: fetchTokens2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
2804
- const { csrfToken, sessionId } = await fetchTokens2(this.auth.cookies);
2805
- this.auth.csrfToken = csrfToken;
2806
- this.auth.sessionId = sessionId;
2936
+ if (!this.refreshPromise) {
2937
+ this.refreshPromise = refreshAuthTokens(this.auth).then(() => void 0).finally(() => {
2938
+ this.refreshPromise = null;
2939
+ });
2940
+ }
2941
+ await this.refreshPromise;
2807
2942
  }
2808
2943
  };
2809
2944
 
2810
2945
  // src/index.ts
2811
2946
  init_enums();
2812
- init_errors();
2813
2947
 
2814
2948
  export { ArtifactDownloadError, ArtifactError, ArtifactNotFoundError, ArtifactNotReadyError, ArtifactParseError, ArtifactTypeCode, ArtifactsAPI, AudioFormat, AudioLength, AuthError, ChatAPI, ChatError, ChatGoal, ChatMode, ChatResponseLength, ClientError, DriveMimeType, ExportType, InfographicDetail, InfographicOrientation, InfographicStyle, NetworkError, NotebookError, NotebookLMClient, NotebookLMError, NotebookNotFoundError, NotebooksAPI, NotesAPI, QuizDifficulty, QuizQuantity, RPCError, RPCMethod, RPCTimeoutError, RateLimitError, ResearchAPI, ServerError, SettingsAPI, ShareAccess, SharePermission, ShareViewLevel, SharingAPI, SlideDeckFormat, SlideDeckLength, SourceAddError, SourceError, SourceNotFoundError, SourceProcessingError, SourceTimeoutError, SourcesAPI, VideoFormat, VideoStyle, connect };
2815
2949
  //# sourceMappingURL=index.js.map