notebooklm-sdk 0.1.0
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/LICENSE +21 -0
- package/README.md +249 -0
- package/dist/errors.cjs +193 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.cts +134 -0
- package/dist/errors.d.ts +134 -0
- package/dist/errors.js +171 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.cjs +2390 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +578 -0
- package/dist/index.d.ts +578 -0
- package/dist/index.js +2379 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2390 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __esm = (fn, res) => function __init() {
|
|
8
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
9
|
+
};
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all)
|
|
12
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/types/errors.ts
|
|
16
|
+
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
|
+
var init_errors = __esm({
|
|
18
|
+
"src/types/errors.ts"() {
|
|
19
|
+
exports.NotebookLMError = class extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = this.constructor.name;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
exports.NetworkError = class extends exports.NotebookLMError {
|
|
26
|
+
methodId;
|
|
27
|
+
originalError;
|
|
28
|
+
constructor(message, opts = {}) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.methodId = opts.methodId;
|
|
31
|
+
this.originalError = opts.originalError;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
exports.RPCTimeoutError = class extends exports.NetworkError {
|
|
35
|
+
};
|
|
36
|
+
exports.RPCError = class extends exports.NotebookLMError {
|
|
37
|
+
methodId;
|
|
38
|
+
rawResponse;
|
|
39
|
+
rpcCode;
|
|
40
|
+
foundIds;
|
|
41
|
+
constructor(message, opts = {}) {
|
|
42
|
+
super(message);
|
|
43
|
+
this.methodId = opts.methodId;
|
|
44
|
+
this.rawResponse = opts.rawResponse ? opts.rawResponse.slice(0, 500) : void 0;
|
|
45
|
+
this.rpcCode = opts.rpcCode;
|
|
46
|
+
this.foundIds = opts.foundIds ?? [];
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
exports.AuthError = class extends exports.RPCError {
|
|
50
|
+
};
|
|
51
|
+
exports.RateLimitError = class extends exports.RPCError {
|
|
52
|
+
retryAfter;
|
|
53
|
+
constructor(message, opts = {}) {
|
|
54
|
+
super(message, opts);
|
|
55
|
+
this.retryAfter = opts.retryAfter;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
exports.ServerError = class extends exports.RPCError {
|
|
59
|
+
statusCode;
|
|
60
|
+
constructor(message, opts = {}) {
|
|
61
|
+
super(message, opts);
|
|
62
|
+
this.statusCode = opts.statusCode;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
exports.ClientError = class extends exports.RPCError {
|
|
66
|
+
statusCode;
|
|
67
|
+
constructor(message, opts = {}) {
|
|
68
|
+
super(message, opts);
|
|
69
|
+
this.statusCode = opts.statusCode;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
exports.NotebookError = class extends exports.NotebookLMError {
|
|
73
|
+
};
|
|
74
|
+
exports.NotebookNotFoundError = class extends exports.NotebookError {
|
|
75
|
+
notebookId;
|
|
76
|
+
constructor(notebookId) {
|
|
77
|
+
super(`Notebook not found: ${notebookId}`);
|
|
78
|
+
this.notebookId = notebookId;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
exports.SourceError = class extends exports.NotebookLMError {
|
|
82
|
+
};
|
|
83
|
+
exports.SourceNotFoundError = class extends exports.SourceError {
|
|
84
|
+
sourceId;
|
|
85
|
+
constructor(sourceId) {
|
|
86
|
+
super(`Source not found: ${sourceId}`);
|
|
87
|
+
this.sourceId = sourceId;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
exports.SourceAddError = class extends exports.SourceError {
|
|
91
|
+
url;
|
|
92
|
+
cause;
|
|
93
|
+
constructor(url, opts = {}) {
|
|
94
|
+
super(
|
|
95
|
+
opts.message ?? `Failed to add source: ${url}
|
|
96
|
+
Possible causes:
|
|
97
|
+
- URL is invalid or inaccessible
|
|
98
|
+
- Content is behind a paywall or requires authentication
|
|
99
|
+
- Rate limiting or quota exceeded`
|
|
100
|
+
);
|
|
101
|
+
this.url = url;
|
|
102
|
+
this.cause = opts.cause;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
exports.SourceProcessingError = class extends exports.SourceError {
|
|
106
|
+
sourceId;
|
|
107
|
+
status;
|
|
108
|
+
constructor(sourceId, status = 3, message) {
|
|
109
|
+
super(message ?? `Source ${sourceId} failed to process`);
|
|
110
|
+
this.sourceId = sourceId;
|
|
111
|
+
this.status = status;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
exports.SourceTimeoutError = class extends exports.SourceError {
|
|
115
|
+
sourceId;
|
|
116
|
+
timeout;
|
|
117
|
+
lastStatus;
|
|
118
|
+
constructor(sourceId, timeout, lastStatus) {
|
|
119
|
+
const statusInfo = lastStatus != null ? ` (last status: ${lastStatus})` : "";
|
|
120
|
+
super(`Source ${sourceId} not ready after ${timeout.toFixed(1)}s${statusInfo}`);
|
|
121
|
+
this.sourceId = sourceId;
|
|
122
|
+
this.timeout = timeout;
|
|
123
|
+
this.lastStatus = lastStatus;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
exports.ArtifactError = class extends exports.NotebookLMError {
|
|
127
|
+
};
|
|
128
|
+
exports.ArtifactNotFoundError = class extends exports.ArtifactError {
|
|
129
|
+
artifactId;
|
|
130
|
+
artifactType;
|
|
131
|
+
constructor(artifactId, artifactType) {
|
|
132
|
+
const typeInfo = artifactType ? ` ${artifactType}` : "";
|
|
133
|
+
super(`${typeInfo.trim() || "Artifact"} ${artifactId} not found`);
|
|
134
|
+
this.artifactId = artifactId;
|
|
135
|
+
this.artifactType = artifactType;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
exports.ArtifactNotReadyError = class extends exports.ArtifactError {
|
|
139
|
+
artifactType;
|
|
140
|
+
artifactId;
|
|
141
|
+
status;
|
|
142
|
+
constructor(artifactType, opts = {}) {
|
|
143
|
+
const base = opts.artifactId ? `${artifactType} artifact ${opts.artifactId} is not ready` : `No completed ${artifactType} found`;
|
|
144
|
+
const statusInfo = opts.status ? ` (status: ${opts.status})` : "";
|
|
145
|
+
super(`${base}${statusInfo}`);
|
|
146
|
+
this.artifactType = artifactType;
|
|
147
|
+
this.artifactId = opts.artifactId;
|
|
148
|
+
this.status = opts.status;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
exports.ArtifactParseError = class extends exports.ArtifactError {
|
|
152
|
+
artifactType;
|
|
153
|
+
artifactId;
|
|
154
|
+
details;
|
|
155
|
+
cause;
|
|
156
|
+
constructor(artifactType, opts = {}) {
|
|
157
|
+
let msg = `Failed to parse ${artifactType} artifact`;
|
|
158
|
+
if (opts.artifactId) msg += ` ${opts.artifactId}`;
|
|
159
|
+
if (opts.details) msg += `: ${opts.details}`;
|
|
160
|
+
super(msg);
|
|
161
|
+
this.artifactType = artifactType;
|
|
162
|
+
this.artifactId = opts.artifactId;
|
|
163
|
+
this.details = opts.details;
|
|
164
|
+
this.cause = opts.cause;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
exports.ArtifactDownloadError = class extends exports.ArtifactError {
|
|
168
|
+
artifactType;
|
|
169
|
+
artifactId;
|
|
170
|
+
details;
|
|
171
|
+
cause;
|
|
172
|
+
constructor(artifactType, opts = {}) {
|
|
173
|
+
let msg = `Failed to download ${artifactType} artifact`;
|
|
174
|
+
if (opts.artifactId) msg += ` ${opts.artifactId}`;
|
|
175
|
+
if (opts.details) msg += `: ${opts.details}`;
|
|
176
|
+
super(msg);
|
|
177
|
+
this.artifactType = artifactType;
|
|
178
|
+
this.artifactId = opts.artifactId;
|
|
179
|
+
this.details = opts.details;
|
|
180
|
+
this.cause = opts.cause;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
exports.ChatError = class extends exports.NotebookLMError {
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// src/auth.ts
|
|
189
|
+
var auth_exports = {};
|
|
190
|
+
__export(auth_exports, {
|
|
191
|
+
buildCookieHeader: () => buildCookieHeader,
|
|
192
|
+
buildGoogleCookieHeader: () => buildGoogleCookieHeader,
|
|
193
|
+
connect: () => connect,
|
|
194
|
+
fetchTokens: () => fetchTokens,
|
|
195
|
+
loadCookiesFromFile: () => loadCookiesFromFile,
|
|
196
|
+
loadCookiesFromMap: () => loadCookiesFromMap,
|
|
197
|
+
loadCookiesFromObject: () => loadCookiesFromObject,
|
|
198
|
+
loadCookiesFromString: () => loadCookiesFromString
|
|
199
|
+
});
|
|
200
|
+
function loadCookiesFromFile(filePath) {
|
|
201
|
+
let raw;
|
|
202
|
+
try {
|
|
203
|
+
raw = fs.readFileSync(filePath, "utf-8");
|
|
204
|
+
} catch {
|
|
205
|
+
throw new exports.AuthError(
|
|
206
|
+
`Cookie file not found: ${filePath}
|
|
207
|
+
Provide valid Playwright storage state JSON.`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return extractCookiesFromStorageState(JSON.parse(raw));
|
|
211
|
+
}
|
|
212
|
+
function loadCookiesFromObject(storageState) {
|
|
213
|
+
return extractCookiesFromStorageState(storageState);
|
|
214
|
+
}
|
|
215
|
+
function buildGoogleCookieHeader(storageState) {
|
|
216
|
+
const map = {};
|
|
217
|
+
for (const c of storageState.cookies ?? []) {
|
|
218
|
+
if (c.domain === ".google.com" && c.name && c.value) {
|
|
219
|
+
map[c.name] = map[c.name] ?? c.value;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return buildCookieHeader(map);
|
|
223
|
+
}
|
|
224
|
+
function loadCookiesFromMap(map) {
|
|
225
|
+
return { ...map };
|
|
226
|
+
}
|
|
227
|
+
function loadCookiesFromString(cookieStr) {
|
|
228
|
+
const map = {};
|
|
229
|
+
for (const part of cookieStr.split(/;\s*/)) {
|
|
230
|
+
const idx = part.indexOf("=");
|
|
231
|
+
if (idx > 0) {
|
|
232
|
+
const name = part.slice(0, idx).trim();
|
|
233
|
+
const value = part.slice(idx + 1).trim();
|
|
234
|
+
if (name) map[name] = value;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return map;
|
|
238
|
+
}
|
|
239
|
+
function extractCookiesFromStorageState(storageState) {
|
|
240
|
+
const cookies = {};
|
|
241
|
+
for (const cookie of storageState.cookies ?? []) {
|
|
242
|
+
const { domain, name, value } = cookie;
|
|
243
|
+
if (!isAllowedDomain(domain) || !name) continue;
|
|
244
|
+
const isBase = domain === ".google.com";
|
|
245
|
+
if (!(name in cookies) || isBase) {
|
|
246
|
+
cookies[name] = value;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (!cookies["SID"]) {
|
|
250
|
+
throw new exports.AuthError(
|
|
251
|
+
"Missing required cookie: SID. Provide valid Playwright storage state with Google cookies."
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
return cookies;
|
|
255
|
+
}
|
|
256
|
+
function isAllowedDomain(domain) {
|
|
257
|
+
if (domain === ".google.com" || domain === "notebooklm.google.com" || domain === ".googleusercontent.com") {
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
if (domain.startsWith(".google.")) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
function buildCookieHeader(cookies) {
|
|
266
|
+
return Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join("; ");
|
|
267
|
+
}
|
|
268
|
+
async function fetchTokens(cookies) {
|
|
269
|
+
const cookieHeader = buildCookieHeader(cookies);
|
|
270
|
+
const response = await fetch(NOTEBOOKLM_URL, {
|
|
271
|
+
headers: { Cookie: cookieHeader },
|
|
272
|
+
redirect: "follow"
|
|
273
|
+
});
|
|
274
|
+
if (!response.ok) {
|
|
275
|
+
throw new exports.AuthError(`Failed to fetch NotebookLM page: HTTP ${response.status}`);
|
|
276
|
+
}
|
|
277
|
+
const finalUrl = response.url;
|
|
278
|
+
if (isGoogleAuthRedirect(finalUrl)) {
|
|
279
|
+
throw new exports.AuthError(`Redirected to login page: ${finalUrl}. Cookies may be expired.`);
|
|
280
|
+
}
|
|
281
|
+
const html = await response.text();
|
|
282
|
+
const csrfToken = extractCsrfToken(html, finalUrl);
|
|
283
|
+
const sessionId = extractSessionId(html, finalUrl);
|
|
284
|
+
return { csrfToken, sessionId };
|
|
285
|
+
}
|
|
286
|
+
function extractCsrfToken(html, finalUrl) {
|
|
287
|
+
const match = /"SNlM0e"\s*:\s*"([^"]+)"/.exec(html);
|
|
288
|
+
if (!match?.[1]) {
|
|
289
|
+
if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
|
|
290
|
+
throw new exports.AuthError("Authentication expired or invalid. Cookies may need to be refreshed.");
|
|
291
|
+
}
|
|
292
|
+
throw new exports.AuthError("CSRF token (SNlM0e) not found in NotebookLM page HTML.");
|
|
293
|
+
}
|
|
294
|
+
return match[1];
|
|
295
|
+
}
|
|
296
|
+
function extractSessionId(html, finalUrl) {
|
|
297
|
+
const match = /"FdrFJe"\s*:\s*"([^"]+)"/.exec(html);
|
|
298
|
+
if (!match?.[1]) {
|
|
299
|
+
if (isGoogleAuthRedirect(finalUrl) || html.includes("accounts.google.com")) {
|
|
300
|
+
throw new exports.AuthError("Authentication expired or invalid. Cookies may need to be refreshed.");
|
|
301
|
+
}
|
|
302
|
+
throw new exports.AuthError("Session ID (FdrFJe) not found in NotebookLM page HTML.");
|
|
303
|
+
}
|
|
304
|
+
return match[1];
|
|
305
|
+
}
|
|
306
|
+
function isGoogleAuthRedirect(url) {
|
|
307
|
+
return url.includes("accounts.google.com") || url.includes("signin");
|
|
308
|
+
}
|
|
309
|
+
async function connect(opts) {
|
|
310
|
+
let cookieMap;
|
|
311
|
+
let googleCookieHeader = null;
|
|
312
|
+
if (opts.cookies) {
|
|
313
|
+
cookieMap = loadCookiesFromString(opts.cookies);
|
|
314
|
+
} else if (opts.cookiesFile) {
|
|
315
|
+
cookieMap = loadCookiesFromFile(opts.cookiesFile);
|
|
316
|
+
} else if (opts.cookiesObject) {
|
|
317
|
+
if ("cookies" in opts.cookiesObject && Array.isArray(opts.cookiesObject.cookies)) {
|
|
318
|
+
const storageState = opts.cookiesObject;
|
|
319
|
+
cookieMap = loadCookiesFromObject(storageState);
|
|
320
|
+
googleCookieHeader = buildGoogleCookieHeader(storageState);
|
|
321
|
+
} else {
|
|
322
|
+
cookieMap = loadCookiesFromMap(opts.cookiesObject);
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
const envCookies = process.env["NOTEBOOKLM_COOKIES"];
|
|
326
|
+
if (envCookies) {
|
|
327
|
+
cookieMap = loadCookiesFromString(envCookies);
|
|
328
|
+
} else {
|
|
329
|
+
throw new exports.AuthError(
|
|
330
|
+
"No cookies provided. Pass cookies, cookiesFile, or cookiesObject to connect()."
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const { csrfToken, sessionId } = await fetchTokens(cookieMap);
|
|
335
|
+
const cookieHeader = buildCookieHeader(cookieMap);
|
|
336
|
+
return {
|
|
337
|
+
cookies: cookieMap,
|
|
338
|
+
csrfToken,
|
|
339
|
+
sessionId,
|
|
340
|
+
cookieHeader,
|
|
341
|
+
googleCookieHeader: googleCookieHeader ?? cookieHeader
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
var NOTEBOOKLM_URL;
|
|
345
|
+
var init_auth = __esm({
|
|
346
|
+
"src/auth.ts"() {
|
|
347
|
+
init_errors();
|
|
348
|
+
NOTEBOOKLM_URL = "https://notebooklm.google.com/";
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
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
|
|
888
|
+
init_enums();
|
|
889
|
+
|
|
890
|
+
// src/types/models.ts
|
|
891
|
+
init_enums();
|
|
892
|
+
function parseNotebook(data) {
|
|
893
|
+
const rawTitle = typeof data[0] === "string" ? data[0] : "";
|
|
894
|
+
const title = rawTitle.replace("thought\n", "").trim();
|
|
895
|
+
const id = typeof data[2] === "string" ? data[2] : "";
|
|
896
|
+
let createdAt = null;
|
|
897
|
+
if (Array.isArray(data[5]) && Array.isArray(data[5][5]) && data[5][5].length > 0) {
|
|
898
|
+
try {
|
|
899
|
+
createdAt = new Date(data[5][5][0] * 1e3);
|
|
900
|
+
} catch {
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
const isOwner = !(Array.isArray(data[5]) && data[5][1] === true);
|
|
904
|
+
return { id, title, createdAt, sourcesCount: 0, isOwner };
|
|
905
|
+
}
|
|
906
|
+
function parseSource(src) {
|
|
907
|
+
const srcId = Array.isArray(src[0]) ? src[0][0] : src[0];
|
|
908
|
+
const title = typeof src[1] === "string" ? src[1] : null;
|
|
909
|
+
let url = null;
|
|
910
|
+
if (Array.isArray(src[2]) && Array.isArray(src[2][7]) && src[2][7].length > 0) {
|
|
911
|
+
url = typeof src[2][7][0] === "string" ? src[2][7][0] : null;
|
|
912
|
+
}
|
|
913
|
+
let createdAt = null;
|
|
914
|
+
if (Array.isArray(src[2]) && Array.isArray(src[2][2]) && typeof src[2][2][0] === "number") {
|
|
915
|
+
try {
|
|
916
|
+
createdAt = new Date(src[2][2][0] * 1e3);
|
|
917
|
+
} catch {
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
let statusCode = 2;
|
|
921
|
+
if (Array.isArray(src[3]) && typeof src[3][1] === "number") {
|
|
922
|
+
statusCode = src[3][1];
|
|
923
|
+
}
|
|
924
|
+
let typeCode = null;
|
|
925
|
+
if (Array.isArray(src[2]) && typeof src[2][4] === "number") {
|
|
926
|
+
typeCode = src[2][4];
|
|
927
|
+
}
|
|
928
|
+
return {
|
|
929
|
+
id: String(srcId),
|
|
930
|
+
title,
|
|
931
|
+
url,
|
|
932
|
+
kind: sourceTypeFromCode(typeCode),
|
|
933
|
+
createdAt,
|
|
934
|
+
status: sourceStatusFromCode(statusCode),
|
|
935
|
+
_typeCode: typeCode
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
function parseArtifact(data, notebookId) {
|
|
939
|
+
const id = typeof data[0] === "string" ? data[0] : "";
|
|
940
|
+
const title = typeof data[1] === "string" ? data[1] : null;
|
|
941
|
+
const typeCode = typeof data[2] === "number" ? data[2] : 0;
|
|
942
|
+
const statusCode = typeof data[4] === "number" ? data[4] : 0;
|
|
943
|
+
let variant = null;
|
|
944
|
+
if (typeCode === 4 && Array.isArray(data[9]) && Array.isArray(data[9][1]) && typeof data[9][1][0] === "number") {
|
|
945
|
+
variant = data[9][1][0];
|
|
946
|
+
}
|
|
947
|
+
let audioUrl = null;
|
|
948
|
+
const meta6 = data[6];
|
|
949
|
+
if (Array.isArray(meta6) && Array.isArray(meta6[5])) {
|
|
950
|
+
const mediaList = meta6[5];
|
|
951
|
+
for (const item of mediaList) {
|
|
952
|
+
if (Array.isArray(item) && item[2] === "audio/mp4" && typeof item[0] === "string") {
|
|
953
|
+
audioUrl = item[0];
|
|
954
|
+
break;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
if (!audioUrl && Array.isArray(mediaList[0]) && typeof mediaList[0][0] === "string") {
|
|
958
|
+
audioUrl = mediaList[0][0];
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
let videoUrl = null;
|
|
962
|
+
if (Array.isArray(data[8])) {
|
|
963
|
+
const meta8 = data[8];
|
|
964
|
+
for (const item of meta8) {
|
|
965
|
+
if (Array.isArray(item) && Array.isArray(item[0]) && typeof item[0][0] === "string" && item[0][0].startsWith("http")) {
|
|
966
|
+
const mediaList = item;
|
|
967
|
+
let best = null;
|
|
968
|
+
for (const m of mediaList) {
|
|
969
|
+
if (Array.isArray(m) && m[2] === "video/mp4" && typeof m[0] === "string") {
|
|
970
|
+
best = m[0];
|
|
971
|
+
if (m[1] === 4) break;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
videoUrl = best ?? (Array.isArray(mediaList[0]) && typeof mediaList[0][0] === "string" ? mediaList[0][0] : null);
|
|
975
|
+
break;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
let content = null;
|
|
980
|
+
if (Array.isArray(data[7]) && typeof data[7][0] === "string") {
|
|
981
|
+
content = data[7][0];
|
|
982
|
+
} else if (typeof data[7] === "string") {
|
|
983
|
+
content = data[7];
|
|
984
|
+
}
|
|
985
|
+
return {
|
|
986
|
+
id,
|
|
987
|
+
title,
|
|
988
|
+
kind: artifactTypeFromCode(typeCode, variant),
|
|
989
|
+
status: artifactStatusFromCode(statusCode),
|
|
990
|
+
notebookId,
|
|
991
|
+
audioUrl,
|
|
992
|
+
videoUrl,
|
|
993
|
+
exportUrl: null,
|
|
994
|
+
shareUrl: null,
|
|
995
|
+
content,
|
|
996
|
+
_raw: Array.isArray(data) ? data : []
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
function parseNote(data) {
|
|
1000
|
+
const id = typeof data[0] === "string" ? data[0] : "";
|
|
1001
|
+
const content = typeof data[1] === "string" ? data[1] : "";
|
|
1002
|
+
const title = typeof data[2] === "string" ? data[2] : null;
|
|
1003
|
+
let createdAt = null;
|
|
1004
|
+
let updatedAt = null;
|
|
1005
|
+
if (Array.isArray(data[3]) && typeof data[3][0] === "number") {
|
|
1006
|
+
try {
|
|
1007
|
+
createdAt = new Date(data[3][0] * 1e3);
|
|
1008
|
+
} catch {
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (Array.isArray(data[4]) && typeof data[4][0] === "number") {
|
|
1012
|
+
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;
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
for (const item of result) {
|
|
1280
|
+
if (typeof item === "string" && item.length > 8) return item;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
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));
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// src/api/artifacts.ts
|
|
1291
|
+
init_enums();
|
|
1292
|
+
init_errors();
|
|
1293
|
+
function tripleNest(ids) {
|
|
1294
|
+
return ids.map((id) => [[id]]);
|
|
1295
|
+
}
|
|
1296
|
+
function doubleNest(ids) {
|
|
1297
|
+
return ids.map((id) => [id]);
|
|
1298
|
+
}
|
|
1299
|
+
var ArtifactsAPI = class {
|
|
1300
|
+
constructor(rpc, auth) {
|
|
1301
|
+
this.rpc = rpc;
|
|
1302
|
+
this.auth = auth;
|
|
1303
|
+
}
|
|
1304
|
+
async list(notebookId) {
|
|
1305
|
+
const params = [[2], notebookId, 'NOT artifact.status = "ARTIFACT_STATUS_SUGGESTED"'];
|
|
1306
|
+
const result = await this.rpc.call(exports.RPCMethod.LIST_ARTIFACTS, params, {
|
|
1307
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1308
|
+
allowNull: true
|
|
1309
|
+
});
|
|
1310
|
+
if (!Array.isArray(result) || !result.length) return [];
|
|
1311
|
+
const rawList = Array.isArray(result[0]) ? result[0] : result;
|
|
1312
|
+
const artifacts = [];
|
|
1313
|
+
for (const item of rawList) {
|
|
1314
|
+
if (Array.isArray(item)) {
|
|
1315
|
+
try {
|
|
1316
|
+
artifacts.push(parseArtifact(item, notebookId));
|
|
1317
|
+
} catch {
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
return artifacts;
|
|
1322
|
+
}
|
|
1323
|
+
async get(notebookId, artifactId) {
|
|
1324
|
+
const artifacts = await this.list(notebookId);
|
|
1325
|
+
return artifacts.find((a) => a.id === artifactId) ?? null;
|
|
1326
|
+
}
|
|
1327
|
+
async delete(notebookId, artifactId) {
|
|
1328
|
+
const params = [[2], notebookId, artifactId];
|
|
1329
|
+
await this.rpc.call(exports.RPCMethod.DELETE_ARTIFACT, params, {
|
|
1330
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1331
|
+
allowNull: true
|
|
1332
|
+
});
|
|
1333
|
+
return true;
|
|
1334
|
+
}
|
|
1335
|
+
async rename(notebookId, artifactId, newTitle) {
|
|
1336
|
+
const params = [[2], notebookId, artifactId, newTitle];
|
|
1337
|
+
await this.rpc.call(exports.RPCMethod.RENAME_ARTIFACT, params, {
|
|
1338
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1339
|
+
allowNull: true
|
|
1340
|
+
});
|
|
1341
|
+
return true;
|
|
1342
|
+
}
|
|
1343
|
+
// ---------------------------------------------------------------------------
|
|
1344
|
+
// Generation
|
|
1345
|
+
// ---------------------------------------------------------------------------
|
|
1346
|
+
async createAudio(notebookId, opts = {}) {
|
|
1347
|
+
const format = opts.format ?? null;
|
|
1348
|
+
const length = opts.length ?? null;
|
|
1349
|
+
const language = opts.language ?? "en";
|
|
1350
|
+
const sourceIds = opts.sourceIds ?? await this.rpc.getSourceIds(notebookId);
|
|
1351
|
+
const triple = tripleNest(sourceIds);
|
|
1352
|
+
const double = doubleNest(sourceIds);
|
|
1353
|
+
const params = [
|
|
1354
|
+
[2],
|
|
1355
|
+
notebookId,
|
|
1356
|
+
[
|
|
1357
|
+
null,
|
|
1358
|
+
null,
|
|
1359
|
+
exports.ArtifactTypeCode.AUDIO,
|
|
1360
|
+
triple,
|
|
1361
|
+
null,
|
|
1362
|
+
null,
|
|
1363
|
+
[null, [opts.instructions ?? null, length, null, double, language, null, format]]
|
|
1364
|
+
]
|
|
1365
|
+
];
|
|
1366
|
+
return this._callGenerate(notebookId, params);
|
|
1367
|
+
}
|
|
1368
|
+
async createVideo(notebookId, opts = {}) {
|
|
1369
|
+
const format = opts.format ?? null;
|
|
1370
|
+
const style = opts.style ?? null;
|
|
1371
|
+
const language = opts.language ?? "en";
|
|
1372
|
+
const sourceIds = opts.sourceIds ?? await this.rpc.getSourceIds(notebookId);
|
|
1373
|
+
const triple = tripleNest(sourceIds);
|
|
1374
|
+
const double = doubleNest(sourceIds);
|
|
1375
|
+
const params = [
|
|
1376
|
+
[2],
|
|
1377
|
+
notebookId,
|
|
1378
|
+
[
|
|
1379
|
+
null,
|
|
1380
|
+
null,
|
|
1381
|
+
exports.ArtifactTypeCode.VIDEO,
|
|
1382
|
+
triple,
|
|
1383
|
+
null,
|
|
1384
|
+
null,
|
|
1385
|
+
null,
|
|
1386
|
+
null,
|
|
1387
|
+
[null, null, [double, language, opts.instructions ?? null, null, format, style]]
|
|
1388
|
+
]
|
|
1389
|
+
];
|
|
1390
|
+
return this._callGenerate(notebookId, params);
|
|
1391
|
+
}
|
|
1392
|
+
async createQuiz(notebookId, opts = {}) {
|
|
1393
|
+
const quantity = opts.quantity ?? null;
|
|
1394
|
+
const difficulty = opts.difficulty ?? null;
|
|
1395
|
+
const sourceIds = opts.sourceIds ?? await this.rpc.getSourceIds(notebookId);
|
|
1396
|
+
const triple = tripleNest(sourceIds);
|
|
1397
|
+
const params = [
|
|
1398
|
+
[2],
|
|
1399
|
+
notebookId,
|
|
1400
|
+
[
|
|
1401
|
+
null,
|
|
1402
|
+
null,
|
|
1403
|
+
exports.ArtifactTypeCode.QUIZ,
|
|
1404
|
+
triple,
|
|
1405
|
+
null,
|
|
1406
|
+
null,
|
|
1407
|
+
null,
|
|
1408
|
+
null,
|
|
1409
|
+
null,
|
|
1410
|
+
[
|
|
1411
|
+
null,
|
|
1412
|
+
[2, null, opts.instructions ?? null, null, null, null, null, [quantity, difficulty]]
|
|
1413
|
+
]
|
|
1414
|
+
]
|
|
1415
|
+
];
|
|
1416
|
+
return this._callGenerate(notebookId, params);
|
|
1417
|
+
}
|
|
1418
|
+
async createFlashcards(notebookId, opts = {}) {
|
|
1419
|
+
const quantity = opts.quantity ?? null;
|
|
1420
|
+
const difficulty = opts.difficulty ?? null;
|
|
1421
|
+
const sourceIds = opts.sourceIds ?? await this.rpc.getSourceIds(notebookId);
|
|
1422
|
+
const triple = tripleNest(sourceIds);
|
|
1423
|
+
const params = [
|
|
1424
|
+
[2],
|
|
1425
|
+
notebookId,
|
|
1426
|
+
[
|
|
1427
|
+
null,
|
|
1428
|
+
null,
|
|
1429
|
+
exports.ArtifactTypeCode.QUIZ,
|
|
1430
|
+
triple,
|
|
1431
|
+
null,
|
|
1432
|
+
null,
|
|
1433
|
+
null,
|
|
1434
|
+
null,
|
|
1435
|
+
null,
|
|
1436
|
+
[null, [1, null, opts.instructions ?? null, null, null, null, [difficulty, quantity]]]
|
|
1437
|
+
]
|
|
1438
|
+
];
|
|
1439
|
+
return this._callGenerate(notebookId, params);
|
|
1440
|
+
}
|
|
1441
|
+
async createInfographic(notebookId, opts = {}) {
|
|
1442
|
+
const orientation = opts.orientation ?? null;
|
|
1443
|
+
const detail = opts.detail ?? null;
|
|
1444
|
+
const style = opts.style ?? null;
|
|
1445
|
+
const language = opts.language ?? "en";
|
|
1446
|
+
const sourceIds = opts.sourceIds ?? await this.rpc.getSourceIds(notebookId);
|
|
1447
|
+
const triple = tripleNest(sourceIds);
|
|
1448
|
+
const params = [
|
|
1449
|
+
[2],
|
|
1450
|
+
notebookId,
|
|
1451
|
+
[
|
|
1452
|
+
null,
|
|
1453
|
+
null,
|
|
1454
|
+
exports.ArtifactTypeCode.INFOGRAPHIC,
|
|
1455
|
+
triple,
|
|
1456
|
+
null,
|
|
1457
|
+
null,
|
|
1458
|
+
null,
|
|
1459
|
+
null,
|
|
1460
|
+
null,
|
|
1461
|
+
null,
|
|
1462
|
+
null,
|
|
1463
|
+
null,
|
|
1464
|
+
null,
|
|
1465
|
+
null,
|
|
1466
|
+
[[opts.instructions ?? null, language, null, orientation, detail, style]]
|
|
1467
|
+
]
|
|
1468
|
+
];
|
|
1469
|
+
return this._callGenerate(notebookId, params);
|
|
1470
|
+
}
|
|
1471
|
+
async createSlideDeck(notebookId, opts = {}) {
|
|
1472
|
+
const format = opts.format ?? null;
|
|
1473
|
+
const length = opts.length ?? null;
|
|
1474
|
+
const language = opts.language ?? "en";
|
|
1475
|
+
const sourceIds = opts.sourceIds ?? await this.rpc.getSourceIds(notebookId);
|
|
1476
|
+
const triple = tripleNest(sourceIds);
|
|
1477
|
+
const params = [
|
|
1478
|
+
[2],
|
|
1479
|
+
notebookId,
|
|
1480
|
+
[
|
|
1481
|
+
null,
|
|
1482
|
+
null,
|
|
1483
|
+
exports.ArtifactTypeCode.SLIDE_DECK,
|
|
1484
|
+
triple,
|
|
1485
|
+
null,
|
|
1486
|
+
null,
|
|
1487
|
+
null,
|
|
1488
|
+
null,
|
|
1489
|
+
null,
|
|
1490
|
+
null,
|
|
1491
|
+
null,
|
|
1492
|
+
null,
|
|
1493
|
+
null,
|
|
1494
|
+
null,
|
|
1495
|
+
null,
|
|
1496
|
+
null,
|
|
1497
|
+
[[opts.instructions ?? null, language, format, length]]
|
|
1498
|
+
]
|
|
1499
|
+
];
|
|
1500
|
+
return this._callGenerate(notebookId, params);
|
|
1501
|
+
}
|
|
1502
|
+
async createReport(notebookId, opts = {}) {
|
|
1503
|
+
const format = opts.format ?? "briefing_doc";
|
|
1504
|
+
const language = opts.language ?? "en";
|
|
1505
|
+
const sourceIds = opts.sourceIds ?? await this.rpc.getSourceIds(notebookId);
|
|
1506
|
+
const triple = tripleNest(sourceIds);
|
|
1507
|
+
const double = doubleNest(sourceIds);
|
|
1508
|
+
const configs = {
|
|
1509
|
+
briefing_doc: {
|
|
1510
|
+
title: "Briefing Doc",
|
|
1511
|
+
description: "Key insights and important quotes",
|
|
1512
|
+
prompt: "Create a comprehensive briefing document that includes an Executive Summary, detailed analysis of key themes, important quotes with context, and actionable insights."
|
|
1513
|
+
},
|
|
1514
|
+
study_guide: {
|
|
1515
|
+
title: "Study Guide",
|
|
1516
|
+
description: "Short-answer quiz, essay questions, glossary",
|
|
1517
|
+
prompt: "Create a comprehensive study guide that includes key concepts, short-answer practice questions, essay prompts for deeper exploration, and a glossary of important terms."
|
|
1518
|
+
},
|
|
1519
|
+
blog_post: {
|
|
1520
|
+
title: "Blog Post",
|
|
1521
|
+
description: "Insightful takeaways in readable article format",
|
|
1522
|
+
prompt: "Write an engaging blog post that presents the key insights in an accessible, reader-friendly format with an attention-grabbing introduction and compelling conclusion."
|
|
1523
|
+
},
|
|
1524
|
+
custom: {
|
|
1525
|
+
title: "Custom Report",
|
|
1526
|
+
description: "Custom format",
|
|
1527
|
+
prompt: opts.customPrompt ?? "Create a report based on the provided sources."
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
const cfg = configs[format] ?? configs["briefing_doc"];
|
|
1531
|
+
const prompt = opts.extraInstructions && format !== "custom" ? `${cfg.prompt}
|
|
1532
|
+
|
|
1533
|
+
${opts.extraInstructions}` : cfg.prompt;
|
|
1534
|
+
const params = [
|
|
1535
|
+
[2],
|
|
1536
|
+
notebookId,
|
|
1537
|
+
[
|
|
1538
|
+
null,
|
|
1539
|
+
null,
|
|
1540
|
+
exports.ArtifactTypeCode.REPORT,
|
|
1541
|
+
triple,
|
|
1542
|
+
null,
|
|
1543
|
+
null,
|
|
1544
|
+
null,
|
|
1545
|
+
[null, [cfg.title, cfg.description, null, double, language, prompt, null, true]]
|
|
1546
|
+
]
|
|
1547
|
+
];
|
|
1548
|
+
return this._callGenerate(notebookId, params);
|
|
1549
|
+
}
|
|
1550
|
+
async createMindMap(notebookId, sourceIds) {
|
|
1551
|
+
const ids = sourceIds ?? await this.rpc.getSourceIds(notebookId);
|
|
1552
|
+
const triple = tripleNest(ids);
|
|
1553
|
+
const params = [
|
|
1554
|
+
triple,
|
|
1555
|
+
null,
|
|
1556
|
+
null,
|
|
1557
|
+
null,
|
|
1558
|
+
null,
|
|
1559
|
+
["interactive_mindmap", [["[CONTEXT]", ""]], ""],
|
|
1560
|
+
null,
|
|
1561
|
+
[2, null, [1]]
|
|
1562
|
+
];
|
|
1563
|
+
const result = await this.rpc.call(exports.RPCMethod.GENERATE_MIND_MAP, params, {
|
|
1564
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1565
|
+
allowNull: true
|
|
1566
|
+
});
|
|
1567
|
+
return this._parseGenerationResult(result);
|
|
1568
|
+
}
|
|
1569
|
+
// ---------------------------------------------------------------------------
|
|
1570
|
+
// Polling / download
|
|
1571
|
+
// ---------------------------------------------------------------------------
|
|
1572
|
+
/** Poll until artifact reaches completed/failed status. */
|
|
1573
|
+
async waitUntilReady(notebookId, artifactId, timeout = 300, pollInterval = 3) {
|
|
1574
|
+
const deadline = Date.now() + timeout * 1e3;
|
|
1575
|
+
while (Date.now() < deadline) {
|
|
1576
|
+
const artifact = await this.get(notebookId, artifactId);
|
|
1577
|
+
if (artifact?.status === "completed") return artifact;
|
|
1578
|
+
if (artifact?.status === "failed") {
|
|
1579
|
+
throw new exports.ArtifactNotReadyError(artifact.kind, { artifactId, status: "failed" });
|
|
1580
|
+
}
|
|
1581
|
+
await sleep2(pollInterval * 1e3);
|
|
1582
|
+
}
|
|
1583
|
+
throw new exports.ArtifactNotReadyError("artifact", { artifactId, status: "timeout" });
|
|
1584
|
+
}
|
|
1585
|
+
/** Download audio content as a Buffer. */
|
|
1586
|
+
async downloadAudio(notebookId, artifactId) {
|
|
1587
|
+
const artifact = await this.get(notebookId, artifactId);
|
|
1588
|
+
if (!artifact || artifact.status !== "completed") {
|
|
1589
|
+
throw new exports.ArtifactNotReadyError("audio", { artifactId, status: artifact?.status });
|
|
1590
|
+
}
|
|
1591
|
+
if (!artifact.audioUrl) throw new exports.ArtifactNotReadyError("audio", { artifactId });
|
|
1592
|
+
return this._fetchMediaWithCookies(artifact.audioUrl);
|
|
1593
|
+
}
|
|
1594
|
+
/** Download video content as a Buffer. */
|
|
1595
|
+
async downloadVideo(notebookId, artifactId) {
|
|
1596
|
+
const artifact = await this.get(notebookId, artifactId);
|
|
1597
|
+
if (!artifact || artifact.status !== "completed") {
|
|
1598
|
+
throw new exports.ArtifactNotReadyError("video", { artifactId, status: artifact?.status });
|
|
1599
|
+
}
|
|
1600
|
+
if (!artifact.videoUrl) throw new exports.ArtifactNotReadyError("video", { artifactId });
|
|
1601
|
+
return this._fetchMediaWithCookies(artifact.videoUrl);
|
|
1602
|
+
}
|
|
1603
|
+
/** Get markdown content for a completed report artifact. */
|
|
1604
|
+
async getReportMarkdown(notebookId, artifactId) {
|
|
1605
|
+
const artifact = await this.get(notebookId, artifactId);
|
|
1606
|
+
return artifact?.content ?? null;
|
|
1607
|
+
}
|
|
1608
|
+
/** Get interactive HTML for quiz/flashcard artifacts. */
|
|
1609
|
+
async getInteractiveHtml(notebookId, artifactId) {
|
|
1610
|
+
const params = [artifactId];
|
|
1611
|
+
const result = await this.rpc.call(exports.RPCMethod.GET_INTERACTIVE_HTML, params, {
|
|
1612
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1613
|
+
allowNull: true
|
|
1614
|
+
});
|
|
1615
|
+
if (Array.isArray(result) && Array.isArray(result[0])) {
|
|
1616
|
+
const data = result[0];
|
|
1617
|
+
if (Array.isArray(data[9]) && typeof data[9][0] === "string") {
|
|
1618
|
+
return data[9][0];
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
return null;
|
|
1622
|
+
}
|
|
1623
|
+
// ---------------------------------------------------------------------------
|
|
1624
|
+
// Internal
|
|
1625
|
+
// ---------------------------------------------------------------------------
|
|
1626
|
+
/**
|
|
1627
|
+
* Fetch a Google-hosted media URL, manually following redirects to ensure
|
|
1628
|
+
* cookies are included on every hop. Node/Bun fetch strips the Cookie header
|
|
1629
|
+
* on cross-origin redirects (e.g. googleusercontent.com → lh3.google.com).
|
|
1630
|
+
*/
|
|
1631
|
+
async _fetchMediaWithCookies(url, maxRedirects = 10) {
|
|
1632
|
+
let current = url;
|
|
1633
|
+
for (let i = 0; i < maxRedirects; i++) {
|
|
1634
|
+
if (!isTrustedDomain(current)) {
|
|
1635
|
+
throw new Error(`Untrusted redirect target: ${new URL(current).hostname}`);
|
|
1636
|
+
}
|
|
1637
|
+
const response = await fetch(current, {
|
|
1638
|
+
headers: { Cookie: this.auth.googleCookieHeader },
|
|
1639
|
+
redirect: "manual"
|
|
1640
|
+
});
|
|
1641
|
+
if (response.status >= 300 && response.status < 400) {
|
|
1642
|
+
const location = response.headers.get("location");
|
|
1643
|
+
if (!location)
|
|
1644
|
+
throw new Error(`Redirect with no Location header (status ${response.status})`);
|
|
1645
|
+
current = location.startsWith("http") ? location : new URL(location, current).href;
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
if (!response.ok) throw new Error(`Media download failed: HTTP ${response.status}`);
|
|
1649
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
1650
|
+
if (contentType.includes("text/html")) {
|
|
1651
|
+
throw new Error("Media download returned HTML \u2014 authentication cookies may be expired.");
|
|
1652
|
+
}
|
|
1653
|
+
return Buffer.from(await response.arrayBuffer());
|
|
1654
|
+
}
|
|
1655
|
+
throw new Error("Too many redirects fetching media URL");
|
|
1656
|
+
}
|
|
1657
|
+
async _callGenerate(notebookId, params) {
|
|
1658
|
+
const result = await this.rpc.call(exports.RPCMethod.CREATE_ARTIFACT, params, {
|
|
1659
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1660
|
+
allowNull: true
|
|
1661
|
+
});
|
|
1662
|
+
return this._parseGenerationResult(result);
|
|
1663
|
+
}
|
|
1664
|
+
_parseGenerationResult(result) {
|
|
1665
|
+
if (Array.isArray(result) && result.length > 0) {
|
|
1666
|
+
const artifactData = result[0];
|
|
1667
|
+
const artifactId = Array.isArray(artifactData) && artifactData.length > 0 && typeof artifactData[0] === "string" ? artifactData[0] : null;
|
|
1668
|
+
const statusCode = Array.isArray(artifactData) && artifactData.length > 4 && typeof artifactData[4] === "number" ? artifactData[4] : null;
|
|
1669
|
+
if (artifactId) {
|
|
1670
|
+
return {
|
|
1671
|
+
artifactId,
|
|
1672
|
+
status: statusCode != null ? artifactStatusFromCode(statusCode) : "pending"
|
|
1673
|
+
};
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
return { artifactId: null, status: "failed" };
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1679
|
+
function sleep2(ms) {
|
|
1680
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1681
|
+
}
|
|
1682
|
+
var TRUSTED_MEDIA_DOMAINS = [
|
|
1683
|
+
".google.com",
|
|
1684
|
+
".googleusercontent.com",
|
|
1685
|
+
".googleapis.com",
|
|
1686
|
+
".usercontent.google.com"
|
|
1687
|
+
];
|
|
1688
|
+
function isTrustedDomain(url) {
|
|
1689
|
+
try {
|
|
1690
|
+
const host = new URL(url).hostname;
|
|
1691
|
+
return TRUSTED_MEDIA_DOMAINS.some((d) => host === d.slice(1) || host.endsWith(d));
|
|
1692
|
+
} catch {
|
|
1693
|
+
return false;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// src/api/chat.ts
|
|
1698
|
+
init_enums();
|
|
1699
|
+
init_errors();
|
|
1700
|
+
var QUERY_URL = "https://notebooklm.google.com/_/LabsTailwindUi/data/google.internal.labs.tailwind.orchestration.v1.LabsTailwindOrchestrationService/GenerateFreeFormStreamed";
|
|
1701
|
+
var DEFAULT_BL = "boq_labs-tailwind-frontend_20260301.03_p0";
|
|
1702
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1703
|
+
var ChatAPI = class {
|
|
1704
|
+
constructor(rpc, auth) {
|
|
1705
|
+
this.rpc = rpc;
|
|
1706
|
+
this.auth = auth;
|
|
1707
|
+
}
|
|
1708
|
+
conversationCache = /* @__PURE__ */ new Map();
|
|
1709
|
+
reqid = Math.floor(Math.random() * 9e5) + 1e5;
|
|
1710
|
+
async ask(notebookId, query, opts = {}) {
|
|
1711
|
+
const sourceIds = opts.sourceIds ?? await this.rpc.getSourceIds(notebookId);
|
|
1712
|
+
const isNew = !opts.conversationId;
|
|
1713
|
+
const conversationId = opts.conversationId ?? randomUUID();
|
|
1714
|
+
const history = isNew ? null : this._buildHistory(conversationId);
|
|
1715
|
+
const sourcesArray = sourceIds.map((sid) => [[sid]]);
|
|
1716
|
+
const params = [
|
|
1717
|
+
sourcesArray,
|
|
1718
|
+
query,
|
|
1719
|
+
history,
|
|
1720
|
+
[2, null, [1], [1]],
|
|
1721
|
+
conversationId,
|
|
1722
|
+
null,
|
|
1723
|
+
null,
|
|
1724
|
+
notebookId,
|
|
1725
|
+
1
|
|
1726
|
+
];
|
|
1727
|
+
const paramsJson = JSON.stringify(params, null, 0);
|
|
1728
|
+
const fReq = JSON.stringify([null, paramsJson]);
|
|
1729
|
+
this.reqid += 1e5;
|
|
1730
|
+
const bl = typeof process !== "undefined" && process.env["NOTEBOOKLM_BL"] || DEFAULT_BL;
|
|
1731
|
+
const urlParams = new URLSearchParams({ bl, hl: "en", _reqid: String(this.reqid), rt: "c" });
|
|
1732
|
+
if (this.auth.sessionId) urlParams.set("f.sid", this.auth.sessionId);
|
|
1733
|
+
const bodyParts = [`f.req=${encodeURIComponent(fReq)}`];
|
|
1734
|
+
if (this.auth.csrfToken) bodyParts.push(`at=${encodeURIComponent(this.auth.csrfToken)}`);
|
|
1735
|
+
const body = bodyParts.join("&") + "&";
|
|
1736
|
+
let response;
|
|
1737
|
+
try {
|
|
1738
|
+
response = await fetch(`${QUERY_URL}?${urlParams.toString()}`, {
|
|
1739
|
+
method: "POST",
|
|
1740
|
+
headers: {
|
|
1741
|
+
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
1742
|
+
Cookie: this.auth.cookieHeader
|
|
1743
|
+
},
|
|
1744
|
+
body
|
|
1745
|
+
});
|
|
1746
|
+
} catch (e) {
|
|
1747
|
+
throw new exports.ChatError(`Chat request failed: ${String(e)}`);
|
|
1748
|
+
}
|
|
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 };
|
|
1758
|
+
}
|
|
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
|
|
1764
|
+
});
|
|
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;
|
|
1774
|
+
}
|
|
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 {
|
|
1783
|
+
}
|
|
1784
|
+
i++;
|
|
1785
|
+
}
|
|
1786
|
+
turns.push({ query: q, answer: a, turnNumber: turns.length + 1 });
|
|
1787
|
+
}
|
|
1788
|
+
i++;
|
|
1789
|
+
}
|
|
1790
|
+
return turns;
|
|
1791
|
+
}
|
|
1792
|
+
async getLastConversationId(notebookId) {
|
|
1793
|
+
const params = [[], null, notebookId, 1];
|
|
1794
|
+
const result = await this.rpc.call(exports.RPCMethod.GET_LAST_CONVERSATION_ID, params, {
|
|
1795
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1796
|
+
allowNull: true
|
|
1797
|
+
});
|
|
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;
|
|
1808
|
+
}
|
|
1809
|
+
clearCache(conversationId) {
|
|
1810
|
+
if (conversationId) {
|
|
1811
|
+
this.conversationCache.delete(conversationId);
|
|
1812
|
+
} else {
|
|
1813
|
+
this.conversationCache.clear();
|
|
1814
|
+
}
|
|
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]);
|
|
1823
|
+
}
|
|
1824
|
+
return history;
|
|
1825
|
+
}
|
|
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;
|
|
1841
|
+
}
|
|
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 });
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
if (isAnswer && answerText.length > bestMarkedAnswer.length) {
|
|
1876
|
+
bestMarkedAnswer = answerText;
|
|
1877
|
+
} else if (!isAnswer && answerText.length > bestUnmarkedAnswer.length) {
|
|
1878
|
+
bestUnmarkedAnswer = answerText;
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
let i = 0;
|
|
1883
|
+
while (i < lines.length) {
|
|
1884
|
+
const line = (lines[i] ?? "").trim();
|
|
1885
|
+
if (!line) {
|
|
1886
|
+
i++;
|
|
1887
|
+
continue;
|
|
1888
|
+
}
|
|
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++;
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
return {
|
|
1900
|
+
answer: bestMarkedAnswer || bestUnmarkedAnswer,
|
|
1901
|
+
conversationId: serverConvId,
|
|
1902
|
+
references
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
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
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
return null;
|
|
1915
|
+
}
|
|
1916
|
+
function randomUUID() {
|
|
1917
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
1918
|
+
return crypto.randomUUID();
|
|
1919
|
+
}
|
|
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
|
+
}
|
|
1925
|
+
|
|
1926
|
+
// src/api/notes.ts
|
|
1927
|
+
init_enums();
|
|
1928
|
+
var NotesAPI = class {
|
|
1929
|
+
constructor(rpc) {
|
|
1930
|
+
this.rpc = rpc;
|
|
1931
|
+
}
|
|
1932
|
+
async list(notebookId) {
|
|
1933
|
+
const params = [notebookId, [2]];
|
|
1934
|
+
const result = await this.rpc.call(exports.RPCMethod.GET_NOTES_AND_MIND_MAPS, params, {
|
|
1935
|
+
sourcePath: `/notebook/${notebookId}`
|
|
1936
|
+
});
|
|
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 };
|
|
1963
|
+
}
|
|
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}`
|
|
1968
|
+
});
|
|
1969
|
+
if (Array.isArray(result)) return parseNote(result);
|
|
1970
|
+
throw new Error("Could not parse note creation response");
|
|
1971
|
+
}
|
|
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}`
|
|
1976
|
+
});
|
|
1977
|
+
if (Array.isArray(result)) return parseNote(result);
|
|
1978
|
+
return { id: noteId, title: title ?? null, content, createdAt: null, updatedAt: /* @__PURE__ */ new Date() };
|
|
1979
|
+
}
|
|
1980
|
+
async delete(notebookId, noteId) {
|
|
1981
|
+
const params = [notebookId, noteId, [2]];
|
|
1982
|
+
await this.rpc.call(exports.RPCMethod.DELETE_NOTE, params, {
|
|
1983
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1984
|
+
allowNull: true
|
|
1985
|
+
});
|
|
1986
|
+
return true;
|
|
1987
|
+
}
|
|
1988
|
+
};
|
|
1989
|
+
|
|
1990
|
+
// src/api/research.ts
|
|
1991
|
+
init_enums();
|
|
1992
|
+
var ResearchAPI = class {
|
|
1993
|
+
constructor(rpc) {
|
|
1994
|
+
this.rpc = rpc;
|
|
1995
|
+
}
|
|
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;
|
|
2007
|
+
let params;
|
|
2008
|
+
if (mode === "fast") {
|
|
2009
|
+
rpcId = exports.RPCMethod.START_FAST_RESEARCH;
|
|
2010
|
+
params = [[query, sourceType], null, 1, notebookId];
|
|
2011
|
+
} else {
|
|
2012
|
+
rpcId = exports.RPCMethod.START_DEEP_RESEARCH;
|
|
2013
|
+
params = [null, [1], [query, sourceType], 5, notebookId];
|
|
2014
|
+
}
|
|
2015
|
+
const result = await this.rpc.call(rpcId, params, {
|
|
2016
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
2017
|
+
allowNull: true
|
|
2018
|
+
});
|
|
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
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
return null;
|
|
2029
|
+
}
|
|
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, {
|
|
2034
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
2035
|
+
allowNull: true
|
|
2036
|
+
});
|
|
2037
|
+
if (!Array.isArray(result) || !result.length) {
|
|
2038
|
+
return emptyResult();
|
|
2039
|
+
}
|
|
2040
|
+
if (Array.isArray(result[0]) && Array.isArray(result[0][0])) {
|
|
2041
|
+
result = result[0];
|
|
2042
|
+
}
|
|
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
|
+
});
|
|
2100
|
+
}
|
|
2101
|
+
if (parsedTasks.length > 0) {
|
|
2102
|
+
return { ...parsedTasks[0], tasks: parsedTasks };
|
|
2103
|
+
}
|
|
2104
|
+
return emptyResult();
|
|
2105
|
+
}
|
|
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, {
|
|
2129
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
2130
|
+
allowNull: true
|
|
2131
|
+
});
|
|
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] });
|
|
2142
|
+
}
|
|
2143
|
+
return imported;
|
|
2144
|
+
}
|
|
2145
|
+
};
|
|
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;
|
|
2151
|
+
}
|
|
2152
|
+
return 1;
|
|
2153
|
+
}
|
|
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");
|
|
2157
|
+
}
|
|
2158
|
+
function buildReportEntry(title, markdown) {
|
|
2159
|
+
return [null, [title, markdown], null, 3, null, null, null, null, null, null, 3];
|
|
2160
|
+
}
|
|
2161
|
+
function buildWebEntry(url, title) {
|
|
2162
|
+
return [null, null, [url, title], null, null, null, null, null, null, null, 2];
|
|
2163
|
+
}
|
|
2164
|
+
function emptyResult() {
|
|
2165
|
+
return {
|
|
2166
|
+
taskId: null,
|
|
2167
|
+
status: "no_research",
|
|
2168
|
+
query: "",
|
|
2169
|
+
sources: [],
|
|
2170
|
+
summary: "",
|
|
2171
|
+
report: "",
|
|
2172
|
+
tasks: []
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
// src/api/settings.ts
|
|
2177
|
+
init_enums();
|
|
2178
|
+
var SettingsAPI = class {
|
|
2179
|
+
constructor(rpc) {
|
|
2180
|
+
this.rpc = rpc;
|
|
2181
|
+
}
|
|
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]);
|
|
2190
|
+
}
|
|
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]);
|
|
2204
|
+
}
|
|
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];
|
|
2212
|
+
}
|
|
2213
|
+
return typeof cur === "string" && cur ? cur : null;
|
|
2214
|
+
} catch {
|
|
2215
|
+
return null;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
|
|
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);
|
|
2275
|
+
}
|
|
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
|
+
}
|
|
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
|
|
2291
|
+
});
|
|
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));
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
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
|
+
}
|
|
2332
|
+
|
|
2333
|
+
// src/client.ts
|
|
2334
|
+
var NotebookLMClient = class _NotebookLMClient {
|
|
2335
|
+
constructor(auth, opts = {}) {
|
|
2336
|
+
this.auth = auth;
|
|
2337
|
+
const rpc = new RPCCore(auth, opts.timeoutMs);
|
|
2338
|
+
this.notebooks = new NotebooksAPI(rpc);
|
|
2339
|
+
this.sources = new SourcesAPI(rpc, auth);
|
|
2340
|
+
this.artifacts = new ArtifactsAPI(rpc, auth);
|
|
2341
|
+
this.chat = new ChatAPI(rpc, auth);
|
|
2342
|
+
this.notes = new NotesAPI(rpc);
|
|
2343
|
+
this.research = new ResearchAPI(rpc);
|
|
2344
|
+
this.settings = new SettingsAPI(rpc);
|
|
2345
|
+
this.sharing = new SharingAPI(rpc);
|
|
2346
|
+
}
|
|
2347
|
+
notebooks;
|
|
2348
|
+
sources;
|
|
2349
|
+
artifacts;
|
|
2350
|
+
chat;
|
|
2351
|
+
notes;
|
|
2352
|
+
research;
|
|
2353
|
+
settings;
|
|
2354
|
+
sharing;
|
|
2355
|
+
/**
|
|
2356
|
+
* Connect to NotebookLM using cookies.
|
|
2357
|
+
* Fetches CSRF and session tokens from the NotebookLM homepage.
|
|
2358
|
+
*/
|
|
2359
|
+
static async connect(opts, clientOpts = {}) {
|
|
2360
|
+
const auth = await connect(opts);
|
|
2361
|
+
return new _NotebookLMClient(auth, clientOpts);
|
|
2362
|
+
}
|
|
2363
|
+
/**
|
|
2364
|
+
* Refresh CSRF and session tokens (e.g. if they expire mid-session).
|
|
2365
|
+
*/
|
|
2366
|
+
async refreshTokens() {
|
|
2367
|
+
const { fetchTokens: fetchTokens2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
|
|
2368
|
+
const { csrfToken, sessionId } = await fetchTokens2(this.auth.cookies);
|
|
2369
|
+
this.auth.csrfToken = csrfToken;
|
|
2370
|
+
this.auth.sessionId = sessionId;
|
|
2371
|
+
}
|
|
2372
|
+
};
|
|
2373
|
+
|
|
2374
|
+
// src/index.ts
|
|
2375
|
+
init_auth();
|
|
2376
|
+
init_enums();
|
|
2377
|
+
init_errors();
|
|
2378
|
+
|
|
2379
|
+
exports.ArtifactsAPI = ArtifactsAPI;
|
|
2380
|
+
exports.ChatAPI = ChatAPI;
|
|
2381
|
+
exports.NotebookLMClient = NotebookLMClient;
|
|
2382
|
+
exports.NotebooksAPI = NotebooksAPI;
|
|
2383
|
+
exports.NotesAPI = NotesAPI;
|
|
2384
|
+
exports.ResearchAPI = ResearchAPI;
|
|
2385
|
+
exports.SettingsAPI = SettingsAPI;
|
|
2386
|
+
exports.SharingAPI = SharingAPI;
|
|
2387
|
+
exports.SourcesAPI = SourcesAPI;
|
|
2388
|
+
exports.connect = connect;
|
|
2389
|
+
//# sourceMappingURL=index.cjs.map
|
|
2390
|
+
//# sourceMappingURL=index.cjs.map
|