notebooklm-sdk 0.3.1 → 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/README.md +22 -9
- package/dist/{auth-Dxsm8894.d.cts → auth-BlG6x47F.d.cts} +2 -1
- package/dist/{auth-Dxsm8894.d.ts → auth-BlG6x47F.d.ts} +2 -1
- package/dist/auth.cjs +7 -0
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +2 -2
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +7 -1
- package/dist/auth.js.map +1 -1
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +588 -400
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -7
- package/dist/index.d.ts +31 -7
- package/dist/index.js +540 -373
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
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
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
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
|
-
|
|
556
|
-
|
|
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
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
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
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
|
|
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
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
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
|
-
|
|
660
|
-
|
|
661
|
-
|
|
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")
|
|
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);
|
|
@@ -1245,6 +1084,24 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1245
1084
|
}
|
|
1246
1085
|
// ---------------------------------------------------------------------------
|
|
1247
1086
|
// Internal
|
|
1087
|
+
/** Export a completed report artifact to Google Docs. Returns the created document URL. */
|
|
1088
|
+
async exportReport(notebookId, artifactId, title) {
|
|
1089
|
+
const params = [null, artifactId, null, title, ExportType.DOCS];
|
|
1090
|
+
const result = await this.rpc.call(RPCMethod.EXPORT_ARTIFACT, params, {
|
|
1091
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1092
|
+
allowNull: true
|
|
1093
|
+
});
|
|
1094
|
+
return extractExportUrl(result);
|
|
1095
|
+
}
|
|
1096
|
+
/** Export a completed data table artifact to Google Sheets. Returns the created spreadsheet URL. */
|
|
1097
|
+
async exportDataTable(notebookId, artifactId, title) {
|
|
1098
|
+
const params = [null, artifactId, null, title, ExportType.SHEETS];
|
|
1099
|
+
const result = await this.rpc.call(RPCMethod.EXPORT_ARTIFACT, params, {
|
|
1100
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1101
|
+
allowNull: true
|
|
1102
|
+
});
|
|
1103
|
+
return extractExportUrl(result);
|
|
1104
|
+
}
|
|
1248
1105
|
// ---------------------------------------------------------------------------
|
|
1249
1106
|
/**
|
|
1250
1107
|
* Fetch a Google-hosted media URL, manually following redirects to ensure
|
|
@@ -1327,6 +1184,21 @@ function parseDataTable(rawData) {
|
|
|
1327
1184
|
throw new Error(`Failed to parse data table: ${e}`);
|
|
1328
1185
|
}
|
|
1329
1186
|
}
|
|
1187
|
+
function extractExportUrl(result) {
|
|
1188
|
+
if (!Array.isArray(result)) return null;
|
|
1189
|
+
function findUrl(data, depth = 5) {
|
|
1190
|
+
if (depth <= 0 || data == null) return null;
|
|
1191
|
+
if (typeof data === "string" && data.startsWith("https://")) return data;
|
|
1192
|
+
if (Array.isArray(data)) {
|
|
1193
|
+
for (const item of data) {
|
|
1194
|
+
const found = findUrl(item, depth - 1);
|
|
1195
|
+
if (found) return found;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
return null;
|
|
1199
|
+
}
|
|
1200
|
+
return findUrl(result);
|
|
1201
|
+
}
|
|
1330
1202
|
function sleep(ms) {
|
|
1331
1203
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1332
1204
|
}
|
|
@@ -1347,14 +1219,14 @@ function isTrustedDomain(url) {
|
|
|
1347
1219
|
|
|
1348
1220
|
// src/api/chat.ts
|
|
1349
1221
|
init_enums();
|
|
1350
|
-
init_errors();
|
|
1351
1222
|
var QUERY_URL = "https://notebooklm.google.com/_/LabsTailwindUi/data/google.internal.labs.tailwind.orchestration.v1.LabsTailwindOrchestrationService/GenerateFreeFormStreamed";
|
|
1352
1223
|
var DEFAULT_BL = "boq_labs-tailwind-frontend_20260301.03_p0";
|
|
1353
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;
|
|
1354
1225
|
var ChatAPI = class {
|
|
1355
|
-
constructor(rpc, auth) {
|
|
1226
|
+
constructor(rpc, auth, refreshAuth) {
|
|
1356
1227
|
this.rpc = rpc;
|
|
1357
1228
|
this.auth = auth;
|
|
1229
|
+
this.refreshAuth = refreshAuth;
|
|
1358
1230
|
}
|
|
1359
1231
|
conversationCache = /* @__PURE__ */ new Map();
|
|
1360
1232
|
reqid = Math.floor(Math.random() * 9e5) + 1e5;
|
|
@@ -1384,20 +1256,7 @@ var ChatAPI = class {
|
|
|
1384
1256
|
const bodyParts = [`f.req=${encodeURIComponent(fReq)}`];
|
|
1385
1257
|
if (this.auth.csrfToken) bodyParts.push(`at=${encodeURIComponent(this.auth.csrfToken)}`);
|
|
1386
1258
|
const body = bodyParts.join("&") + "&";
|
|
1387
|
-
|
|
1388
|
-
try {
|
|
1389
|
-
response = await fetch(`${QUERY_URL}?${urlParams.toString()}`, {
|
|
1390
|
-
method: "POST",
|
|
1391
|
-
headers: {
|
|
1392
|
-
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
1393
|
-
Cookie: this.auth.cookieHeader
|
|
1394
|
-
},
|
|
1395
|
-
body
|
|
1396
|
-
});
|
|
1397
|
-
} catch (e) {
|
|
1398
|
-
throw new ChatError(`Chat request failed: ${String(e)}`);
|
|
1399
|
-
}
|
|
1400
|
-
if (!response.ok) throw new ChatError(`Chat request failed: HTTP ${response.status}`);
|
|
1259
|
+
const response = await this._postChatRequest(`${QUERY_URL}?${urlParams.toString()}`, body);
|
|
1401
1260
|
const text = await response.text();
|
|
1402
1261
|
const { answer, conversationId: serverConvId, references } = parseStreamingResponse(text);
|
|
1403
1262
|
const finalConvId = serverConvId ?? conversationId;
|
|
@@ -1457,12 +1316,47 @@ var ChatAPI = class {
|
|
|
1457
1316
|
}
|
|
1458
1317
|
return null;
|
|
1459
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
|
+
}
|
|
1460
1354
|
/**
|
|
1461
1355
|
* Low-level chat configuration. Set goal, response length, and optional
|
|
1462
1356
|
* custom instructions directly. Persists on the server per notebook.
|
|
1463
1357
|
* Use `setMode()` for preset combinations instead.
|
|
1464
1358
|
*/
|
|
1465
|
-
async configure(notebookId, goal, length, customPrompt) {
|
|
1359
|
+
async configure(notebookId, goal = ChatGoal.DEFAULT, length = ChatResponseLength.DEFAULT, customPrompt) {
|
|
1466
1360
|
if (goal === ChatGoal.CUSTOM && !customPrompt) {
|
|
1467
1361
|
throw new Error("customPrompt is required when goal is ChatGoal.CUSTOM");
|
|
1468
1362
|
}
|
|
@@ -1494,6 +1388,14 @@ var ChatAPI = class {
|
|
|
1494
1388
|
this.conversationCache.clear();
|
|
1495
1389
|
}
|
|
1496
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
|
+
}
|
|
1497
1399
|
_buildHistory(conversationId) {
|
|
1498
1400
|
const turns = this.conversationCache.get(conversationId) ?? [];
|
|
1499
1401
|
if (!turns.length) return null;
|
|
@@ -1504,6 +1406,27 @@ var ChatAPI = class {
|
|
|
1504
1406
|
}
|
|
1505
1407
|
return history;
|
|
1506
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
|
+
}
|
|
1507
1430
|
};
|
|
1508
1431
|
function parseStreamingResponse(rawText) {
|
|
1509
1432
|
let text = rawText;
|
|
@@ -1660,6 +1583,12 @@ var NotebooksAPI = class {
|
|
|
1660
1583
|
async removeFromRecent(notebookId) {
|
|
1661
1584
|
await this.rpc.call(RPCMethod.REMOVE_RECENTLY_VIEWED, [notebookId], { allowNull: true });
|
|
1662
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
|
+
}
|
|
1663
1592
|
async getDescription(notebookId) {
|
|
1664
1593
|
const params = [notebookId, [2]];
|
|
1665
1594
|
const result = await this.rpc.call(RPCMethod.SUMMARIZE, params, {
|
|
@@ -1687,6 +1616,41 @@ var NotebooksAPI = class {
|
|
|
1687
1616
|
}
|
|
1688
1617
|
return { summary, suggestedTopics };
|
|
1689
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
|
+
}
|
|
1690
1654
|
};
|
|
1691
1655
|
|
|
1692
1656
|
// src/api/notes.ts
|
|
@@ -1699,6 +1663,10 @@ var NotesAPI = class {
|
|
|
1699
1663
|
const all = await this._fetchAll(notebookId);
|
|
1700
1664
|
return all.filter((n) => !this._isMindMap(n.content));
|
|
1701
1665
|
}
|
|
1666
|
+
async get(notebookId, noteId) {
|
|
1667
|
+
const all = await this._fetchAll(notebookId);
|
|
1668
|
+
return all.find((note) => note.id === noteId) ?? null;
|
|
1669
|
+
}
|
|
1702
1670
|
async listMindMaps(notebookId) {
|
|
1703
1671
|
const all = await this._fetchAll(notebookId);
|
|
1704
1672
|
return all.filter((n) => this._isMindMap(n.content));
|
|
@@ -1730,6 +1698,9 @@ var NotesAPI = class {
|
|
|
1730
1698
|
});
|
|
1731
1699
|
return true;
|
|
1732
1700
|
}
|
|
1701
|
+
async deleteMindMap(notebookId, mindMapId) {
|
|
1702
|
+
return this.delete(notebookId, mindMapId);
|
|
1703
|
+
}
|
|
1733
1704
|
async _fetchAll(notebookId) {
|
|
1734
1705
|
const result = await this.rpc.call(RPCMethod.GET_NOTES_AND_MIND_MAPS, [notebookId], {
|
|
1735
1706
|
sourcePath: `/notebook/${notebookId}`,
|
|
@@ -2106,7 +2077,6 @@ function parseShareStatus(data, notebookId) {
|
|
|
2106
2077
|
|
|
2107
2078
|
// src/api/sources.ts
|
|
2108
2079
|
init_enums();
|
|
2109
|
-
init_errors();
|
|
2110
2080
|
var UPLOAD_URL = "https://notebooklm.google.com/upload/_/";
|
|
2111
2081
|
var SourcesAPI = class {
|
|
2112
2082
|
constructor(rpc, auth) {
|
|
@@ -2389,6 +2359,29 @@ var SourcesAPI = class {
|
|
|
2389
2359
|
});
|
|
2390
2360
|
return true;
|
|
2391
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
|
+
}
|
|
2392
2385
|
async waitUntilReady(notebookId, sourceId, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
|
|
2393
2386
|
const deadline = Date.now() + timeout * 1e3;
|
|
2394
2387
|
let interval = initialInterval;
|
|
@@ -2407,6 +2400,20 @@ var SourcesAPI = class {
|
|
|
2407
2400
|
}
|
|
2408
2401
|
throw new SourceTimeoutError(sourceId, timeout, lastStatus);
|
|
2409
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
|
+
}
|
|
2410
2417
|
};
|
|
2411
2418
|
function extractSourceId(result) {
|
|
2412
2419
|
if (Array.isArray(result)) {
|
|
@@ -2439,18 +2446,169 @@ function extractAllText(data, maxDepth = 100) {
|
|
|
2439
2446
|
function sleep2(ms) {
|
|
2440
2447
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2441
2448
|
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
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
|
+
}
|
|
2451
2610
|
|
|
2452
2611
|
// src/rpc/decoder.ts
|
|
2453
|
-
init_errors();
|
|
2454
2612
|
function stripAntiXSSI(response) {
|
|
2455
2613
|
if (response.startsWith(")]}'")) {
|
|
2456
2614
|
const match = /\)\]\}'\r?\n/.exec(response);
|
|
@@ -2634,11 +2792,13 @@ var DEFAULT_TIMEOUT_MS = 3e4;
|
|
|
2634
2792
|
var RPCCore = class {
|
|
2635
2793
|
auth;
|
|
2636
2794
|
timeoutMs;
|
|
2637
|
-
|
|
2795
|
+
refreshAuth;
|
|
2796
|
+
constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS, refreshAuth) {
|
|
2638
2797
|
this.auth = auth;
|
|
2639
2798
|
this.timeoutMs = timeoutMs;
|
|
2799
|
+
this.refreshAuth = refreshAuth;
|
|
2640
2800
|
}
|
|
2641
|
-
async call(methodId, params, opts = {}) {
|
|
2801
|
+
async call(methodId, params, opts = {}, retried = false) {
|
|
2642
2802
|
const sourcePath = opts.sourcePath ?? "/";
|
|
2643
2803
|
const allowNull = opts.allowNull ?? false;
|
|
2644
2804
|
const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
|
|
@@ -2685,6 +2845,10 @@ var RPCCore = class {
|
|
|
2685
2845
|
});
|
|
2686
2846
|
}
|
|
2687
2847
|
if (status === 401 || status === 403) {
|
|
2848
|
+
if (!retried && this.refreshAuth) {
|
|
2849
|
+
await this.refreshAuth();
|
|
2850
|
+
return this.call(methodId, params, opts, true);
|
|
2851
|
+
}
|
|
2688
2852
|
throw new AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
|
|
2689
2853
|
methodId
|
|
2690
2854
|
});
|
|
@@ -2737,12 +2901,13 @@ var RPCCore = class {
|
|
|
2737
2901
|
var NotebookLMClient = class _NotebookLMClient {
|
|
2738
2902
|
constructor(auth, opts = {}) {
|
|
2739
2903
|
this.auth = auth;
|
|
2740
|
-
const
|
|
2904
|
+
const refreshAuth = this.refreshTokens.bind(this);
|
|
2905
|
+
const rpc = new RPCCore(auth, opts.timeoutMs, refreshAuth);
|
|
2741
2906
|
this.notebooks = new NotebooksAPI(rpc);
|
|
2742
2907
|
this.sources = new SourcesAPI(rpc, auth);
|
|
2743
2908
|
this.notes = new NotesAPI(rpc);
|
|
2744
2909
|
this.artifacts = new ArtifactsAPI(rpc, auth, this.notes);
|
|
2745
|
-
this.chat = new ChatAPI(rpc, auth);
|
|
2910
|
+
this.chat = new ChatAPI(rpc, auth, refreshAuth);
|
|
2746
2911
|
this.research = new ResearchAPI(rpc);
|
|
2747
2912
|
this.settings = new SettingsAPI(rpc);
|
|
2748
2913
|
this.sharing = new SharingAPI(rpc);
|
|
@@ -2755,6 +2920,7 @@ var NotebookLMClient = class _NotebookLMClient {
|
|
|
2755
2920
|
research;
|
|
2756
2921
|
settings;
|
|
2757
2922
|
sharing;
|
|
2923
|
+
refreshPromise = null;
|
|
2758
2924
|
/**
|
|
2759
2925
|
* Connect to NotebookLM using cookies.
|
|
2760
2926
|
* Fetches CSRF and session tokens from the NotebookLM homepage.
|
|
@@ -2767,16 +2933,17 @@ var NotebookLMClient = class _NotebookLMClient {
|
|
|
2767
2933
|
* Refresh CSRF and session tokens (e.g. if they expire mid-session).
|
|
2768
2934
|
*/
|
|
2769
2935
|
async refreshTokens() {
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2936
|
+
if (!this.refreshPromise) {
|
|
2937
|
+
this.refreshPromise = refreshAuthTokens(this.auth).then(() => void 0).finally(() => {
|
|
2938
|
+
this.refreshPromise = null;
|
|
2939
|
+
});
|
|
2940
|
+
}
|
|
2941
|
+
await this.refreshPromise;
|
|
2774
2942
|
}
|
|
2775
2943
|
};
|
|
2776
2944
|
|
|
2777
2945
|
// src/index.ts
|
|
2778
2946
|
init_enums();
|
|
2779
|
-
init_errors();
|
|
2780
2947
|
|
|
2781
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 };
|
|
2782
2949
|
//# sourceMappingURL=index.js.map
|