notebooklm-sdk 0.1.2 → 0.1.5

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
@@ -12,6 +12,257 @@ var __export = (target, all) => {
12
12
  __defProp(target, name, { get: all[name], enumerable: true });
13
13
  };
14
14
 
15
+ // src/types/enums.ts
16
+ var enums_exports = {};
17
+ __export(enums_exports, {
18
+ ArtifactStatusCode: () => ArtifactStatusCode,
19
+ ArtifactTypeCode: () => exports.ArtifactTypeCode,
20
+ AudioFormat: () => exports.AudioFormat,
21
+ AudioLength: () => exports.AudioLength,
22
+ ExportType: () => exports.ExportType,
23
+ InfographicDetail: () => exports.InfographicDetail,
24
+ InfographicOrientation: () => exports.InfographicOrientation,
25
+ InfographicStyle: () => exports.InfographicStyle,
26
+ QuizDifficulty: () => exports.QuizDifficulty,
27
+ QuizQuantity: () => exports.QuizQuantity,
28
+ RPCMethod: () => exports.RPCMethod,
29
+ ShareAccess: () => exports.ShareAccess,
30
+ SharePermission: () => exports.SharePermission,
31
+ ShareViewLevel: () => exports.ShareViewLevel,
32
+ SlideDeckFormat: () => exports.SlideDeckFormat,
33
+ SlideDeckLength: () => exports.SlideDeckLength,
34
+ SourceStatusCode: () => SourceStatusCode,
35
+ VideoFormat: () => exports.VideoFormat,
36
+ VideoStyle: () => exports.VideoStyle,
37
+ artifactStatusFromCode: () => artifactStatusFromCode,
38
+ artifactTypeFromCode: () => artifactTypeFromCode,
39
+ sourceStatusFromCode: () => sourceStatusFromCode,
40
+ sourceTypeFromCode: () => sourceTypeFromCode
41
+ });
42
+ function sourceTypeFromCode(code) {
43
+ if (code == null) return "unknown";
44
+ return SOURCE_TYPE_MAP[code] ?? "unknown";
45
+ }
46
+ function artifactTypeFromCode(typeCode, variant) {
47
+ if (typeCode === 4) {
48
+ if (variant === 1) return "flashcards";
49
+ if (variant === 2) return "quiz";
50
+ return "unknown";
51
+ }
52
+ return ARTIFACT_TYPE_MAP[typeCode] ?? "unknown";
53
+ }
54
+ function artifactStatusFromCode(code) {
55
+ return ARTIFACT_STATUS_MAP[code] ?? "unknown";
56
+ }
57
+ function sourceStatusFromCode(code) {
58
+ return SOURCE_STATUS_MAP[code] ?? "unknown";
59
+ }
60
+ exports.RPCMethod = void 0; exports.ArtifactTypeCode = void 0; var ArtifactStatusCode, SourceStatusCode; exports.AudioFormat = void 0; exports.AudioLength = void 0; exports.VideoFormat = void 0; exports.VideoStyle = void 0; exports.QuizQuantity = void 0; exports.QuizDifficulty = void 0; exports.InfographicOrientation = void 0; exports.InfographicDetail = void 0; exports.InfographicStyle = void 0; exports.SlideDeckFormat = void 0; exports.SlideDeckLength = void 0; exports.ExportType = void 0; var SOURCE_TYPE_MAP, ARTIFACT_TYPE_MAP, ARTIFACT_STATUS_MAP, SOURCE_STATUS_MAP; exports.ShareAccess = void 0; exports.ShareViewLevel = void 0; exports.SharePermission = void 0;
61
+ var init_enums = __esm({
62
+ "src/types/enums.ts"() {
63
+ exports.RPCMethod = {
64
+ // Notebook
65
+ LIST_NOTEBOOKS: "wXbhsf",
66
+ CREATE_NOTEBOOK: "CCqFvf",
67
+ GET_NOTEBOOK: "rLM1Ne",
68
+ RENAME_NOTEBOOK: "s0tc2d",
69
+ DELETE_NOTEBOOK: "WWINqb",
70
+ // Sources
71
+ ADD_SOURCE: "izAoDd",
72
+ ADD_SOURCE_FILE: "o4cbdc",
73
+ DELETE_SOURCE: "tGMBJ",
74
+ GET_SOURCE: "hizoJc",
75
+ REFRESH_SOURCE: "FLmJqe",
76
+ CHECK_SOURCE_FRESHNESS: "yR9Yof",
77
+ UPDATE_SOURCE: "b7Wfje",
78
+ // Summary
79
+ SUMMARIZE: "VfAZjd",
80
+ GET_SOURCE_GUIDE: "tr032e",
81
+ GET_SUGGESTED_REPORTS: "ciyUvf",
82
+ // Artifacts
83
+ CREATE_ARTIFACT: "R7cb6c",
84
+ LIST_ARTIFACTS: "gArtLc",
85
+ DELETE_ARTIFACT: "V5N4be",
86
+ RENAME_ARTIFACT: "rc3d8d",
87
+ EXPORT_ARTIFACT: "Krh3pd",
88
+ SHARE_ARTIFACT: "RGP97b",
89
+ GET_INTERACTIVE_HTML: "v9rmvd",
90
+ REVISE_SLIDE: "KmcKPe",
91
+ // Research
92
+ START_FAST_RESEARCH: "Ljjv0c",
93
+ START_DEEP_RESEARCH: "QA9ei",
94
+ POLL_RESEARCH: "e3bVqc",
95
+ IMPORT_RESEARCH: "LBwxtb",
96
+ // Notes / Mind Maps
97
+ GENERATE_MIND_MAP: "yyryJe",
98
+ CREATE_NOTE: "CYK0Xb",
99
+ GET_NOTES_AND_MIND_MAPS: "cFji9",
100
+ UPDATE_NOTE: "cYAfTb",
101
+ DELETE_NOTE: "AH0mwd",
102
+ // Conversation
103
+ GET_LAST_CONVERSATION_ID: "hPTbtc",
104
+ GET_CONVERSATION_TURNS: "khqZz",
105
+ // Sharing
106
+ SHARE_NOTEBOOK: "QDyure",
107
+ GET_SHARE_STATUS: "JFMDGd",
108
+ // Misc
109
+ REMOVE_RECENTLY_VIEWED: "fejl7e",
110
+ GET_USER_SETTINGS: "ZwVcOc",
111
+ SET_USER_SETTINGS: "hT54vc"
112
+ };
113
+ exports.ArtifactTypeCode = {
114
+ AUDIO: 1,
115
+ REPORT: 2,
116
+ VIDEO: 3,
117
+ QUIZ: 4,
118
+ MIND_MAP: 5,
119
+ INFOGRAPHIC: 7,
120
+ SLIDE_DECK: 8,
121
+ DATA_TABLE: 9
122
+ };
123
+ ArtifactStatusCode = {
124
+ PROCESSING: 1,
125
+ PENDING: 2,
126
+ COMPLETED: 3,
127
+ FAILED: 4
128
+ };
129
+ SourceStatusCode = {
130
+ PROCESSING: 1,
131
+ READY: 2,
132
+ ERROR: 3,
133
+ PREPARING: 5
134
+ };
135
+ exports.AudioFormat = {
136
+ DEEP_DIVE: 1,
137
+ BRIEF: 2,
138
+ CRITIQUE: 3,
139
+ DEBATE: 4
140
+ };
141
+ exports.AudioLength = {
142
+ SHORT: 1,
143
+ DEFAULT: 2,
144
+ LONG: 3
145
+ };
146
+ exports.VideoFormat = {
147
+ EXPLAINER: 1,
148
+ BRIEF: 2,
149
+ CINEMATIC: 3
150
+ };
151
+ exports.VideoStyle = {
152
+ AUTO_SELECT: 1,
153
+ CUSTOM: 2,
154
+ CLASSIC: 3,
155
+ WHITEBOARD: 4,
156
+ KAWAII: 5,
157
+ ANIME: 6,
158
+ WATERCOLOR: 7,
159
+ RETRO_PRINT: 8,
160
+ HERITAGE: 9,
161
+ PAPER_CRAFT: 10
162
+ };
163
+ exports.QuizQuantity = {
164
+ FEWER: 1,
165
+ STANDARD: 2,
166
+ MORE: 2
167
+ // API limitation: same as STANDARD
168
+ };
169
+ exports.QuizDifficulty = {
170
+ EASY: 1,
171
+ MEDIUM: 2,
172
+ HARD: 3
173
+ };
174
+ exports.InfographicOrientation = {
175
+ LANDSCAPE: 1,
176
+ PORTRAIT: 2,
177
+ SQUARE: 3
178
+ };
179
+ exports.InfographicDetail = {
180
+ CONCISE: 1,
181
+ STANDARD: 2,
182
+ DETAILED: 3
183
+ };
184
+ exports.InfographicStyle = {
185
+ AUTO_SELECT: 1,
186
+ SKETCH_NOTE: 2,
187
+ PROFESSIONAL: 3,
188
+ BENTO_GRID: 4,
189
+ EDITORIAL: 5,
190
+ INSTRUCTIONAL: 6,
191
+ BRICKS: 7,
192
+ CLAY: 8,
193
+ ANIME: 9,
194
+ KAWAII: 10,
195
+ SCIENTIFIC: 11
196
+ };
197
+ exports.SlideDeckFormat = {
198
+ DETAILED_DECK: 1,
199
+ PRESENTER_SLIDES: 2
200
+ };
201
+ exports.SlideDeckLength = {
202
+ DEFAULT: 1,
203
+ SHORT: 2
204
+ };
205
+ exports.ExportType = {
206
+ DOCS: 1,
207
+ SHEETS: 2
208
+ };
209
+ SOURCE_TYPE_MAP = {
210
+ 1: "google_docs",
211
+ 2: "google_slides",
212
+ 3: "pdf",
213
+ 4: "pasted_text",
214
+ 5: "web_page",
215
+ 8: "markdown",
216
+ 9: "youtube",
217
+ 10: "media",
218
+ 11: "docx",
219
+ 13: "image",
220
+ 14: "google_spreadsheet",
221
+ 16: "csv"
222
+ };
223
+ ARTIFACT_TYPE_MAP = {
224
+ 1: "audio",
225
+ 2: "report",
226
+ 3: "video",
227
+ 5: "mind_map",
228
+ 7: "infographic",
229
+ 8: "slide_deck",
230
+ 9: "data_table"
231
+ };
232
+ ARTIFACT_STATUS_MAP = {
233
+ 1: "in_progress",
234
+ 2: "pending",
235
+ 3: "completed",
236
+ 4: "failed"
237
+ };
238
+ SOURCE_STATUS_MAP = {
239
+ 1: "processing",
240
+ 2: "ready",
241
+ 3: "error",
242
+ 5: "preparing"
243
+ };
244
+ exports.ShareAccess = {
245
+ /** Only explicitly shared users can access */
246
+ RESTRICTED: 0,
247
+ /** Anyone with the link can access */
248
+ ANYONE_WITH_LINK: 1
249
+ };
250
+ exports.ShareViewLevel = {
251
+ /** Chat + sources + notes */
252
+ FULL_NOTEBOOK: 0,
253
+ /** Chat interface only */
254
+ CHAT_ONLY: 1
255
+ };
256
+ exports.SharePermission = {
257
+ OWNER: 1,
258
+ EDITOR: 2,
259
+ VIEWER: 3,
260
+ /** Internal: remove user from share list */
261
+ _REMOVE: 4
262
+ };
263
+ }
264
+ });
265
+
15
266
  // src/types/errors.ts
16
267
  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;
17
268
  var init_errors = __esm({
@@ -349,543 +600,9 @@ var init_auth = __esm({
349
600
  }
350
601
  });
351
602
 
352
- // src/types/enums.ts
353
- var enums_exports = {};
354
- __export(enums_exports, {
355
- ArtifactStatusCode: () => ArtifactStatusCode,
356
- ArtifactTypeCode: () => exports.ArtifactTypeCode,
357
- AudioFormat: () => exports.AudioFormat,
358
- AudioLength: () => exports.AudioLength,
359
- ExportType: () => exports.ExportType,
360
- InfographicDetail: () => exports.InfographicDetail,
361
- InfographicOrientation: () => exports.InfographicOrientation,
362
- InfographicStyle: () => exports.InfographicStyle,
363
- QuizDifficulty: () => exports.QuizDifficulty,
364
- QuizQuantity: () => exports.QuizQuantity,
365
- RPCMethod: () => exports.RPCMethod,
366
- ShareAccess: () => exports.ShareAccess,
367
- SharePermission: () => exports.SharePermission,
368
- ShareViewLevel: () => exports.ShareViewLevel,
369
- SlideDeckFormat: () => exports.SlideDeckFormat,
370
- SlideDeckLength: () => exports.SlideDeckLength,
371
- SourceStatusCode: () => SourceStatusCode,
372
- VideoFormat: () => exports.VideoFormat,
373
- VideoStyle: () => exports.VideoStyle,
374
- artifactStatusFromCode: () => artifactStatusFromCode,
375
- artifactTypeFromCode: () => artifactTypeFromCode,
376
- sourceStatusFromCode: () => sourceStatusFromCode,
377
- sourceTypeFromCode: () => sourceTypeFromCode
378
- });
379
- function sourceTypeFromCode(code) {
380
- if (code == null) return "unknown";
381
- return SOURCE_TYPE_MAP[code] ?? "unknown";
382
- }
383
- function artifactTypeFromCode(typeCode, variant) {
384
- if (typeCode === 4) {
385
- if (variant === 1) return "flashcards";
386
- if (variant === 2) return "quiz";
387
- return "unknown";
388
- }
389
- return ARTIFACT_TYPE_MAP[typeCode] ?? "unknown";
390
- }
391
- function artifactStatusFromCode(code) {
392
- return ARTIFACT_STATUS_MAP[code] ?? "unknown";
393
- }
394
- function sourceStatusFromCode(code) {
395
- return SOURCE_STATUS_MAP[code] ?? "unknown";
396
- }
397
- exports.RPCMethod = void 0; exports.ArtifactTypeCode = void 0; var ArtifactStatusCode, SourceStatusCode; exports.AudioFormat = void 0; exports.AudioLength = void 0; exports.VideoFormat = void 0; exports.VideoStyle = void 0; exports.QuizQuantity = void 0; exports.QuizDifficulty = void 0; exports.InfographicOrientation = void 0; exports.InfographicDetail = void 0; exports.InfographicStyle = void 0; exports.SlideDeckFormat = void 0; exports.SlideDeckLength = void 0; exports.ExportType = void 0; var SOURCE_TYPE_MAP, ARTIFACT_TYPE_MAP, ARTIFACT_STATUS_MAP, SOURCE_STATUS_MAP; exports.ShareAccess = void 0; exports.ShareViewLevel = void 0; exports.SharePermission = void 0;
398
- var init_enums = __esm({
399
- "src/types/enums.ts"() {
400
- exports.RPCMethod = {
401
- // Notebook
402
- LIST_NOTEBOOKS: "wXbhsf",
403
- CREATE_NOTEBOOK: "CCqFvf",
404
- GET_NOTEBOOK: "rLM1Ne",
405
- RENAME_NOTEBOOK: "s0tc2d",
406
- DELETE_NOTEBOOK: "WWINqb",
407
- // Sources
408
- ADD_SOURCE: "izAoDd",
409
- ADD_SOURCE_FILE: "o4cbdc",
410
- DELETE_SOURCE: "tGMBJ",
411
- GET_SOURCE: "hizoJc",
412
- REFRESH_SOURCE: "FLmJqe",
413
- CHECK_SOURCE_FRESHNESS: "yR9Yof",
414
- UPDATE_SOURCE: "b7Wfje",
415
- // Summary
416
- SUMMARIZE: "VfAZjd",
417
- GET_SOURCE_GUIDE: "tr032e",
418
- GET_SUGGESTED_REPORTS: "ciyUvf",
419
- // Artifacts
420
- CREATE_ARTIFACT: "R7cb6c",
421
- LIST_ARTIFACTS: "gArtLc",
422
- DELETE_ARTIFACT: "V5N4be",
423
- RENAME_ARTIFACT: "rc3d8d",
424
- EXPORT_ARTIFACT: "Krh3pd",
425
- SHARE_ARTIFACT: "RGP97b",
426
- GET_INTERACTIVE_HTML: "v9rmvd",
427
- REVISE_SLIDE: "KmcKPe",
428
- // Research
429
- START_FAST_RESEARCH: "Ljjv0c",
430
- START_DEEP_RESEARCH: "QA9ei",
431
- POLL_RESEARCH: "e3bVqc",
432
- IMPORT_RESEARCH: "LBwxtb",
433
- // Notes / Mind Maps
434
- GENERATE_MIND_MAP: "yyryJe",
435
- CREATE_NOTE: "CYK0Xb",
436
- GET_NOTES_AND_MIND_MAPS: "cFji9",
437
- UPDATE_NOTE: "cYAfTb",
438
- DELETE_NOTE: "AH0mwd",
439
- // Conversation
440
- GET_LAST_CONVERSATION_ID: "hPTbtc",
441
- GET_CONVERSATION_TURNS: "khqZz",
442
- // Sharing
443
- SHARE_NOTEBOOK: "QDyure",
444
- GET_SHARE_STATUS: "JFMDGd",
445
- // Misc
446
- REMOVE_RECENTLY_VIEWED: "fejl7e",
447
- GET_USER_SETTINGS: "ZwVcOc",
448
- SET_USER_SETTINGS: "hT54vc"
449
- };
450
- exports.ArtifactTypeCode = {
451
- AUDIO: 1,
452
- REPORT: 2,
453
- VIDEO: 3,
454
- QUIZ: 4,
455
- MIND_MAP: 5,
456
- INFOGRAPHIC: 7,
457
- SLIDE_DECK: 8,
458
- DATA_TABLE: 9
459
- };
460
- ArtifactStatusCode = {
461
- PROCESSING: 1,
462
- PENDING: 2,
463
- COMPLETED: 3,
464
- FAILED: 4
465
- };
466
- SourceStatusCode = {
467
- PROCESSING: 1,
468
- READY: 2,
469
- ERROR: 3,
470
- PREPARING: 5
471
- };
472
- exports.AudioFormat = {
473
- DEEP_DIVE: 1,
474
- BRIEF: 2,
475
- CRITIQUE: 3,
476
- DEBATE: 4
477
- };
478
- exports.AudioLength = {
479
- SHORT: 1,
480
- DEFAULT: 2,
481
- LONG: 3
482
- };
483
- exports.VideoFormat = {
484
- EXPLAINER: 1,
485
- BRIEF: 2,
486
- CINEMATIC: 3
487
- };
488
- exports.VideoStyle = {
489
- AUTO_SELECT: 1,
490
- CUSTOM: 2,
491
- CLASSIC: 3,
492
- WHITEBOARD: 4,
493
- KAWAII: 5,
494
- ANIME: 6,
495
- WATERCOLOR: 7,
496
- RETRO_PRINT: 8,
497
- HERITAGE: 9,
498
- PAPER_CRAFT: 10
499
- };
500
- exports.QuizQuantity = {
501
- FEWER: 1,
502
- STANDARD: 2,
503
- MORE: 2
504
- // API limitation: same as STANDARD
505
- };
506
- exports.QuizDifficulty = {
507
- EASY: 1,
508
- MEDIUM: 2,
509
- HARD: 3
510
- };
511
- exports.InfographicOrientation = {
512
- LANDSCAPE: 1,
513
- PORTRAIT: 2,
514
- SQUARE: 3
515
- };
516
- exports.InfographicDetail = {
517
- CONCISE: 1,
518
- STANDARD: 2,
519
- DETAILED: 3
520
- };
521
- exports.InfographicStyle = {
522
- AUTO_SELECT: 1,
523
- SKETCH_NOTE: 2,
524
- PROFESSIONAL: 3,
525
- BENTO_GRID: 4,
526
- EDITORIAL: 5,
527
- INSTRUCTIONAL: 6,
528
- BRICKS: 7,
529
- CLAY: 8,
530
- ANIME: 9,
531
- KAWAII: 10,
532
- SCIENTIFIC: 11
533
- };
534
- exports.SlideDeckFormat = {
535
- DETAILED_DECK: 1,
536
- PRESENTER_SLIDES: 2
537
- };
538
- exports.SlideDeckLength = {
539
- DEFAULT: 1,
540
- SHORT: 2
541
- };
542
- exports.ExportType = {
543
- DOCS: 1,
544
- SHEETS: 2
545
- };
546
- SOURCE_TYPE_MAP = {
547
- 1: "google_docs",
548
- 2: "google_slides",
549
- 3: "pdf",
550
- 4: "pasted_text",
551
- 5: "web_page",
552
- 8: "markdown",
553
- 9: "youtube",
554
- 10: "media",
555
- 11: "docx",
556
- 13: "image",
557
- 14: "google_spreadsheet",
558
- 16: "csv"
559
- };
560
- ARTIFACT_TYPE_MAP = {
561
- 1: "audio",
562
- 2: "report",
563
- 3: "video",
564
- 5: "mind_map",
565
- 7: "infographic",
566
- 8: "slide_deck",
567
- 9: "data_table"
568
- };
569
- ARTIFACT_STATUS_MAP = {
570
- 1: "in_progress",
571
- 2: "pending",
572
- 3: "completed",
573
- 4: "failed"
574
- };
575
- SOURCE_STATUS_MAP = {
576
- 1: "processing",
577
- 2: "ready",
578
- 3: "error",
579
- 5: "preparing"
580
- };
581
- exports.ShareAccess = {
582
- /** Only explicitly shared users can access */
583
- RESTRICTED: 0,
584
- /** Anyone with the link can access */
585
- ANYONE_WITH_LINK: 1
586
- };
587
- exports.ShareViewLevel = {
588
- /** Chat + sources + notes */
589
- FULL_NOTEBOOK: 0,
590
- /** Chat interface only */
591
- CHAT_ONLY: 1
592
- };
593
- exports.SharePermission = {
594
- OWNER: 1,
595
- EDITOR: 2,
596
- VIEWER: 3,
597
- /** Internal: remove user from share list */
598
- _REMOVE: 4
599
- };
600
- }
601
- });
602
-
603
- // src/client.ts
604
- init_auth();
605
-
606
- // src/rpc/core.ts
607
- init_errors();
608
-
609
- // src/rpc/encoder.ts
610
- function encodeRPCRequest(methodId, params) {
611
- const paramsJson = JSON.stringify(params);
612
- return [[[methodId, paramsJson, null, "generic"]]];
613
- }
614
- function buildRequestBody(rpcRequest, csrfToken) {
615
- const fReq = encodeURIComponent(JSON.stringify(rpcRequest));
616
- const at = encodeURIComponent(csrfToken);
617
- return `f.req=${fReq}&at=${at}&`;
618
- }
619
- function buildUrlParams(methodId, sessionId, sourcePath = "/") {
620
- return new URLSearchParams({
621
- rpcids: methodId,
622
- "source-path": sourcePath,
623
- "f.sid": sessionId,
624
- hl: "en",
625
- rt: "c"
626
- });
627
- }
628
-
629
- // src/rpc/decoder.ts
630
- init_errors();
631
- function stripAntiXSSI(response) {
632
- if (response.startsWith(")]}'")) {
633
- const match = /\)\]\}'\r?\n/.exec(response);
634
- if (match) return response.slice(match[0].length);
635
- }
636
- return response;
637
- }
638
- function parseChunkedResponse(response) {
639
- if (!response || !response.trim()) return [];
640
- const chunks = [];
641
- let skippedCount = 0;
642
- const lines = response.trim().split("\n");
643
- let i = 0;
644
- while (i < lines.length) {
645
- const line = (lines[i] ?? "").trim();
646
- if (!line) {
647
- i++;
648
- continue;
649
- }
650
- if (/^\d+$/.test(line)) {
651
- i++;
652
- if (i < lines.length) {
653
- try {
654
- const chunk = JSON.parse(lines[i] ?? "");
655
- chunks.push(chunk);
656
- } catch {
657
- skippedCount++;
658
- }
659
- }
660
- i++;
661
- } else {
662
- try {
663
- const chunk = JSON.parse(line);
664
- chunks.push(chunk);
665
- } catch {
666
- skippedCount++;
667
- }
668
- i++;
669
- }
670
- }
671
- if (skippedCount > 0 && lines.length > 0) {
672
- const errorRate = skippedCount / lines.length;
673
- if (errorRate > 0.1) {
674
- throw new exports.RPCError(
675
- `Response parsing failed: ${skippedCount} of ${lines.length} chunks malformed`,
676
- { rawResponse: response.slice(0, 500) }
677
- );
678
- }
679
- }
680
- return chunks;
681
- }
682
- function containsUserDisplayableError(obj) {
683
- if (typeof obj === "string") return obj.includes("UserDisplayableError");
684
- if (Array.isArray(obj)) return obj.some(containsUserDisplayableError);
685
- if (obj !== null && typeof obj === "object") {
686
- return Object.values(obj).some(containsUserDisplayableError);
687
- }
688
- return false;
689
- }
690
- function collectRPCIds(chunks) {
691
- const ids = [];
692
- for (const chunk of chunks) {
693
- if (!Array.isArray(chunk)) continue;
694
- const items = Array.isArray(chunk[0]) ? chunk : [chunk];
695
- for (const item of items) {
696
- if (!Array.isArray(item) || item.length < 2) continue;
697
- if ((item[0] === "wrb.fr" || item[0] === "er") && typeof item[1] === "string") {
698
- ids.push(item[1]);
699
- }
700
- }
701
- }
702
- return ids;
703
- }
704
- function extractRPCResult(chunks, rpcId) {
705
- for (const chunk of chunks) {
706
- if (!Array.isArray(chunk)) continue;
707
- const items = Array.isArray(chunk[0]) ? chunk : [chunk];
708
- for (const item of items) {
709
- if (!Array.isArray(item) || item.length < 3) continue;
710
- if (item[0] === "er" && item[1] === rpcId) {
711
- const code = item[2];
712
- let msg = "Unknown error";
713
- if (typeof code === "number") {
714
- if (code === 429) msg = "API rate limit exceeded. Please wait before retrying.";
715
- else if (code === 401 || code === 403) msg = "Authentication required or forbidden.";
716
- else if (code === 404) msg = "Resource not found.";
717
- else if (code >= 500) msg = `Server error ${code}. Try again later.`;
718
- else msg = `Error code: ${code}`;
719
- } else if (typeof code === "string") {
720
- msg = code;
721
- }
722
- throw new exports.RPCError(msg, { methodId: rpcId, rpcCode: code });
723
- }
724
- if (item[0] === "wrb.fr" && item[1] === rpcId) {
725
- const resultData = item[2];
726
- if (resultData === null && item.length > 5 && item[5] != null) {
727
- if (containsUserDisplayableError(item[5])) {
728
- throw new exports.RateLimitError(
729
- "API rate limit or quota exceeded. Please wait before retrying.",
730
- { methodId: rpcId, rpcCode: "USER_DISPLAYABLE_ERROR" }
731
- );
732
- }
733
- }
734
- if (typeof resultData === "string") {
735
- try {
736
- return JSON.parse(resultData);
737
- } catch {
738
- return resultData;
739
- }
740
- }
741
- return resultData;
742
- }
743
- }
744
- }
745
- return void 0;
746
- }
747
- function decodeResponse(rawResponse, rpcId, allowNull = false) {
748
- const cleaned = stripAntiXSSI(rawResponse);
749
- const chunks = parseChunkedResponse(cleaned);
750
- const responsePreview = cleaned.slice(0, 500);
751
- const foundIds = collectRPCIds(chunks);
752
- let result;
753
- try {
754
- result = extractRPCResult(chunks, rpcId);
755
- } catch (e) {
756
- if (e instanceof exports.RPCError && e.foundIds.length === 0) {
757
- throw new exports.RPCError(e.message, {
758
- methodId: e.methodId,
759
- rpcCode: e.rpcCode,
760
- foundIds,
761
- rawResponse: responsePreview
762
- });
763
- }
764
- throw e;
765
- }
766
- if (result === void 0 && !allowNull) {
767
- if (foundIds.length > 0 && !foundIds.includes(rpcId)) {
768
- throw new exports.RPCError(
769
- `No result for RPC ID '${rpcId}'. Response has IDs: ${foundIds.join(", ")}. Method ID may have changed.`,
770
- { methodId: rpcId, foundIds, rawResponse: responsePreview }
771
- );
772
- }
773
- throw new exports.RPCError(`No result found for RPC ID: ${rpcId} (${chunks.length} chunks parsed)`, {
774
- methodId: rpcId,
775
- foundIds,
776
- rawResponse: responsePreview
777
- });
778
- }
779
- return result ?? null;
780
- }
781
-
782
- // src/rpc/core.ts
783
- var BATCHEXECUTE_URL = "https://notebooklm.google.com/_/LabsTailwindUi/data/batchexecute";
784
- var DEFAULT_TIMEOUT_MS = 3e4;
785
- var RPCCore = class {
786
- auth;
787
- timeoutMs;
788
- constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS) {
789
- this.auth = auth;
790
- this.timeoutMs = timeoutMs;
791
- }
792
- async call(methodId, params, opts = {}) {
793
- const sourcePath = opts.sourcePath ?? "/";
794
- const allowNull = opts.allowNull ?? false;
795
- const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
796
- const rpcRequest = encodeRPCRequest(methodId, params);
797
- const body = buildRequestBody(rpcRequest, this.auth.csrfToken);
798
- const urlParams = buildUrlParams(methodId, this.auth.sessionId, sourcePath);
799
- const url = `${BATCHEXECUTE_URL}?${urlParams.toString()}`;
800
- const controller = new AbortController();
801
- const timer = setTimeout(() => controller.abort(), timeoutMs);
802
- let response;
803
- try {
804
- response = await fetch(url, {
805
- method: "POST",
806
- headers: {
807
- "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
808
- Cookie: this.auth.cookieHeader
809
- },
810
- body,
811
- signal: controller.signal
812
- });
813
- } catch (e) {
814
- clearTimeout(timer);
815
- if (e instanceof Error && e.name === "AbortError") {
816
- throw new exports.RPCTimeoutError(`Request timed out calling ${methodId}`, {
817
- methodId,
818
- originalError: e
819
- });
820
- }
821
- throw new exports.NetworkError(`Request failed calling ${methodId}: ${String(e)}`, {
822
- methodId,
823
- originalError: e instanceof Error ? e : void 0
824
- });
825
- } finally {
826
- clearTimeout(timer);
827
- }
828
- if (!response.ok) {
829
- const status = response.status;
830
- if (status === 429) {
831
- const retryAfterHeader = response.headers.get("retry-after");
832
- const retryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : void 0;
833
- throw new exports.RateLimitError(`API rate limit exceeded calling ${methodId}`, {
834
- methodId,
835
- retryAfter: isNaN(retryAfter ?? NaN) ? void 0 : retryAfter
836
- });
837
- }
838
- if (status === 401 || status === 403) {
839
- throw new exports.AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
840
- methodId
841
- });
842
- }
843
- if (status >= 500) {
844
- throw new exports.ServerError(`Server error ${status} calling ${methodId}`, {
845
- methodId,
846
- statusCode: status
847
- });
848
- }
849
- if (status >= 400) {
850
- throw new exports.ClientError(`Client error ${status} calling ${methodId}`, {
851
- methodId,
852
- statusCode: status
853
- });
854
- }
855
- throw new exports.RPCError(`HTTP ${status} calling ${methodId}`, { methodId });
856
- }
857
- const text = await response.text();
858
- return decodeResponse(text, methodId, allowNull);
859
- }
860
- /** Extract source IDs from a notebook (used by chat/artifact APIs). */
861
- async getSourceIds(notebookId) {
862
- const params = [notebookId, null, [2], null, 0];
863
- const { RPCMethod: RPCMethod2 } = await Promise.resolve().then(() => (init_enums(), enums_exports));
864
- const data = await this.call(RPCMethod2.GET_NOTEBOOK, params, {
865
- sourcePath: `/notebook/${notebookId}`
866
- });
867
- const sourceIds = [];
868
- if (!Array.isArray(data) || !data.length) return sourceIds;
869
- try {
870
- const nbInfo = data[0];
871
- if (!Array.isArray(nbInfo) || nbInfo.length <= 1) return sourceIds;
872
- const sources = nbInfo[1];
873
- if (!Array.isArray(sources)) return sourceIds;
874
- for (const src of sources) {
875
- if (!Array.isArray(src) || !src.length) continue;
876
- const first = src[0];
877
- if (Array.isArray(first) && first.length > 0 && typeof first[0] === "string") {
878
- sourceIds.push(first[0]);
879
- }
880
- }
881
- } catch {
882
- }
883
- return sourceIds;
884
- }
885
- };
886
-
887
- // src/api/notebooks.ts
603
+ // src/api/artifacts.ts
888
604
  init_enums();
605
+ init_errors();
889
606
 
890
607
  // src/types/models.ts
891
608
  init_enums();
@@ -1010,286 +727,14 @@ function parseNote(data) {
1010
727
  }
1011
728
  if (Array.isArray(data[4]) && typeof data[4][0] === "number") {
1012
729
  try {
1013
- updatedAt = new Date(data[4][0] * 1e3);
1014
- } catch {
1015
- }
1016
- }
1017
- return { id, title, content, createdAt, updatedAt };
1018
- }
1019
-
1020
- // src/api/notebooks.ts
1021
- var NotebooksAPI = class {
1022
- constructor(rpc) {
1023
- this.rpc = rpc;
1024
- }
1025
- async list() {
1026
- const params = [null, 1, null, [2]];
1027
- const result = await this.rpc.call(exports.RPCMethod.LIST_NOTEBOOKS, params);
1028
- if (!Array.isArray(result) || !result.length) return [];
1029
- const raw = Array.isArray(result[0]) ? result[0] : result;
1030
- return raw.map((nb) => parseNotebook(nb));
1031
- }
1032
- async create(title) {
1033
- const params = [title, null, null, [2], [1]];
1034
- const result = await this.rpc.call(exports.RPCMethod.CREATE_NOTEBOOK, params);
1035
- return parseNotebook(result);
1036
- }
1037
- async get(notebookId) {
1038
- const params = [notebookId, null, [2], null, 0];
1039
- const result = await this.rpc.call(exports.RPCMethod.GET_NOTEBOOK, params, {
1040
- sourcePath: `/notebook/${notebookId}`
1041
- });
1042
- const data = Array.isArray(result) && result.length ? result[0] : result;
1043
- return parseNotebook(data);
1044
- }
1045
- async delete(notebookId) {
1046
- const params = [[notebookId], [2]];
1047
- await this.rpc.call(exports.RPCMethod.DELETE_NOTEBOOK, params);
1048
- return true;
1049
- }
1050
- async rename(notebookId, newTitle) {
1051
- const params = [notebookId, [[null, null, null, [null, newTitle]]]];
1052
- await this.rpc.call(exports.RPCMethod.RENAME_NOTEBOOK, params, {
1053
- sourcePath: "/",
1054
- allowNull: true
1055
- });
1056
- return this.get(notebookId);
1057
- }
1058
- async getSummary(notebookId) {
1059
- const params = [notebookId, [2]];
1060
- const result = await this.rpc.call(exports.RPCMethod.SUMMARIZE, params, {
1061
- sourcePath: `/notebook/${notebookId}`
1062
- });
1063
- try {
1064
- if (Array.isArray(result)) {
1065
- const val = result[0]?.[0]?.[0];
1066
- return typeof val === "string" ? val : "";
1067
- }
1068
- } catch {
1069
- }
1070
- return "";
1071
- }
1072
- async getDescription(notebookId) {
1073
- const params = [notebookId, [2]];
1074
- const result = await this.rpc.call(exports.RPCMethod.SUMMARIZE, params, {
1075
- sourcePath: `/notebook/${notebookId}`
1076
- });
1077
- let summary = "";
1078
- const suggestedTopics = [];
1079
- try {
1080
- if (Array.isArray(result)) {
1081
- const outer = result[0];
1082
- if (Array.isArray(outer)) {
1083
- const summaryVal = outer[0]?.[0];
1084
- if (typeof summaryVal === "string") summary = summaryVal;
1085
- const topicsList = outer[1]?.[0];
1086
- if (Array.isArray(topicsList)) {
1087
- for (const t of topicsList) {
1088
- const question = typeof t[0] === "string" ? t[0] : "";
1089
- const prompt = typeof t[1] === "string" ? t[1] : "";
1090
- suggestedTopics.push({ question, prompt });
1091
- }
1092
- }
1093
- }
1094
- }
1095
- } catch {
1096
- }
1097
- return { summary, suggestedTopics };
1098
- }
1099
- };
1100
-
1101
- // src/api/sources.ts
1102
- init_enums();
1103
- init_errors();
1104
- var UPLOAD_URL = "https://notebooklm.google.com/upload/_/";
1105
- var SourcesAPI = class {
1106
- constructor(rpc, auth) {
1107
- this.rpc = rpc;
1108
- this.auth = auth;
1109
- }
1110
- async list(notebookId) {
1111
- const params = [notebookId, null, [2], null, 0];
1112
- const notebook = await this.rpc.call(exports.RPCMethod.GET_NOTEBOOK, params, {
1113
- sourcePath: `/notebook/${notebookId}`
1114
- });
1115
- if (!Array.isArray(notebook) || !notebook.length) return [];
1116
- const nbInfo = notebook[0];
1117
- if (!Array.isArray(nbInfo) || nbInfo.length <= 1) return [];
1118
- const sourcesList = nbInfo[1];
1119
- if (!Array.isArray(sourcesList)) return [];
1120
- return sourcesList.filter((s) => Array.isArray(s) && s.length > 0).map((s) => parseSource(s));
1121
- }
1122
- async get(notebookId, sourceId) {
1123
- const sources = await this.list(notebookId);
1124
- return sources.find((s) => s.id === sourceId) ?? null;
1125
- }
1126
- async addUrl(notebookId, url, opts = {}) {
1127
- const params = [notebookId, [[url]], null, null, [2]];
1128
- const result = await this.rpc.call(exports.RPCMethod.ADD_SOURCE, params, {
1129
- sourcePath: `/notebook/${notebookId}`
1130
- });
1131
- const sourceId = extractSourceId(result);
1132
- if (opts.waitUntilReady) {
1133
- return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
1134
- }
1135
- return {
1136
- id: sourceId,
1137
- title: url,
1138
- url,
1139
- kind: "web_page",
1140
- createdAt: null,
1141
- status: "processing",
1142
- _typeCode: null
1143
- };
1144
- }
1145
- async addText(notebookId, text, title, opts = {}) {
1146
- const params = [notebookId, null, [[null, null, null, text, title ?? null]], null, [2]];
1147
- const result = await this.rpc.call(exports.RPCMethod.ADD_SOURCE, params, {
1148
- sourcePath: `/notebook/${notebookId}`
1149
- });
1150
- const sourceId = extractSourceId(result);
1151
- if (opts.waitUntilReady) {
1152
- return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
1153
- }
1154
- return {
1155
- id: sourceId,
1156
- title: title ?? null,
1157
- url: null,
1158
- kind: "pasted_text",
1159
- createdAt: null,
1160
- status: "processing",
1161
- _typeCode: null
1162
- };
1163
- }
1164
- async addFile(notebookId, filePath, mimeType, opts = {}) {
1165
- const fileData = fs.readFileSync(filePath);
1166
- const fileName = filePath.split("/").pop() ?? "file";
1167
- return this.addFileBuffer(notebookId, fileData, fileName, mimeType, opts);
1168
- }
1169
- async addFileBuffer(notebookId, data, fileName, mimeType, opts = {}) {
1170
- const uploadUrl = await this.uploadFile(notebookId, data, fileName, mimeType);
1171
- const params = [notebookId, null, null, [[uploadUrl, fileName, mimeType]], [2]];
1172
- const result = await this.rpc.call(exports.RPCMethod.ADD_SOURCE_FILE, params, {
1173
- sourcePath: `/notebook/${notebookId}`
1174
- });
1175
- const sourceId = extractSourceId(result);
1176
- if (opts.waitUntilReady) {
1177
- return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
1178
- }
1179
- return {
1180
- id: sourceId,
1181
- title: fileName,
1182
- url: null,
1183
- kind: "pdf",
1184
- createdAt: null,
1185
- status: "processing",
1186
- _typeCode: null
1187
- };
1188
- }
1189
- async uploadFile(notebookId, data, fileName, mimeType) {
1190
- const params = new URLSearchParams({
1191
- "source-path": `/notebook/${notebookId}`,
1192
- upload_id: `${Date.now()}`,
1193
- upload_protocol: "resumable"
1194
- });
1195
- const initResp = await fetch(`${UPLOAD_URL}?${params}`, {
1196
- method: "POST",
1197
- headers: {
1198
- Cookie: this.auth.cookieHeader,
1199
- "X-Goog-Upload-Command": "start",
1200
- "X-Goog-Upload-Header-Content-Length": String(data.length),
1201
- "X-Goog-Upload-Header-Content-Type": mimeType,
1202
- "X-Upload-Content-Type": mimeType,
1203
- "Content-Type": "application/json"
1204
- },
1205
- body: JSON.stringify({ title: fileName })
1206
- });
1207
- if (!initResp.ok) {
1208
- throw new Error(`Upload initiation failed: HTTP ${initResp.status}`);
1209
- }
1210
- const uploadSessionUrl = initResp.headers.get("x-goog-upload-url") ?? `${UPLOAD_URL}?${params}`;
1211
- const uploadResp = await fetch(uploadSessionUrl, {
1212
- method: "POST",
1213
- headers: {
1214
- Cookie: this.auth.cookieHeader,
1215
- "X-Goog-Upload-Command": "upload, finalize",
1216
- "X-Goog-Upload-Offset": "0",
1217
- "Content-Type": mimeType,
1218
- "Content-Length": String(data.length)
1219
- },
1220
- body: data instanceof Buffer ? data.buffer : data.buffer
1221
- });
1222
- if (!uploadResp.ok) {
1223
- throw new Error(`File upload failed: HTTP ${uploadResp.status}`);
1224
- }
1225
- const uploadResult = await uploadResp.text();
1226
- return uploadResult.trim();
1227
- }
1228
- async delete(notebookId, sourceId) {
1229
- const params = [notebookId, [sourceId], [2]];
1230
- await this.rpc.call(exports.RPCMethod.DELETE_SOURCE, params, {
1231
- sourcePath: `/notebook/${notebookId}`,
1232
- allowNull: true
1233
- });
1234
- return true;
1235
- }
1236
- async refresh(notebookId, sourceId) {
1237
- const params = [notebookId, sourceId, [2]];
1238
- await this.rpc.call(exports.RPCMethod.REFRESH_SOURCE, params, {
1239
- sourcePath: `/notebook/${notebookId}`,
1240
- allowNull: true
1241
- });
1242
- return true;
1243
- }
1244
- async waitUntilReady(notebookId, sourceId, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
1245
- const deadline = Date.now() + timeout * 1e3;
1246
- let interval = initialInterval;
1247
- let lastStatus;
1248
- while (Date.now() < deadline) {
1249
- const source = await this.get(notebookId, sourceId);
1250
- if (source) {
1251
- if (source.status === "ready") return source;
1252
- if (source.status === "error") {
1253
- throw new exports.SourceProcessingError(sourceId, 3);
1254
- }
1255
- lastStatus = source._typeCode ?? void 0;
1256
- }
1257
- await sleep(interval * 1e3);
1258
- interval = Math.min(interval * backoffFactor, maxInterval);
1259
- }
1260
- throw new exports.SourceTimeoutError(sourceId, timeout, lastStatus);
1261
- }
1262
- };
1263
- function extractSourceId(result) {
1264
- if (Array.isArray(result)) {
1265
- try {
1266
- const v0 = result[0];
1267
- if (Array.isArray(v0)) {
1268
- const v00 = v0[0];
1269
- if (Array.isArray(v00)) {
1270
- const v000 = v00[0];
1271
- if (Array.isArray(v000) && typeof v000[0] === "string") return v000[0];
1272
- if (typeof v000 === "string") return v000;
1273
- }
1274
- if (typeof v00 === "string") return v00;
1275
- }
1276
- if (typeof v0 === "string") return v0;
730
+ updatedAt = new Date(data[4][0] * 1e3);
1277
731
  } catch {
1278
732
  }
1279
- for (const item of result) {
1280
- if (typeof item === "string" && item.length > 8) return item;
1281
- }
1282
733
  }
1283
- if (typeof result === "string") return result;
1284
- throw new Error("Could not extract source ID from API response");
1285
- }
1286
- function sleep(ms) {
1287
- return new Promise((resolve) => setTimeout(resolve, ms));
734
+ return { id, title, content, createdAt, updatedAt };
1288
735
  }
1289
736
 
1290
737
  // src/api/artifacts.ts
1291
- init_enums();
1292
- init_errors();
1293
738
  function tripleNest(ids) {
1294
739
  return ids.map((id) => [[id]]);
1295
740
  }
@@ -1578,7 +1023,7 @@ ${opts.extraInstructions}` : cfg.prompt;
1578
1023
  if (artifact?.status === "failed") {
1579
1024
  throw new exports.ArtifactNotReadyError(artifact.kind, { artifactId, status: "failed" });
1580
1025
  }
1581
- await sleep2(pollInterval * 1e3);
1026
+ await sleep(pollInterval * 1e3);
1582
1027
  }
1583
1028
  throw new exports.ArtifactNotReadyError("artifact", { artifactId, status: "timeout" });
1584
1029
  }
@@ -1676,7 +1121,7 @@ ${opts.extraInstructions}` : cfg.prompt;
1676
1121
  return { artifactId: null, status: "failed" };
1677
1122
  }
1678
1123
  };
1679
- function sleep2(ms) {
1124
+ function sleep(ms) {
1680
1125
  return new Promise((resolve) => setTimeout(resolve, ms));
1681
1126
  }
1682
1127
  var TRUSTED_MEDIA_DOMAINS = [
@@ -1746,589 +1191,1185 @@ var ChatAPI = class {
1746
1191
  } catch (e) {
1747
1192
  throw new exports.ChatError(`Chat request failed: ${String(e)}`);
1748
1193
  }
1749
- if (!response.ok) throw new exports.ChatError(`Chat request failed: HTTP ${response.status}`);
1750
- const text = await response.text();
1751
- const { answer, conversationId: serverConvId, references } = parseStreamingResponse(text);
1752
- const finalConvId = serverConvId ?? conversationId;
1753
- const cached = this.conversationCache.get(finalConvId) ?? [];
1754
- const turnNumber = cached.length + 1;
1755
- cached.push({ query, answer, turnNumber });
1756
- this.conversationCache.set(finalConvId, cached);
1757
- return { answer, conversationId: finalConvId, turnNumber, references };
1194
+ if (!response.ok) throw new exports.ChatError(`Chat request failed: HTTP ${response.status}`);
1195
+ const text = await response.text();
1196
+ const { answer, conversationId: serverConvId, references } = parseStreamingResponse(text);
1197
+ const finalConvId = serverConvId ?? conversationId;
1198
+ const cached = this.conversationCache.get(finalConvId) ?? [];
1199
+ const turnNumber = cached.length + 1;
1200
+ cached.push({ query, answer, turnNumber });
1201
+ this.conversationCache.set(finalConvId, cached);
1202
+ return { answer, conversationId: finalConvId, turnNumber, references };
1203
+ }
1204
+ async getConversationTurns(notebookId, conversationId) {
1205
+ const params = [[], null, null, conversationId, 100];
1206
+ const result = await this.rpc.call(exports.RPCMethod.GET_CONVERSATION_TURNS, params, {
1207
+ sourcePath: `/notebook/${notebookId}`,
1208
+ allowNull: true
1209
+ });
1210
+ if (!Array.isArray(result) || !Array.isArray(result[0])) return [];
1211
+ const rawTurns = [...result[0]].reverse();
1212
+ const turns = [];
1213
+ let i = 0;
1214
+ while (i < rawTurns.length) {
1215
+ const turn = rawTurns[i];
1216
+ if (!Array.isArray(turn) || turn.length < 3) {
1217
+ i++;
1218
+ continue;
1219
+ }
1220
+ if (turn[2] === 1 && turn.length > 3) {
1221
+ const q = typeof turn[3] === "string" ? turn[3] : "";
1222
+ let a = "";
1223
+ const next = rawTurns[i + 1];
1224
+ if (Array.isArray(next) && next.length > 4 && next[2] === 2) {
1225
+ try {
1226
+ a = String(next[4][0][0] ?? "");
1227
+ } catch {
1228
+ }
1229
+ i++;
1230
+ }
1231
+ turns.push({ query: q, answer: a, turnNumber: turns.length + 1 });
1232
+ }
1233
+ i++;
1234
+ }
1235
+ return turns;
1236
+ }
1237
+ async getLastConversationId(notebookId) {
1238
+ const params = [[], null, notebookId, 1];
1239
+ const result = await this.rpc.call(exports.RPCMethod.GET_LAST_CONVERSATION_ID, params, {
1240
+ sourcePath: `/notebook/${notebookId}`,
1241
+ allowNull: true
1242
+ });
1243
+ if (!Array.isArray(result)) return null;
1244
+ for (const group of result) {
1245
+ if (!Array.isArray(group)) continue;
1246
+ for (const conv of group) {
1247
+ if (Array.isArray(conv) && typeof conv[0] === "string") {
1248
+ return conv[0];
1249
+ }
1250
+ }
1251
+ }
1252
+ return null;
1253
+ }
1254
+ clearCache(conversationId) {
1255
+ if (conversationId) {
1256
+ this.conversationCache.delete(conversationId);
1257
+ } else {
1258
+ this.conversationCache.clear();
1259
+ }
1260
+ }
1261
+ _buildHistory(conversationId) {
1262
+ const turns = this.conversationCache.get(conversationId) ?? [];
1263
+ if (!turns.length) return null;
1264
+ const history = [];
1265
+ for (const turn of turns) {
1266
+ history.push([turn.answer, null, 2]);
1267
+ history.push([turn.query, null, 1]);
1268
+ }
1269
+ return history;
1270
+ }
1271
+ };
1272
+ function parseStreamingResponse(rawText) {
1273
+ let text = rawText;
1274
+ if (text.startsWith(")]}'")) text = text.slice(4);
1275
+ const lines = text.trim().split("\n");
1276
+ let bestMarkedAnswer = "";
1277
+ let bestUnmarkedAnswer = "";
1278
+ let serverConvId = null;
1279
+ const references = [];
1280
+ function processChunk(jsonStr) {
1281
+ let data;
1282
+ try {
1283
+ data = JSON.parse(jsonStr);
1284
+ } catch {
1285
+ return;
1286
+ }
1287
+ if (!Array.isArray(data)) return;
1288
+ for (const item of data) {
1289
+ if (!Array.isArray(item) || item.length < 3 || item[0] !== "wrb.fr") continue;
1290
+ const innerJson = item[2];
1291
+ if (typeof innerJson !== "string") continue;
1292
+ let innerData;
1293
+ try {
1294
+ innerData = JSON.parse(innerJson);
1295
+ } catch {
1296
+ continue;
1297
+ }
1298
+ if (!Array.isArray(innerData) || !innerData.length) continue;
1299
+ const first = innerData[0];
1300
+ if (!Array.isArray(first) || !first.length) continue;
1301
+ const answerText = first[0];
1302
+ if (typeof answerText !== "string" || !answerText) continue;
1303
+ const typeInfo = first[4];
1304
+ const isAnswer = Array.isArray(typeInfo) && typeInfo.length > 0 && typeInfo[typeInfo.length - 1] === 1;
1305
+ const convData = first[2];
1306
+ if (!serverConvId && Array.isArray(convData) && convData.length > 0 && typeof convData[0] === "string") {
1307
+ serverConvId = convData[0];
1308
+ }
1309
+ if (Array.isArray(typeInfo) && typeInfo.length > 3) {
1310
+ const citations = typeInfo[3];
1311
+ if (Array.isArray(citations)) {
1312
+ for (const cite of citations) {
1313
+ const sourceId = extractUuid(cite);
1314
+ if (sourceId) {
1315
+ references.push({ sourceId, title: null, url: null });
1316
+ }
1317
+ }
1318
+ }
1319
+ }
1320
+ if (isAnswer && answerText.length > bestMarkedAnswer.length) {
1321
+ bestMarkedAnswer = answerText;
1322
+ } else if (!isAnswer && answerText.length > bestUnmarkedAnswer.length) {
1323
+ bestUnmarkedAnswer = answerText;
1324
+ }
1325
+ }
1326
+ }
1327
+ let i = 0;
1328
+ while (i < lines.length) {
1329
+ const line = (lines[i] ?? "").trim();
1330
+ if (!line) {
1331
+ i++;
1332
+ continue;
1333
+ }
1334
+ if (/^\d+$/.test(line)) {
1335
+ i++;
1336
+ const next = lines[i];
1337
+ if (next !== void 0) processChunk(next);
1338
+ i++;
1339
+ } else {
1340
+ processChunk(line);
1341
+ i++;
1342
+ }
1343
+ }
1344
+ return {
1345
+ answer: bestMarkedAnswer || bestUnmarkedAnswer,
1346
+ conversationId: serverConvId,
1347
+ references
1348
+ };
1349
+ }
1350
+ function extractUuid(data, depth = 8) {
1351
+ if (depth <= 0 || data == null) return null;
1352
+ if (typeof data === "string") return UUID_RE.test(data) ? data : null;
1353
+ if (Array.isArray(data)) {
1354
+ for (const item of data) {
1355
+ const found = extractUuid(item, depth - 1);
1356
+ if (found) return found;
1357
+ }
1358
+ }
1359
+ return null;
1360
+ }
1361
+ function randomUUID() {
1362
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1363
+ return crypto.randomUUID();
1364
+ }
1365
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
1366
+ const r = Math.random() * 16 | 0;
1367
+ return (c === "x" ? r : r & 3 | 8).toString(16);
1368
+ });
1369
+ }
1370
+
1371
+ // src/api/notebooks.ts
1372
+ init_enums();
1373
+ var NotebooksAPI = class {
1374
+ constructor(rpc) {
1375
+ this.rpc = rpc;
1376
+ }
1377
+ async list() {
1378
+ const params = [null, 1, null, [2]];
1379
+ const result = await this.rpc.call(exports.RPCMethod.LIST_NOTEBOOKS, params);
1380
+ if (!Array.isArray(result) || !result.length) return [];
1381
+ const raw = Array.isArray(result[0]) ? result[0] : result;
1382
+ return raw.map((nb) => parseNotebook(nb));
1383
+ }
1384
+ async create(title) {
1385
+ const params = [title, null, null, [2], [1]];
1386
+ const result = await this.rpc.call(exports.RPCMethod.CREATE_NOTEBOOK, params);
1387
+ return parseNotebook(result);
1388
+ }
1389
+ async get(notebookId) {
1390
+ const params = [notebookId, null, [2], null, 0];
1391
+ const result = await this.rpc.call(exports.RPCMethod.GET_NOTEBOOK, params, {
1392
+ sourcePath: `/notebook/${notebookId}`
1393
+ });
1394
+ const data = Array.isArray(result) && result.length ? result[0] : result;
1395
+ return parseNotebook(data);
1396
+ }
1397
+ async delete(notebookId) {
1398
+ const params = [[notebookId], [2]];
1399
+ await this.rpc.call(exports.RPCMethod.DELETE_NOTEBOOK, params);
1400
+ return true;
1401
+ }
1402
+ async rename(notebookId, newTitle) {
1403
+ const params = [notebookId, [[null, null, null, [null, newTitle]]]];
1404
+ await this.rpc.call(exports.RPCMethod.RENAME_NOTEBOOK, params, {
1405
+ sourcePath: "/",
1406
+ allowNull: true
1407
+ });
1408
+ return this.get(notebookId);
1409
+ }
1410
+ async getSummary(notebookId) {
1411
+ const params = [notebookId, [2]];
1412
+ const result = await this.rpc.call(exports.RPCMethod.SUMMARIZE, params, {
1413
+ sourcePath: `/notebook/${notebookId}`
1414
+ });
1415
+ try {
1416
+ if (Array.isArray(result)) {
1417
+ const val = result[0]?.[0]?.[0];
1418
+ return typeof val === "string" ? val : "";
1419
+ }
1420
+ } catch {
1421
+ }
1422
+ return "";
1423
+ }
1424
+ async getDescription(notebookId) {
1425
+ const params = [notebookId, [2]];
1426
+ const result = await this.rpc.call(exports.RPCMethod.SUMMARIZE, params, {
1427
+ sourcePath: `/notebook/${notebookId}`
1428
+ });
1429
+ let summary = "";
1430
+ const suggestedTopics = [];
1431
+ try {
1432
+ if (Array.isArray(result)) {
1433
+ const outer = result[0];
1434
+ if (Array.isArray(outer)) {
1435
+ const summaryVal = outer[0]?.[0];
1436
+ if (typeof summaryVal === "string") summary = summaryVal;
1437
+ const topicsList = outer[1]?.[0];
1438
+ if (Array.isArray(topicsList)) {
1439
+ for (const t of topicsList) {
1440
+ const question = typeof t[0] === "string" ? t[0] : "";
1441
+ const prompt = typeof t[1] === "string" ? t[1] : "";
1442
+ suggestedTopics.push({ question, prompt });
1443
+ }
1444
+ }
1445
+ }
1446
+ }
1447
+ } catch {
1448
+ }
1449
+ return { summary, suggestedTopics };
1758
1450
  }
1759
- async getConversationTurns(notebookId, conversationId) {
1760
- const params = [[], null, null, conversationId, 100];
1761
- const result = await this.rpc.call(exports.RPCMethod.GET_CONVERSATION_TURNS, params, {
1762
- sourcePath: `/notebook/${notebookId}`,
1763
- allowNull: true
1451
+ };
1452
+
1453
+ // src/api/notes.ts
1454
+ init_enums();
1455
+ var NotesAPI = class {
1456
+ constructor(rpc) {
1457
+ this.rpc = rpc;
1458
+ }
1459
+ async list(notebookId) {
1460
+ const params = [notebookId, [2]];
1461
+ const result = await this.rpc.call(exports.RPCMethod.GET_NOTES_AND_MIND_MAPS, params, {
1462
+ sourcePath: `/notebook/${notebookId}`
1764
1463
  });
1765
- if (!Array.isArray(result) || !Array.isArray(result[0])) return [];
1766
- const rawTurns = [...result[0]].reverse();
1767
- const turns = [];
1768
- let i = 0;
1769
- while (i < rawTurns.length) {
1770
- const turn = rawTurns[i];
1771
- if (!Array.isArray(turn) || turn.length < 3) {
1772
- i++;
1773
- continue;
1464
+ const notes = [];
1465
+ const mindMaps = [];
1466
+ if (!Array.isArray(result)) return { notes, mindMaps };
1467
+ try {
1468
+ const notesData = result[0];
1469
+ if (Array.isArray(notesData)) {
1470
+ for (const n of notesData) {
1471
+ if (Array.isArray(n)) notes.push(parseNote(n));
1472
+ }
1774
1473
  }
1775
- if (turn[2] === 1 && turn.length > 3) {
1776
- const q = typeof turn[3] === "string" ? turn[3] : "";
1777
- let a = "";
1778
- const next = rawTurns[i + 1];
1779
- if (Array.isArray(next) && next.length > 4 && next[2] === 2) {
1780
- try {
1781
- a = String(next[4][0][0] ?? "");
1782
- } catch {
1474
+ const mapsData = result[1];
1475
+ if (Array.isArray(mapsData)) {
1476
+ for (const m of mapsData) {
1477
+ if (Array.isArray(m)) {
1478
+ mindMaps.push({
1479
+ id: typeof m[0] === "string" ? m[0] : "",
1480
+ title: typeof m[2] === "string" ? m[2] : null,
1481
+ content: typeof m[1] === "string" ? m[1] : "",
1482
+ createdAt: Array.isArray(m[3]) && typeof m[3][0] === "number" ? new Date(m[3][0] * 1e3) : null
1483
+ });
1783
1484
  }
1784
- i++;
1785
1485
  }
1786
- turns.push({ query: q, answer: a, turnNumber: turns.length + 1 });
1787
1486
  }
1788
- i++;
1487
+ } catch {
1789
1488
  }
1790
- return turns;
1489
+ return { notes, mindMaps };
1791
1490
  }
1792
- async getLastConversationId(notebookId) {
1793
- const params = [[], null, notebookId, 1];
1794
- const result = await this.rpc.call(exports.RPCMethod.GET_LAST_CONVERSATION_ID, params, {
1491
+ async create(notebookId, content, title) {
1492
+ const params = [notebookId, content, title ?? null, [2]];
1493
+ const result = await this.rpc.call(exports.RPCMethod.CREATE_NOTE, params, {
1494
+ sourcePath: `/notebook/${notebookId}`
1495
+ });
1496
+ if (Array.isArray(result)) return parseNote(result);
1497
+ throw new Error("Could not parse note creation response");
1498
+ }
1499
+ async update(notebookId, noteId, content, title) {
1500
+ const params = [notebookId, noteId, content, title ?? null, [2]];
1501
+ const result = await this.rpc.call(exports.RPCMethod.UPDATE_NOTE, params, {
1502
+ sourcePath: `/notebook/${notebookId}`
1503
+ });
1504
+ if (Array.isArray(result)) return parseNote(result);
1505
+ return { id: noteId, title: title ?? null, content, createdAt: null, updatedAt: /* @__PURE__ */ new Date() };
1506
+ }
1507
+ async delete(notebookId, noteId) {
1508
+ const params = [notebookId, noteId, [2]];
1509
+ await this.rpc.call(exports.RPCMethod.DELETE_NOTE, params, {
1795
1510
  sourcePath: `/notebook/${notebookId}`,
1796
1511
  allowNull: true
1797
1512
  });
1798
- if (!Array.isArray(result)) return null;
1799
- for (const group of result) {
1800
- if (!Array.isArray(group)) continue;
1801
- for (const conv of group) {
1802
- if (Array.isArray(conv) && typeof conv[0] === "string") {
1803
- return conv[0];
1804
- }
1805
- }
1806
- }
1807
- return null;
1513
+ return true;
1808
1514
  }
1809
- clearCache(conversationId) {
1810
- if (conversationId) {
1811
- this.conversationCache.delete(conversationId);
1515
+ };
1516
+
1517
+ // src/api/research.ts
1518
+ init_enums();
1519
+ var ResearchAPI = class {
1520
+ constructor(rpc) {
1521
+ this.rpc = rpc;
1522
+ }
1523
+ /**
1524
+ * Start a research session.
1525
+ * @param source "web" or "drive"
1526
+ * @param mode "fast" or "deep" (deep only available for web)
1527
+ */
1528
+ async start(notebookId, query, source = "web", mode = "fast") {
1529
+ if (mode === "deep" && source === "drive") {
1530
+ throw new Error("Deep research only supports web sources.");
1531
+ }
1532
+ const sourceType = source === "web" ? 1 : 2;
1533
+ let rpcId;
1534
+ let params;
1535
+ if (mode === "fast") {
1536
+ rpcId = exports.RPCMethod.START_FAST_RESEARCH;
1537
+ params = [[query, sourceType], null, 1, notebookId];
1812
1538
  } else {
1813
- this.conversationCache.clear();
1539
+ rpcId = exports.RPCMethod.START_DEEP_RESEARCH;
1540
+ params = [null, [1], [query, sourceType], 5, notebookId];
1814
1541
  }
1815
- }
1816
- _buildHistory(conversationId) {
1817
- const turns = this.conversationCache.get(conversationId) ?? [];
1818
- if (!turns.length) return null;
1819
- const history = [];
1820
- for (const turn of turns) {
1821
- history.push([turn.answer, null, 2]);
1822
- history.push([turn.query, null, 1]);
1542
+ const result = await this.rpc.call(rpcId, params, {
1543
+ sourcePath: `/notebook/${notebookId}`,
1544
+ allowNull: true
1545
+ });
1546
+ if (Array.isArray(result) && result.length > 0) {
1547
+ return {
1548
+ taskId: result[0],
1549
+ reportId: result.length > 1 ? result[1] : null,
1550
+ notebookId,
1551
+ query,
1552
+ mode
1553
+ };
1823
1554
  }
1824
- return history;
1555
+ return null;
1825
1556
  }
1826
- };
1827
- function parseStreamingResponse(rawText) {
1828
- let text = rawText;
1829
- if (text.startsWith(")]}'")) text = text.slice(4);
1830
- const lines = text.trim().split("\n");
1831
- let bestMarkedAnswer = "";
1832
- let bestUnmarkedAnswer = "";
1833
- let serverConvId = null;
1834
- const references = [];
1835
- function processChunk(jsonStr) {
1836
- let data;
1837
- try {
1838
- data = JSON.parse(jsonStr);
1839
- } catch {
1840
- return;
1557
+ /** Poll for current research results in a notebook. */
1558
+ async poll(notebookId) {
1559
+ const params = [null, null, notebookId];
1560
+ let result = await this.rpc.call(exports.RPCMethod.POLL_RESEARCH, params, {
1561
+ sourcePath: `/notebook/${notebookId}`,
1562
+ allowNull: true
1563
+ });
1564
+ if (!Array.isArray(result) || !result.length) {
1565
+ return emptyResult();
1841
1566
  }
1842
- if (!Array.isArray(data)) return;
1843
- for (const item of data) {
1844
- if (!Array.isArray(item) || item.length < 3 || item[0] !== "wrb.fr") continue;
1845
- const innerJson = item[2];
1846
- if (typeof innerJson !== "string") continue;
1847
- let innerData;
1848
- try {
1849
- innerData = JSON.parse(innerJson);
1850
- } catch {
1851
- continue;
1852
- }
1853
- if (!Array.isArray(innerData) || !innerData.length) continue;
1854
- const first = innerData[0];
1855
- if (!Array.isArray(first) || !first.length) continue;
1856
- const answerText = first[0];
1857
- if (typeof answerText !== "string" || !answerText) continue;
1858
- const typeInfo = first[4];
1859
- const isAnswer = Array.isArray(typeInfo) && typeInfo.length > 0 && typeInfo[typeInfo.length - 1] === 1;
1860
- const convData = first[2];
1861
- if (!serverConvId && Array.isArray(convData) && convData.length > 0 && typeof convData[0] === "string") {
1862
- serverConvId = convData[0];
1863
- }
1864
- if (Array.isArray(typeInfo) && typeInfo.length > 3) {
1865
- const citations = typeInfo[3];
1866
- if (Array.isArray(citations)) {
1867
- for (const cite of citations) {
1868
- const sourceId = extractUuid(cite);
1869
- if (sourceId) {
1870
- references.push({ sourceId, title: null, url: null });
1567
+ if (Array.isArray(result[0]) && Array.isArray(result[0][0])) {
1568
+ result = result[0];
1569
+ }
1570
+ const parsedTasks = [];
1571
+ for (const taskData of result) {
1572
+ if (!Array.isArray(taskData) || taskData.length < 2) continue;
1573
+ const taskId = taskData[0];
1574
+ const taskInfo = taskData[1];
1575
+ if (typeof taskId !== "string" || !Array.isArray(taskInfo)) continue;
1576
+ const queryText = Array.isArray(taskInfo[1]) ? taskInfo[1][0] ?? "" : "";
1577
+ const sourcesAndSummary = Array.isArray(taskInfo[3]) ? taskInfo[3] : [];
1578
+ const statusCode = typeof taskInfo[4] === "number" ? taskInfo[4] : null;
1579
+ const sourcesData = Array.isArray(sourcesAndSummary[0]) ? sourcesAndSummary[0] : [];
1580
+ const summary = typeof sourcesAndSummary[1] === "string" ? sourcesAndSummary[1] : "";
1581
+ const parsedSources = [];
1582
+ let report = "";
1583
+ for (const src of sourcesData) {
1584
+ if (!Array.isArray(src) || src.length < 2) continue;
1585
+ let url = "";
1586
+ let title = "";
1587
+ let sourceReport = "";
1588
+ let resultType = parseResultType(src.length > 3 ? src[3] : 1);
1589
+ if (src[0] === null) {
1590
+ if (Array.isArray(src[1]) && src[1].length >= 2 && typeof src[1][0] === "string" && typeof src[1][1] === "string") {
1591
+ title = src[1][0];
1592
+ sourceReport = src[1][1];
1593
+ if (resultType === 1) resultType = 5;
1594
+ } else if (typeof src[1] === "string") {
1595
+ title = src[1];
1596
+ if (resultType === 1) resultType = 5;
1597
+ }
1598
+ } else {
1599
+ url = typeof src[0] === "string" ? src[0] : "";
1600
+ title = src.length > 1 && typeof src[1] === "string" ? src[1] : "";
1601
+ }
1602
+ if (title || url) {
1603
+ const parsed = { url, title, resultType, researchTaskId: taskId };
1604
+ if (sourceReport) parsed.reportMarkdown = sourceReport;
1605
+ parsedSources.push(parsed);
1606
+ if (!report && sourceReport) {
1607
+ report = sourceReport;
1608
+ } else if (!report) {
1609
+ const legacyReport = extractLegacyReport(src);
1610
+ if (legacyReport) {
1611
+ report = legacyReport;
1612
+ parsed.reportMarkdown = legacyReport;
1871
1613
  }
1872
1614
  }
1873
1615
  }
1874
1616
  }
1875
- if (isAnswer && answerText.length > bestMarkedAnswer.length) {
1876
- bestMarkedAnswer = answerText;
1877
- } else if (!isAnswer && answerText.length > bestUnmarkedAnswer.length) {
1878
- bestUnmarkedAnswer = answerText;
1879
- }
1617
+ const status = statusCode === 2 || statusCode === 6 ? "completed" : "in_progress";
1618
+ parsedTasks.push({
1619
+ taskId,
1620
+ status,
1621
+ query: queryText,
1622
+ sources: parsedSources,
1623
+ summary,
1624
+ report,
1625
+ tasks: []
1626
+ });
1627
+ }
1628
+ if (parsedTasks.length > 0) {
1629
+ return { ...parsedTasks[0], tasks: parsedTasks };
1880
1630
  }
1631
+ return emptyResult();
1881
1632
  }
1882
- let i = 0;
1883
- while (i < lines.length) {
1884
- const line = (lines[i] ?? "").trim();
1885
- if (!line) {
1886
- i++;
1887
- continue;
1633
+ /**
1634
+ * Import selected research sources into the notebook.
1635
+ * Pass sources from poll() web sources need `url`, deep research report entries
1636
+ * need `reportMarkdown` (resultType=5).
1637
+ *
1638
+ * Note: The API may return fewer items than imported. Use sources.list() to verify.
1639
+ */
1640
+ async importSources(notebookId, taskId, sources) {
1641
+ if (!sources.length) return [];
1642
+ const taskIds = new Set(sources.map((s) => s.researchTaskId).filter(Boolean));
1643
+ if (taskIds.size > 1)
1644
+ throw new Error("Cannot import sources from multiple research tasks in one batch.");
1645
+ const effectiveTaskId = taskIds.size === 1 ? [...taskIds][0] : taskId;
1646
+ const reportSources = sources.filter((s) => s.resultType === 5 && s.title && s.reportMarkdown);
1647
+ const reportSourceSet = new Set(reportSources);
1648
+ const webSources = sources.filter((s) => s.url && !reportSourceSet.has(s));
1649
+ if (!webSources.length && !reportSources.length) return [];
1650
+ const sourceArray = [
1651
+ ...reportSources.map((s) => buildReportEntry(s.title, s.reportMarkdown)),
1652
+ ...webSources.map((s) => buildWebEntry(s.url, s.title))
1653
+ ];
1654
+ const params = [null, [1], effectiveTaskId, notebookId, sourceArray];
1655
+ let result = await this.rpc.call(exports.RPCMethod.IMPORT_RESEARCH, params, {
1656
+ sourcePath: `/notebook/${notebookId}`,
1657
+ allowNull: true
1658
+ });
1659
+ if (!Array.isArray(result)) return [];
1660
+ if (result.length > 0 && Array.isArray(result[0]) && Array.isArray(result[0][0])) {
1661
+ result = result[0];
1888
1662
  }
1889
- if (/^\d+$/.test(line)) {
1890
- i++;
1891
- const next = lines[i];
1892
- if (next !== void 0) processChunk(next);
1893
- i++;
1894
- } else {
1895
- processChunk(line);
1896
- i++;
1663
+ const imported = [];
1664
+ for (const srcData of result) {
1665
+ if (!Array.isArray(srcData) || srcData.length < 2) continue;
1666
+ const first = srcData[0];
1667
+ const srcId = Array.isArray(first) && first.length > 0 ? first[0] : null;
1668
+ if (srcId) imported.push({ id: srcId, title: srcData[1] });
1897
1669
  }
1670
+ return imported;
1898
1671
  }
1672
+ };
1673
+ function parseResultType(value) {
1674
+ if (typeof value === "number") return value;
1675
+ if (typeof value === "string") {
1676
+ const aliases = { web: 1, drive: 2, report: 5 };
1677
+ return aliases[value.toLowerCase()] ?? 1;
1678
+ }
1679
+ return 1;
1680
+ }
1681
+ function extractLegacyReport(src) {
1682
+ if (src.length <= 6 || !Array.isArray(src[6])) return "";
1683
+ return src[6].filter((c) => typeof c === "string" && !!c).join("\n\n");
1684
+ }
1685
+ function buildReportEntry(title, markdown) {
1686
+ return [null, [title, markdown], null, 3, null, null, null, null, null, null, 3];
1687
+ }
1688
+ function buildWebEntry(url, title) {
1689
+ return [null, null, [url, title], null, null, null, null, null, null, null, 2];
1690
+ }
1691
+ function emptyResult() {
1899
1692
  return {
1900
- answer: bestMarkedAnswer || bestUnmarkedAnswer,
1901
- conversationId: serverConvId,
1902
- references
1693
+ taskId: null,
1694
+ status: "no_research",
1695
+ query: "",
1696
+ sources: [],
1697
+ summary: "",
1698
+ report: "",
1699
+ tasks: []
1903
1700
  };
1904
1701
  }
1905
- function extractUuid(data, depth = 8) {
1906
- if (depth <= 0 || data == null) return null;
1907
- if (typeof data === "string") return UUID_RE.test(data) ? data : null;
1908
- if (Array.isArray(data)) {
1909
- for (const item of data) {
1910
- const found = extractUuid(item, depth - 1);
1911
- if (found) return found;
1912
- }
1702
+
1703
+ // src/api/settings.ts
1704
+ init_enums();
1705
+ var SettingsAPI = class {
1706
+ constructor(rpc) {
1707
+ this.rpc = rpc;
1913
1708
  }
1914
- return null;
1915
- }
1916
- function randomUUID() {
1917
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1918
- return crypto.randomUUID();
1709
+ /** Get the current output language setting (e.g. "en", "ja", "zh_Hans"). */
1710
+ async getOutputLanguage() {
1711
+ const params = [null, [1, null, null, null, null, null, null, null, null, null, [1]]];
1712
+ const result = await this.rpc.call(exports.RPCMethod.GET_USER_SETTINGS, params, {
1713
+ sourcePath: "/",
1714
+ allowNull: true
1715
+ });
1716
+ return extractNested(result, [0, 2, 4, 0]);
1717
+ }
1718
+ /**
1719
+ * Set the output language for artifact generation.
1720
+ * Pass a BCP-47 language code, e.g. "en", "ja", "zh_Hans".
1721
+ * Returns the language that was set, or null if the response couldn't be parsed.
1722
+ */
1723
+ async setOutputLanguage(language) {
1724
+ if (!language) return null;
1725
+ const params = [[[null, [[null, null, null, null, [language]]]]]];
1726
+ const result = await this.rpc.call(exports.RPCMethod.SET_USER_SETTINGS, params, {
1727
+ sourcePath: "/",
1728
+ allowNull: true
1729
+ });
1730
+ return extractNested(result, [2, 4, 0]);
1731
+ }
1732
+ };
1733
+ function extractNested(data, path) {
1734
+ try {
1735
+ let cur = data;
1736
+ for (const idx of path) {
1737
+ if (!Array.isArray(cur)) return null;
1738
+ cur = cur[idx];
1739
+ }
1740
+ return typeof cur === "string" && cur ? cur : null;
1741
+ } catch {
1742
+ return null;
1919
1743
  }
1920
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
1921
- const r = Math.random() * 16 | 0;
1922
- return (c === "x" ? r : r & 3 | 8).toString(16);
1923
- });
1924
1744
  }
1925
1745
 
1926
- // src/api/notes.ts
1746
+ // src/api/sharing.ts
1927
1747
  init_enums();
1928
- var NotesAPI = class {
1748
+ var SharingAPI = class {
1929
1749
  constructor(rpc) {
1930
1750
  this.rpc = rpc;
1931
1751
  }
1932
- async list(notebookId) {
1752
+ /** Get current sharing configuration for a notebook. */
1753
+ async getStatus(notebookId) {
1933
1754
  const params = [notebookId, [2]];
1934
- const result = await this.rpc.call(exports.RPCMethod.GET_NOTES_AND_MIND_MAPS, params, {
1935
- sourcePath: `/notebook/${notebookId}`
1755
+ const result = await this.rpc.call(exports.RPCMethod.GET_SHARE_STATUS, params, {
1756
+ sourcePath: `/notebook/${notebookId}`,
1757
+ allowNull: true
1936
1758
  });
1937
- const notes = [];
1938
- const mindMaps = [];
1939
- if (!Array.isArray(result)) return { notes, mindMaps };
1940
- try {
1941
- const notesData = result[0];
1942
- if (Array.isArray(notesData)) {
1943
- for (const n of notesData) {
1944
- if (Array.isArray(n)) notes.push(parseNote(n));
1945
- }
1946
- }
1947
- const mapsData = result[1];
1948
- if (Array.isArray(mapsData)) {
1949
- for (const m of mapsData) {
1950
- if (Array.isArray(m)) {
1951
- mindMaps.push({
1952
- id: typeof m[0] === "string" ? m[0] : "",
1953
- title: typeof m[2] === "string" ? m[2] : null,
1954
- content: typeof m[1] === "string" ? m[1] : "",
1955
- createdAt: Array.isArray(m[3]) && typeof m[3][0] === "number" ? new Date(m[3][0] * 1e3) : null
1956
- });
1957
- }
1958
- }
1959
- }
1960
- } catch {
1961
- }
1962
- return { notes, mindMaps };
1759
+ return parseShareStatus(result, notebookId);
1963
1760
  }
1964
- async create(notebookId, content, title) {
1965
- const params = [notebookId, content, title ?? null, [2]];
1966
- const result = await this.rpc.call(exports.RPCMethod.CREATE_NOTE, params, {
1967
- sourcePath: `/notebook/${notebookId}`
1761
+ /** Enable or disable public link sharing. Returns updated status. */
1762
+ async setPublic(notebookId, isPublic) {
1763
+ const access = isPublic ? exports.ShareAccess.ANYONE_WITH_LINK : exports.ShareAccess.RESTRICTED;
1764
+ const params = [[[notebookId, null, [access], [access, ""]]], 1, null, [2]];
1765
+ await this.rpc.call(exports.RPCMethod.SHARE_NOTEBOOK, params, {
1766
+ sourcePath: `/notebook/${notebookId}`,
1767
+ allowNull: true
1968
1768
  });
1969
- if (Array.isArray(result)) return parseNote(result);
1970
- throw new Error("Could not parse note creation response");
1769
+ return this.getStatus(notebookId);
1971
1770
  }
1972
- async update(notebookId, noteId, content, title) {
1973
- const params = [notebookId, noteId, content, title ?? null, [2]];
1974
- const result = await this.rpc.call(exports.RPCMethod.UPDATE_NOTE, params, {
1975
- sourcePath: `/notebook/${notebookId}`
1771
+ /**
1772
+ * Set what viewers can access: full notebook or chat only.
1773
+ * Note: GET_SHARE_STATUS doesn't return view_level, so it's inferred from what was set.
1774
+ */
1775
+ async setViewLevel(notebookId, level) {
1776
+ const params = [notebookId, [[null, null, null, null, null, null, null, null, [[level]]]]];
1777
+ await this.rpc.call(exports.RPCMethod.RENAME_NOTEBOOK, params, {
1778
+ sourcePath: `/notebook/${notebookId}`,
1779
+ allowNull: true
1976
1780
  });
1977
- if (Array.isArray(result)) return parseNote(result);
1978
- return { id: noteId, title: title ?? null, content, createdAt: null, updatedAt: /* @__PURE__ */ new Date() };
1781
+ const status = await this.getStatus(notebookId);
1782
+ return { ...status, viewLevel: level };
1783
+ }
1784
+ /** Share notebook with a user. Returns updated status. */
1785
+ async addUser(notebookId, email, permission = exports.SharePermission.VIEWER, opts = {}) {
1786
+ if (permission === exports.SharePermission.OWNER) throw new Error("Cannot assign OWNER permission");
1787
+ if (permission === exports.SharePermission._REMOVE) throw new Error("Use removeUser() instead");
1788
+ const { notify = true, welcomeMessage = "" } = opts;
1789
+ const messageFlag = welcomeMessage ? 0 : 1;
1790
+ const notifyFlag = notify ? 1 : 0;
1791
+ const params = [
1792
+ [[notebookId, [[email, null, permission]], null, [messageFlag, welcomeMessage]]],
1793
+ notifyFlag,
1794
+ null,
1795
+ [2]
1796
+ ];
1797
+ await this.rpc.call(exports.RPCMethod.SHARE_NOTEBOOK, params, {
1798
+ sourcePath: `/notebook/${notebookId}`,
1799
+ allowNull: true
1800
+ });
1801
+ return this.getStatus(notebookId);
1979
1802
  }
1980
- async delete(notebookId, noteId) {
1981
- const params = [notebookId, noteId, [2]];
1982
- await this.rpc.call(exports.RPCMethod.DELETE_NOTE, params, {
1803
+ /** Update an existing user's permission level. Returns updated status. */
1804
+ async updateUser(notebookId, email, permission) {
1805
+ return this.addUser(notebookId, email, permission, { notify: false });
1806
+ }
1807
+ /** Remove a user's access to the notebook. Returns updated status. */
1808
+ async removeUser(notebookId, email) {
1809
+ const params = [
1810
+ [[notebookId, [[email, null, exports.SharePermission._REMOVE]], null, [0, ""]]],
1811
+ 0,
1812
+ null,
1813
+ [2]
1814
+ ];
1815
+ await this.rpc.call(exports.RPCMethod.SHARE_NOTEBOOK, params, {
1983
1816
  sourcePath: `/notebook/${notebookId}`,
1984
1817
  allowNull: true
1985
1818
  });
1986
- return true;
1819
+ return this.getStatus(notebookId);
1987
1820
  }
1988
1821
  };
1822
+ var PERM_MAP = {
1823
+ 1: "owner",
1824
+ 2: "editor",
1825
+ 3: "viewer"
1826
+ };
1827
+ function parseSharedUser(data) {
1828
+ const email = typeof data[0] === "string" ? data[0] : "";
1829
+ const permCode = typeof data[1] === "number" ? data[1] : 3;
1830
+ const permission = PERM_MAP[permCode] ?? "viewer";
1831
+ let displayName = null;
1832
+ let avatarUrl = null;
1833
+ if (Array.isArray(data[3])) {
1834
+ const info = data[3];
1835
+ displayName = typeof info[0] === "string" ? info[0] : null;
1836
+ avatarUrl = typeof info[1] === "string" ? info[1] : null;
1837
+ }
1838
+ return { email, permission, displayName, avatarUrl };
1839
+ }
1840
+ function parseShareStatus(data, notebookId) {
1841
+ const users = [];
1842
+ if (Array.isArray(data[0])) {
1843
+ for (const entry of data[0]) {
1844
+ if (Array.isArray(entry)) users.push(parseSharedUser(entry));
1845
+ }
1846
+ }
1847
+ const isPublic = Array.isArray(data[1]) && data[1][0] === true;
1848
+ const access = isPublic ? exports.ShareAccess.ANYONE_WITH_LINK : exports.ShareAccess.RESTRICTED;
1849
+ const shareUrl = isPublic ? `https://notebooklm.google.com/notebook/${notebookId}` : null;
1850
+ return {
1851
+ notebookId,
1852
+ isPublic,
1853
+ access,
1854
+ viewLevel: exports.ShareViewLevel.FULL_NOTEBOOK,
1855
+ sharedUsers: users,
1856
+ shareUrl
1857
+ };
1858
+ }
1989
1859
 
1990
- // src/api/research.ts
1860
+ // src/api/sources.ts
1991
1861
  init_enums();
1992
- var ResearchAPI = class {
1993
- constructor(rpc) {
1862
+ init_errors();
1863
+ var UPLOAD_URL = "https://notebooklm.google.com/upload/_/";
1864
+ var SourcesAPI = class {
1865
+ constructor(rpc, auth) {
1994
1866
  this.rpc = rpc;
1867
+ this.auth = auth;
1995
1868
  }
1996
- /**
1997
- * Start a research session.
1998
- * @param source "web" or "drive"
1999
- * @param mode "fast" or "deep" (deep only available for web)
2000
- */
2001
- async start(notebookId, query, source = "web", mode = "fast") {
2002
- if (mode === "deep" && source === "drive") {
2003
- throw new Error("Deep research only supports web sources.");
2004
- }
2005
- const sourceType = source === "web" ? 1 : 2;
2006
- let rpcId;
1869
+ async list(notebookId) {
1870
+ const params = [notebookId, null, [2], null, 0];
1871
+ const notebook = await this.rpc.call(exports.RPCMethod.GET_NOTEBOOK, params, {
1872
+ sourcePath: `/notebook/${notebookId}`
1873
+ });
1874
+ if (!Array.isArray(notebook) || !notebook.length) return [];
1875
+ const nbInfo = notebook[0];
1876
+ if (!Array.isArray(nbInfo) || nbInfo.length <= 1) return [];
1877
+ const sourcesList = nbInfo[1];
1878
+ if (!Array.isArray(sourcesList)) return [];
1879
+ return sourcesList.filter((s) => Array.isArray(s) && s.length > 0).map((s) => parseSource(s));
1880
+ }
1881
+ async get(notebookId, sourceId) {
1882
+ const sources = await this.list(notebookId);
1883
+ return sources.find((s) => s.id === sourceId) ?? null;
1884
+ }
1885
+ async addUrl(notebookId, url, opts = {}) {
1886
+ const isYouTube = url.includes("youtube.com") || url.includes("youtu.be");
2007
1887
  let params;
2008
- if (mode === "fast") {
2009
- rpcId = exports.RPCMethod.START_FAST_RESEARCH;
2010
- params = [[query, sourceType], null, 1, notebookId];
1888
+ if (isYouTube) {
1889
+ params = [
1890
+ [[null, null, null, null, null, null, null, [url], null, null, 1]],
1891
+ notebookId,
1892
+ [2],
1893
+ [1, null, null, null, null, null, null, null, null, null, [1]]
1894
+ ];
2011
1895
  } else {
2012
- rpcId = exports.RPCMethod.START_DEEP_RESEARCH;
2013
- params = [null, [1], [query, sourceType], 5, notebookId];
1896
+ params = [[[null, null, [url], null, null, null, null, null]], notebookId, [2], null, null];
2014
1897
  }
2015
- const result = await this.rpc.call(rpcId, params, {
1898
+ const result = await this.rpc.call(exports.RPCMethod.ADD_SOURCE, params, {
2016
1899
  sourcePath: `/notebook/${notebookId}`,
2017
- allowNull: true
1900
+ allowNull: isYouTube
2018
1901
  });
2019
- if (Array.isArray(result) && result.length > 0) {
2020
- return {
2021
- taskId: result[0],
2022
- reportId: result.length > 1 ? result[1] : null,
2023
- notebookId,
2024
- query,
2025
- mode
2026
- };
1902
+ const sourceId = extractSourceId(result);
1903
+ if (opts.waitUntilReady) {
1904
+ return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
2027
1905
  }
2028
- return null;
1906
+ return {
1907
+ id: sourceId,
1908
+ title: url,
1909
+ url,
1910
+ kind: "web_page",
1911
+ createdAt: null,
1912
+ status: "processing",
1913
+ _typeCode: null
1914
+ };
2029
1915
  }
2030
- /** Poll for current research results in a notebook. */
2031
- async poll(notebookId) {
2032
- const params = [null, null, notebookId];
2033
- let result = await this.rpc.call(exports.RPCMethod.POLL_RESEARCH, params, {
1916
+ async addText(notebookId, text, title, opts = {}) {
1917
+ const params = [
1918
+ [[null, [title ?? "", text], null, null, null, null, null, null]],
1919
+ notebookId,
1920
+ [2],
1921
+ null,
1922
+ null
1923
+ ];
1924
+ const result = await this.rpc.call(exports.RPCMethod.ADD_SOURCE, params, {
1925
+ sourcePath: `/notebook/${notebookId}`
1926
+ });
1927
+ const sourceId = extractSourceId(result);
1928
+ if (opts.waitUntilReady) {
1929
+ return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
1930
+ }
1931
+ return {
1932
+ id: sourceId,
1933
+ title: title ?? null,
1934
+ url: null,
1935
+ kind: "pasted_text",
1936
+ createdAt: null,
1937
+ status: "processing",
1938
+ _typeCode: null
1939
+ };
1940
+ }
1941
+ async addFile(notebookId, filePath, mimeType, opts = {}) {
1942
+ const fileData = fs.readFileSync(filePath);
1943
+ const fileName = filePath.split("/").pop() ?? "file";
1944
+ return this.addFileBuffer(notebookId, fileData, fileName, mimeType, opts);
1945
+ }
1946
+ async addFileBuffer(notebookId, data, fileName, _mimeType, opts = {}) {
1947
+ const params = [
1948
+ [[fileName]],
1949
+ notebookId,
1950
+ [2],
1951
+ [1, null, null, null, null, null, null, null, null, null, [1]]
1952
+ ];
1953
+ const result = await this.rpc.call(exports.RPCMethod.ADD_SOURCE_FILE, params, {
2034
1954
  sourcePath: `/notebook/${notebookId}`,
2035
1955
  allowNull: true
2036
1956
  });
2037
- if (!Array.isArray(result) || !result.length) {
2038
- return emptyResult();
1957
+ const sourceId = extractSourceId(result);
1958
+ const uploadUrl = await this.startResumableUpload(notebookId, fileName, data.length, sourceId);
1959
+ await this.uploadFile(uploadUrl, data);
1960
+ if (opts.waitUntilReady) {
1961
+ return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
2039
1962
  }
2040
- if (Array.isArray(result[0]) && Array.isArray(result[0][0])) {
2041
- result = result[0];
1963
+ return {
1964
+ id: sourceId,
1965
+ title: fileName,
1966
+ url: null,
1967
+ kind: "pdf",
1968
+ // Defaults to generic kind until ready
1969
+ createdAt: null,
1970
+ status: "processing",
1971
+ _typeCode: null
1972
+ };
1973
+ }
1974
+ async startResumableUpload(notebookId, fileName, fileSize, sourceId) {
1975
+ const startResp = await fetch(`${UPLOAD_URL}?authuser=0`, {
1976
+ method: "POST",
1977
+ headers: {
1978
+ Accept: "*/*",
1979
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
1980
+ Cookie: this.auth.cookieHeader,
1981
+ Origin: "https://notebooklm.google.com",
1982
+ Referer: "https://notebooklm.google.com/",
1983
+ "x-goog-authuser": "0",
1984
+ "x-goog-upload-command": "start",
1985
+ "x-goog-upload-header-content-length": String(fileSize),
1986
+ "x-goog-upload-protocol": "resumable"
1987
+ },
1988
+ body: JSON.stringify({
1989
+ PROJECT_ID: notebookId,
1990
+ SOURCE_NAME: fileName,
1991
+ SOURCE_ID: sourceId
1992
+ })
1993
+ });
1994
+ if (!startResp.ok) {
1995
+ throw new Error(`Upload initiation failed: HTTP ${startResp.status}`);
2042
1996
  }
2043
- const parsedTasks = [];
2044
- for (const taskData of result) {
2045
- if (!Array.isArray(taskData) || taskData.length < 2) continue;
2046
- const taskId = taskData[0];
2047
- const taskInfo = taskData[1];
2048
- if (typeof taskId !== "string" || !Array.isArray(taskInfo)) continue;
2049
- const queryText = Array.isArray(taskInfo[1]) ? taskInfo[1][0] ?? "" : "";
2050
- const sourcesAndSummary = Array.isArray(taskInfo[3]) ? taskInfo[3] : [];
2051
- const statusCode = typeof taskInfo[4] === "number" ? taskInfo[4] : null;
2052
- const sourcesData = Array.isArray(sourcesAndSummary[0]) ? sourcesAndSummary[0] : [];
2053
- const summary = typeof sourcesAndSummary[1] === "string" ? sourcesAndSummary[1] : "";
2054
- const parsedSources = [];
2055
- let report = "";
2056
- for (const src of sourcesData) {
2057
- if (!Array.isArray(src) || src.length < 2) continue;
2058
- let url = "";
2059
- let title = "";
2060
- let sourceReport = "";
2061
- let resultType = parseResultType(src.length > 3 ? src[3] : 1);
2062
- if (src[0] === null) {
2063
- if (Array.isArray(src[1]) && src[1].length >= 2 && typeof src[1][0] === "string" && typeof src[1][1] === "string") {
2064
- title = src[1][0];
2065
- sourceReport = src[1][1];
2066
- if (resultType === 1) resultType = 5;
2067
- } else if (typeof src[1] === "string") {
2068
- title = src[1];
2069
- if (resultType === 1) resultType = 5;
2070
- }
2071
- } else {
2072
- url = typeof src[0] === "string" ? src[0] : "";
2073
- title = src.length > 1 && typeof src[1] === "string" ? src[1] : "";
2074
- }
2075
- if (title || url) {
2076
- const parsed = { url, title, resultType, researchTaskId: taskId };
2077
- if (sourceReport) parsed.reportMarkdown = sourceReport;
2078
- parsedSources.push(parsed);
2079
- if (!report && sourceReport) {
2080
- report = sourceReport;
2081
- } else if (!report) {
2082
- const legacyReport = extractLegacyReport(src);
2083
- if (legacyReport) {
2084
- report = legacyReport;
2085
- parsed.reportMarkdown = legacyReport;
2086
- }
2087
- }
2088
- }
2089
- }
2090
- const status = statusCode === 2 || statusCode === 6 ? "completed" : "in_progress";
2091
- parsedTasks.push({
2092
- taskId,
2093
- status,
2094
- query: queryText,
2095
- sources: parsedSources,
2096
- summary,
2097
- report,
2098
- tasks: []
2099
- });
1997
+ const uploadSessionUrl = startResp.headers.get("x-goog-upload-url");
1998
+ if (!uploadSessionUrl) {
1999
+ throw new Error("Failed to get upload URL from response headers");
2100
2000
  }
2101
- if (parsedTasks.length > 0) {
2102
- return { ...parsedTasks[0], tasks: parsedTasks };
2001
+ return uploadSessionUrl;
2002
+ }
2003
+ async uploadFile(uploadUrl, data) {
2004
+ const uploadResp = await fetch(uploadUrl, {
2005
+ method: "POST",
2006
+ headers: {
2007
+ Accept: "*/*",
2008
+ "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
2009
+ Cookie: this.auth.cookieHeader,
2010
+ Origin: "https://notebooklm.google.com",
2011
+ Referer: "https://notebooklm.google.com/",
2012
+ "X-Goog-Upload-Command": "upload, finalize",
2013
+ "X-Goog-Upload-Offset": "0"
2014
+ },
2015
+ body: new Uint8Array(data)
2016
+ });
2017
+ if (!uploadResp.ok) {
2018
+ throw new Error(`File upload failed: HTTP ${uploadResp.status}`);
2103
2019
  }
2104
- return emptyResult();
2020
+ const uploadResult = await uploadResp.text();
2021
+ return uploadResult.trim();
2022
+ }
2023
+ async delete(notebookId, sourceId) {
2024
+ const params = [notebookId, [sourceId], [2]];
2025
+ await this.rpc.call(exports.RPCMethod.DELETE_SOURCE, params, {
2026
+ sourcePath: `/notebook/${notebookId}`,
2027
+ allowNull: true
2028
+ });
2029
+ return true;
2105
2030
  }
2106
- /**
2107
- * Import selected research sources into the notebook.
2108
- * Pass sources from poll() web sources need `url`, deep research report entries
2109
- * need `reportMarkdown` (resultType=5).
2110
- *
2111
- * Note: The API may return fewer items than imported. Use sources.list() to verify.
2112
- */
2113
- async importSources(notebookId, taskId, sources) {
2114
- if (!sources.length) return [];
2115
- const taskIds = new Set(sources.map((s) => s.researchTaskId).filter(Boolean));
2116
- if (taskIds.size > 1)
2117
- throw new Error("Cannot import sources from multiple research tasks in one batch.");
2118
- const effectiveTaskId = taskIds.size === 1 ? [...taskIds][0] : taskId;
2119
- const reportSources = sources.filter((s) => s.resultType === 5 && s.title && s.reportMarkdown);
2120
- const reportSourceSet = new Set(reportSources);
2121
- const webSources = sources.filter((s) => s.url && !reportSourceSet.has(s));
2122
- if (!webSources.length && !reportSources.length) return [];
2123
- const sourceArray = [
2124
- ...reportSources.map((s) => buildReportEntry(s.title, s.reportMarkdown)),
2125
- ...webSources.map((s) => buildWebEntry(s.url, s.title))
2126
- ];
2127
- const params = [null, [1], effectiveTaskId, notebookId, sourceArray];
2128
- let result = await this.rpc.call(exports.RPCMethod.IMPORT_RESEARCH, params, {
2031
+ async refresh(notebookId, sourceId) {
2032
+ const params = [notebookId, sourceId, [2]];
2033
+ await this.rpc.call(exports.RPCMethod.REFRESH_SOURCE, params, {
2129
2034
  sourcePath: `/notebook/${notebookId}`,
2130
2035
  allowNull: true
2131
2036
  });
2132
- if (!Array.isArray(result)) return [];
2133
- if (result.length > 0 && Array.isArray(result[0]) && Array.isArray(result[0][0])) {
2134
- result = result[0];
2135
- }
2136
- const imported = [];
2137
- for (const srcData of result) {
2138
- if (!Array.isArray(srcData) || srcData.length < 2) continue;
2139
- const first = srcData[0];
2140
- const srcId = Array.isArray(first) && first.length > 0 ? first[0] : null;
2141
- if (srcId) imported.push({ id: srcId, title: srcData[1] });
2037
+ return true;
2038
+ }
2039
+ async waitUntilReady(notebookId, sourceId, timeout = 120, initialInterval = 1, maxInterval = 10, backoffFactor = 1.5) {
2040
+ const deadline = Date.now() + timeout * 1e3;
2041
+ let interval = initialInterval;
2042
+ let lastStatus;
2043
+ while (Date.now() < deadline) {
2044
+ const source = await this.get(notebookId, sourceId);
2045
+ if (source) {
2046
+ if (source.status === "ready") return source;
2047
+ if (source.status === "error") {
2048
+ throw new exports.SourceProcessingError(sourceId, 3);
2049
+ }
2050
+ lastStatus = source._typeCode ?? void 0;
2051
+ }
2052
+ await sleep2(interval * 1e3);
2053
+ interval = Math.min(interval * backoffFactor, maxInterval);
2142
2054
  }
2143
- return imported;
2055
+ throw new exports.SourceTimeoutError(sourceId, timeout, lastStatus);
2144
2056
  }
2145
2057
  };
2146
- function parseResultType(value) {
2147
- if (typeof value === "number") return value;
2148
- if (typeof value === "string") {
2149
- const aliases = { web: 1, drive: 2, report: 5 };
2150
- return aliases[value.toLowerCase()] ?? 1;
2058
+ function extractSourceId(result) {
2059
+ if (Array.isArray(result)) {
2060
+ let current = result;
2061
+ while (Array.isArray(current) && current.length > 0) {
2062
+ if (typeof current[0] === "string") {
2063
+ if (current[0].length > 8) {
2064
+ return current[0];
2065
+ }
2066
+ }
2067
+ current = current[0];
2068
+ }
2069
+ for (const item of result) {
2070
+ if (typeof item === "string" && item.length > 8) return item;
2071
+ }
2151
2072
  }
2152
- return 1;
2073
+ if (typeof result === "string") return result;
2074
+ console.log("extractSourceId debug info: could not parse:", JSON.stringify(result, null, 2));
2075
+ throw new Error("Could not extract source ID from API response");
2153
2076
  }
2154
- function extractLegacyReport(src) {
2155
- if (src.length <= 6 || !Array.isArray(src[6])) return "";
2156
- return src[6].filter((c) => typeof c === "string" && !!c).join("\n\n");
2077
+ function sleep2(ms) {
2078
+ return new Promise((resolve) => setTimeout(resolve, ms));
2157
2079
  }
2158
- function buildReportEntry(title, markdown) {
2159
- return [null, [title, markdown], null, 3, null, null, null, null, null, null, 3];
2080
+
2081
+ // src/index.ts
2082
+ init_auth();
2083
+
2084
+ // src/client.ts
2085
+ init_auth();
2086
+
2087
+ // src/rpc/core.ts
2088
+ init_errors();
2089
+
2090
+ // src/rpc/decoder.ts
2091
+ init_errors();
2092
+ function stripAntiXSSI(response) {
2093
+ if (response.startsWith(")]}'")) {
2094
+ const match = /\)\]\}'\r?\n/.exec(response);
2095
+ if (match) return response.slice(match[0].length);
2096
+ }
2097
+ return response;
2160
2098
  }
2161
- function buildWebEntry(url, title) {
2162
- return [null, null, [url, title], null, null, null, null, null, null, null, 2];
2099
+ function parseChunkedResponse(response) {
2100
+ if (!response || !response.trim()) return [];
2101
+ const chunks = [];
2102
+ let skippedCount = 0;
2103
+ const lines = response.trim().split("\n");
2104
+ let i = 0;
2105
+ while (i < lines.length) {
2106
+ const line = (lines[i] ?? "").trim();
2107
+ if (!line) {
2108
+ i++;
2109
+ continue;
2110
+ }
2111
+ if (/^\d+$/.test(line)) {
2112
+ i++;
2113
+ if (i < lines.length) {
2114
+ try {
2115
+ const chunk = JSON.parse(lines[i] ?? "");
2116
+ chunks.push(chunk);
2117
+ } catch {
2118
+ skippedCount++;
2119
+ }
2120
+ }
2121
+ i++;
2122
+ } else {
2123
+ try {
2124
+ const chunk = JSON.parse(line);
2125
+ chunks.push(chunk);
2126
+ } catch {
2127
+ skippedCount++;
2128
+ }
2129
+ i++;
2130
+ }
2131
+ }
2132
+ if (skippedCount > 0 && lines.length > 0) {
2133
+ const errorRate = skippedCount / lines.length;
2134
+ if (errorRate > 0.1) {
2135
+ throw new exports.RPCError(
2136
+ `Response parsing failed: ${skippedCount} of ${lines.length} chunks malformed`,
2137
+ { rawResponse: response.slice(0, 500) }
2138
+ );
2139
+ }
2140
+ }
2141
+ return chunks;
2163
2142
  }
2164
- function emptyResult() {
2165
- return {
2166
- taskId: null,
2167
- status: "no_research",
2168
- query: "",
2169
- sources: [],
2170
- summary: "",
2171
- report: "",
2172
- tasks: []
2173
- };
2143
+ function containsUserDisplayableError(obj) {
2144
+ if (typeof obj === "string") return obj.includes("UserDisplayableError");
2145
+ if (Array.isArray(obj)) return obj.some(containsUserDisplayableError);
2146
+ if (obj !== null && typeof obj === "object") {
2147
+ return Object.values(obj).some(containsUserDisplayableError);
2148
+ }
2149
+ return false;
2174
2150
  }
2175
-
2176
- // src/api/settings.ts
2177
- init_enums();
2178
- var SettingsAPI = class {
2179
- constructor(rpc) {
2180
- this.rpc = rpc;
2151
+ function collectRPCIds(chunks) {
2152
+ const ids = [];
2153
+ for (const chunk of chunks) {
2154
+ if (!Array.isArray(chunk)) continue;
2155
+ const items = Array.isArray(chunk[0]) ? chunk : [chunk];
2156
+ for (const item of items) {
2157
+ if (!Array.isArray(item) || item.length < 2) continue;
2158
+ if ((item[0] === "wrb.fr" || item[0] === "er") && typeof item[1] === "string") {
2159
+ ids.push(item[1]);
2160
+ }
2161
+ }
2181
2162
  }
2182
- /** Get the current output language setting (e.g. "en", "ja", "zh_Hans"). */
2183
- async getOutputLanguage() {
2184
- const params = [null, [1, null, null, null, null, null, null, null, null, null, [1]]];
2185
- const result = await this.rpc.call(exports.RPCMethod.GET_USER_SETTINGS, params, {
2186
- sourcePath: "/",
2187
- allowNull: true
2188
- });
2189
- return extractNested(result, [0, 2, 4, 0]);
2163
+ return ids;
2164
+ }
2165
+ function extractRPCResult(chunks, rpcId) {
2166
+ for (const chunk of chunks) {
2167
+ if (!Array.isArray(chunk)) continue;
2168
+ const items = Array.isArray(chunk[0]) ? chunk : [chunk];
2169
+ for (const item of items) {
2170
+ if (!Array.isArray(item) || item.length < 3) continue;
2171
+ if (item[0] === "er" && item[1] === rpcId) {
2172
+ const code = item[2];
2173
+ let msg = "Unknown error";
2174
+ if (typeof code === "number") {
2175
+ if (code === 429) msg = "API rate limit exceeded. Please wait before retrying.";
2176
+ else if (code === 401 || code === 403) msg = "Authentication required or forbidden.";
2177
+ else if (code === 404) msg = "Resource not found.";
2178
+ else if (code >= 500) msg = `Server error ${code}. Try again later.`;
2179
+ else msg = `Error code: ${code}`;
2180
+ } else if (typeof code === "string") {
2181
+ msg = code;
2182
+ }
2183
+ throw new exports.RPCError(msg, { methodId: rpcId, rpcCode: code });
2184
+ }
2185
+ if (item[0] === "wrb.fr" && item[1] === rpcId) {
2186
+ const resultData = item[2];
2187
+ if (resultData === null && item.length > 5 && item[5] != null) {
2188
+ if (containsUserDisplayableError(item[5])) {
2189
+ throw new exports.RateLimitError(
2190
+ "API rate limit or quota exceeded. Please wait before retrying.",
2191
+ { methodId: rpcId, rpcCode: "USER_DISPLAYABLE_ERROR" }
2192
+ );
2193
+ }
2194
+ }
2195
+ if (typeof resultData === "string") {
2196
+ try {
2197
+ return JSON.parse(resultData);
2198
+ } catch {
2199
+ return resultData;
2200
+ }
2201
+ }
2202
+ if (resultData === null) {
2203
+ console.log(
2204
+ "decodeResponse debug info: resultData is exactly null. Full item Array:",
2205
+ JSON.stringify(item)
2206
+ );
2207
+ }
2208
+ return resultData;
2209
+ }
2210
+ }
2190
2211
  }
2191
- /**
2192
- * Set the output language for artifact generation.
2193
- * Pass a BCP-47 language code, e.g. "en", "ja", "zh_Hans".
2194
- * Returns the language that was set, or null if the response couldn't be parsed.
2195
- */
2196
- async setOutputLanguage(language) {
2197
- if (!language) return null;
2198
- const params = [[[null, [[null, null, null, null, [language]]]]]];
2199
- const result = await this.rpc.call(exports.RPCMethod.SET_USER_SETTINGS, params, {
2200
- sourcePath: "/",
2201
- allowNull: true
2202
- });
2203
- return extractNested(result, [2, 4, 0]);
2212
+ return void 0;
2213
+ }
2214
+ function decodeResponse(rawResponse, rpcId, allowNull = false) {
2215
+ const cleaned = stripAntiXSSI(rawResponse);
2216
+ const chunks = parseChunkedResponse(cleaned);
2217
+ const responsePreview = cleaned.slice(0, 500);
2218
+ const foundIds = collectRPCIds(chunks);
2219
+ let result;
2220
+ try {
2221
+ result = extractRPCResult(chunks, rpcId);
2222
+ } catch (e) {
2223
+ if (e instanceof exports.RPCError && e.foundIds.length === 0) {
2224
+ throw new exports.RPCError(e.message, {
2225
+ methodId: e.methodId,
2226
+ rpcCode: e.rpcCode,
2227
+ foundIds,
2228
+ rawResponse: responsePreview
2229
+ });
2230
+ }
2231
+ throw e;
2204
2232
  }
2205
- };
2206
- function extractNested(data, path) {
2207
- try {
2208
- let cur = data;
2209
- for (const idx of path) {
2210
- if (!Array.isArray(cur)) return null;
2211
- cur = cur[idx];
2233
+ if (result === void 0 && !allowNull) {
2234
+ if (foundIds.length > 0 && !foundIds.includes(rpcId)) {
2235
+ throw new exports.RPCError(
2236
+ `No result for RPC ID '${rpcId}'. Response has IDs: ${foundIds.join(", ")}. Method ID may have changed.`,
2237
+ { methodId: rpcId, foundIds, rawResponse: responsePreview }
2238
+ );
2212
2239
  }
2213
- return typeof cur === "string" && cur ? cur : null;
2214
- } catch {
2215
- return null;
2240
+ throw new exports.RPCError(`No result found for RPC ID: ${rpcId} (${chunks.length} chunks parsed)`, {
2241
+ methodId: rpcId,
2242
+ foundIds,
2243
+ rawResponse: responsePreview
2244
+ });
2216
2245
  }
2246
+ return result ?? null;
2217
2247
  }
2218
2248
 
2219
- // src/api/sharing.ts
2220
- init_enums();
2221
- var SharingAPI = class {
2222
- constructor(rpc) {
2223
- this.rpc = rpc;
2224
- }
2225
- /** Get current sharing configuration for a notebook. */
2226
- async getStatus(notebookId) {
2227
- const params = [notebookId, [2]];
2228
- const result = await this.rpc.call(exports.RPCMethod.GET_SHARE_STATUS, params, {
2229
- sourcePath: `/notebook/${notebookId}`,
2230
- allowNull: true
2231
- });
2232
- return parseShareStatus(result, notebookId);
2233
- }
2234
- /** Enable or disable public link sharing. Returns updated status. */
2235
- async setPublic(notebookId, isPublic) {
2236
- const access = isPublic ? exports.ShareAccess.ANYONE_WITH_LINK : exports.ShareAccess.RESTRICTED;
2237
- const params = [[[notebookId, null, [access], [access, ""]]], 1, null, [2]];
2238
- await this.rpc.call(exports.RPCMethod.SHARE_NOTEBOOK, params, {
2239
- sourcePath: `/notebook/${notebookId}`,
2240
- allowNull: true
2241
- });
2242
- return this.getStatus(notebookId);
2243
- }
2244
- /**
2245
- * Set what viewers can access: full notebook or chat only.
2246
- * Note: GET_SHARE_STATUS doesn't return view_level, so it's inferred from what was set.
2247
- */
2248
- async setViewLevel(notebookId, level) {
2249
- const params = [notebookId, [[null, null, null, null, null, null, null, null, [[level]]]]];
2250
- await this.rpc.call(exports.RPCMethod.RENAME_NOTEBOOK, params, {
2251
- sourcePath: `/notebook/${notebookId}`,
2252
- allowNull: true
2253
- });
2254
- const status = await this.getStatus(notebookId);
2255
- return { ...status, viewLevel: level };
2256
- }
2257
- /** Share notebook with a user. Returns updated status. */
2258
- async addUser(notebookId, email, permission = exports.SharePermission.VIEWER, opts = {}) {
2259
- if (permission === exports.SharePermission.OWNER) throw new Error("Cannot assign OWNER permission");
2260
- if (permission === exports.SharePermission._REMOVE) throw new Error("Use removeUser() instead");
2261
- const { notify = true, welcomeMessage = "" } = opts;
2262
- const messageFlag = welcomeMessage ? 0 : 1;
2263
- const notifyFlag = notify ? 1 : 0;
2264
- const params = [
2265
- [[notebookId, [[email, null, permission]], null, [messageFlag, welcomeMessage]]],
2266
- notifyFlag,
2267
- null,
2268
- [2]
2269
- ];
2270
- await this.rpc.call(exports.RPCMethod.SHARE_NOTEBOOK, params, {
2271
- sourcePath: `/notebook/${notebookId}`,
2272
- allowNull: true
2273
- });
2274
- return this.getStatus(notebookId);
2249
+ // src/rpc/encoder.ts
2250
+ function encodeRPCRequest(methodId, params) {
2251
+ const paramsJson = JSON.stringify(params);
2252
+ return [[[methodId, paramsJson, null, "generic"]]];
2253
+ }
2254
+ function buildRequestBody(rpcRequest, csrfToken) {
2255
+ const fReq = encodeURIComponent(JSON.stringify(rpcRequest));
2256
+ const at = encodeURIComponent(csrfToken);
2257
+ return `f.req=${fReq}&at=${at}&`;
2258
+ }
2259
+ function buildUrlParams(methodId, sessionId, sourcePath = "/") {
2260
+ return new URLSearchParams({
2261
+ rpcids: methodId,
2262
+ "source-path": sourcePath,
2263
+ "f.sid": sessionId,
2264
+ hl: "en",
2265
+ rt: "c"
2266
+ });
2267
+ }
2268
+
2269
+ // src/rpc/core.ts
2270
+ var BATCHEXECUTE_URL = "https://notebooklm.google.com/_/LabsTailwindUi/data/batchexecute";
2271
+ var DEFAULT_TIMEOUT_MS = 3e4;
2272
+ var RPCCore = class {
2273
+ auth;
2274
+ timeoutMs;
2275
+ constructor(auth, timeoutMs = DEFAULT_TIMEOUT_MS) {
2276
+ this.auth = auth;
2277
+ this.timeoutMs = timeoutMs;
2275
2278
  }
2276
- /** Update an existing user's permission level. Returns updated status. */
2277
- async updateUser(notebookId, email, permission) {
2278
- return this.addUser(notebookId, email, permission, { notify: false });
2279
+ async call(methodId, params, opts = {}) {
2280
+ const sourcePath = opts.sourcePath ?? "/";
2281
+ const allowNull = opts.allowNull ?? false;
2282
+ const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
2283
+ const rpcRequest = encodeRPCRequest(methodId, params);
2284
+ const body = buildRequestBody(rpcRequest, this.auth.csrfToken);
2285
+ const urlParams = buildUrlParams(methodId, this.auth.sessionId, sourcePath);
2286
+ const url = `${BATCHEXECUTE_URL}?${urlParams.toString()}`;
2287
+ const controller = new AbortController();
2288
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
2289
+ let response;
2290
+ try {
2291
+ response = await fetch(url, {
2292
+ method: "POST",
2293
+ headers: {
2294
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
2295
+ Cookie: this.auth.cookieHeader
2296
+ },
2297
+ body,
2298
+ signal: controller.signal
2299
+ });
2300
+ } catch (e) {
2301
+ clearTimeout(timer);
2302
+ if (e instanceof Error && e.name === "AbortError") {
2303
+ throw new exports.RPCTimeoutError(`Request timed out calling ${methodId}`, {
2304
+ methodId,
2305
+ originalError: e
2306
+ });
2307
+ }
2308
+ throw new exports.NetworkError(`Request failed calling ${methodId}: ${String(e)}`, {
2309
+ methodId,
2310
+ originalError: e instanceof Error ? e : void 0
2311
+ });
2312
+ } finally {
2313
+ clearTimeout(timer);
2314
+ }
2315
+ if (!response.ok) {
2316
+ const status = response.status;
2317
+ if (status === 429) {
2318
+ const retryAfterHeader = response.headers.get("retry-after");
2319
+ const retryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : void 0;
2320
+ throw new exports.RateLimitError(`API rate limit exceeded calling ${methodId}`, {
2321
+ methodId,
2322
+ retryAfter: isNaN(retryAfter ?? NaN) ? void 0 : retryAfter
2323
+ });
2324
+ }
2325
+ if (status === 401 || status === 403) {
2326
+ throw new exports.AuthError(`HTTP ${status} calling ${methodId}: authentication required`, {
2327
+ methodId
2328
+ });
2329
+ }
2330
+ if (status >= 500) {
2331
+ throw new exports.ServerError(`Server error ${status} calling ${methodId}`, {
2332
+ methodId,
2333
+ statusCode: status
2334
+ });
2335
+ }
2336
+ if (status >= 400) {
2337
+ throw new exports.ClientError(`Client error ${status} calling ${methodId}`, {
2338
+ methodId,
2339
+ statusCode: status
2340
+ });
2341
+ }
2342
+ throw new exports.RPCError(`HTTP ${status} calling ${methodId}`, { methodId });
2343
+ }
2344
+ const text = await response.text();
2345
+ return decodeResponse(text, methodId, allowNull);
2279
2346
  }
2280
- /** Remove a user's access to the notebook. Returns updated status. */
2281
- async removeUser(notebookId, email) {
2282
- const params = [
2283
- [[notebookId, [[email, null, exports.SharePermission._REMOVE]], null, [0, ""]]],
2284
- 0,
2285
- null,
2286
- [2]
2287
- ];
2288
- await this.rpc.call(exports.RPCMethod.SHARE_NOTEBOOK, params, {
2289
- sourcePath: `/notebook/${notebookId}`,
2290
- allowNull: true
2347
+ /** Extract source IDs from a notebook (used by chat/artifact APIs). */
2348
+ async getSourceIds(notebookId) {
2349
+ const params = [notebookId, null, [2], null, 0];
2350
+ const { RPCMethod: RPCMethod2 } = await Promise.resolve().then(() => (init_enums(), enums_exports));
2351
+ const data = await this.call(RPCMethod2.GET_NOTEBOOK, params, {
2352
+ sourcePath: `/notebook/${notebookId}`
2291
2353
  });
2292
- return this.getStatus(notebookId);
2293
- }
2294
- };
2295
- var PERM_MAP = {
2296
- 1: "owner",
2297
- 2: "editor",
2298
- 3: "viewer"
2299
- };
2300
- function parseSharedUser(data) {
2301
- const email = typeof data[0] === "string" ? data[0] : "";
2302
- const permCode = typeof data[1] === "number" ? data[1] : 3;
2303
- const permission = PERM_MAP[permCode] ?? "viewer";
2304
- let displayName = null;
2305
- let avatarUrl = null;
2306
- if (Array.isArray(data[3])) {
2307
- const info = data[3];
2308
- displayName = typeof info[0] === "string" ? info[0] : null;
2309
- avatarUrl = typeof info[1] === "string" ? info[1] : null;
2310
- }
2311
- return { email, permission, displayName, avatarUrl };
2312
- }
2313
- function parseShareStatus(data, notebookId) {
2314
- const users = [];
2315
- if (Array.isArray(data[0])) {
2316
- for (const entry of data[0]) {
2317
- if (Array.isArray(entry)) users.push(parseSharedUser(entry));
2354
+ const sourceIds = [];
2355
+ if (!Array.isArray(data) || !data.length) return sourceIds;
2356
+ try {
2357
+ const nbInfo = data[0];
2358
+ if (!Array.isArray(nbInfo) || nbInfo.length <= 1) return sourceIds;
2359
+ const sources = nbInfo[1];
2360
+ if (!Array.isArray(sources)) return sourceIds;
2361
+ for (const src of sources) {
2362
+ if (!Array.isArray(src) || !src.length) continue;
2363
+ const first = src[0];
2364
+ if (Array.isArray(first) && first.length > 0 && typeof first[0] === "string") {
2365
+ sourceIds.push(first[0]);
2366
+ }
2367
+ }
2368
+ } catch {
2318
2369
  }
2370
+ return sourceIds;
2319
2371
  }
2320
- const isPublic = Array.isArray(data[1]) && data[1][0] === true;
2321
- const access = isPublic ? exports.ShareAccess.ANYONE_WITH_LINK : exports.ShareAccess.RESTRICTED;
2322
- const shareUrl = isPublic ? `https://notebooklm.google.com/notebook/${notebookId}` : null;
2323
- return {
2324
- notebookId,
2325
- isPublic,
2326
- access,
2327
- viewLevel: exports.ShareViewLevel.FULL_NOTEBOOK,
2328
- sharedUsers: users,
2329
- shareUrl
2330
- };
2331
- }
2372
+ };
2332
2373
 
2333
2374
  // src/client.ts
2334
2375
  var NotebookLMClient = class _NotebookLMClient {
@@ -2372,7 +2413,6 @@ var NotebookLMClient = class _NotebookLMClient {
2372
2413
  };
2373
2414
 
2374
2415
  // src/index.ts
2375
- init_auth();
2376
2416
  init_enums();
2377
2417
  init_errors();
2378
2418