notebooklm-sdk 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +252 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +92 -1
- package/dist/index.d.ts +92 -1
- package/dist/index.js +253 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
Generate AI podcasts, chat with documents, run web research, and manage notebooks programmatically — from Node.js, Bun, or Deno.
|
|
10
10
|
|
|
11
|
-
>
|
|
11
|
+
> **Unofficial.** This SDK reverse-engineers the NotebookLM internal API. It may break when Google updates their service. Not affiliated with Google.
|
|
12
12
|
|
|
13
13
|
TypeScript port of [notebooklm-py](https://github.com/teng-lin/notebooklm-py).
|
|
14
14
|
|
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,10 @@ __export(enums_exports, {
|
|
|
21
21
|
ArtifactTypeCode: () => exports.ArtifactTypeCode,
|
|
22
22
|
AudioFormat: () => exports.AudioFormat,
|
|
23
23
|
AudioLength: () => exports.AudioLength,
|
|
24
|
+
ChatGoal: () => exports.ChatGoal,
|
|
25
|
+
ChatMode: () => exports.ChatMode,
|
|
26
|
+
ChatResponseLength: () => exports.ChatResponseLength,
|
|
27
|
+
DriveMimeType: () => exports.DriveMimeType,
|
|
24
28
|
ExportType: () => exports.ExportType,
|
|
25
29
|
InfographicDetail: () => exports.InfographicDetail,
|
|
26
30
|
InfographicOrientation: () => exports.InfographicOrientation,
|
|
@@ -38,6 +42,7 @@ __export(enums_exports, {
|
|
|
38
42
|
VideoStyle: () => exports.VideoStyle,
|
|
39
43
|
artifactStatusFromCode: () => artifactStatusFromCode,
|
|
40
44
|
artifactTypeFromCode: () => artifactTypeFromCode,
|
|
45
|
+
chatModeToParams: () => chatModeToParams,
|
|
41
46
|
sourceStatusFromCode: () => sourceStatusFromCode,
|
|
42
47
|
sourceTypeFromCode: () => sourceTypeFromCode
|
|
43
48
|
});
|
|
@@ -59,7 +64,10 @@ function artifactStatusFromCode(code) {
|
|
|
59
64
|
function sourceStatusFromCode(code) {
|
|
60
65
|
return SOURCE_STATUS_MAP[code] ?? "unknown";
|
|
61
66
|
}
|
|
62
|
-
|
|
67
|
+
function chatModeToParams(mode) {
|
|
68
|
+
return CHAT_MODE_PARAMS[mode];
|
|
69
|
+
}
|
|
70
|
+
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; exports.DriveMimeType = void 0; var SOURCE_TYPE_MAP, ARTIFACT_TYPE_MAP, ARTIFACT_STATUS_MAP, SOURCE_STATUS_MAP; exports.ChatMode = void 0; var CHAT_MODE_PARAMS; exports.ChatGoal = void 0; exports.ChatResponseLength = void 0; exports.ShareAccess = void 0; exports.ShareViewLevel = void 0; exports.SharePermission = void 0;
|
|
63
71
|
var init_enums = __esm({
|
|
64
72
|
"src/types/enums.ts"() {
|
|
65
73
|
exports.RPCMethod = {
|
|
@@ -208,6 +216,12 @@ var init_enums = __esm({
|
|
|
208
216
|
DOCS: 1,
|
|
209
217
|
SHEETS: 2
|
|
210
218
|
};
|
|
219
|
+
exports.DriveMimeType = {
|
|
220
|
+
GOOGLE_DOC: "application/vnd.google-apps.document",
|
|
221
|
+
GOOGLE_SLIDES: "application/vnd.google-apps.presentation",
|
|
222
|
+
GOOGLE_SHEETS: "application/vnd.google-apps.spreadsheet",
|
|
223
|
+
PDF: "application/pdf"
|
|
224
|
+
};
|
|
211
225
|
SOURCE_TYPE_MAP = {
|
|
212
226
|
1: "google_docs",
|
|
213
227
|
2: "google_slides",
|
|
@@ -243,6 +257,38 @@ var init_enums = __esm({
|
|
|
243
257
|
3: "error",
|
|
244
258
|
5: "preparing"
|
|
245
259
|
};
|
|
260
|
+
exports.ChatMode = {
|
|
261
|
+
/** General purpose — balanced length and style. */
|
|
262
|
+
DEFAULT: "default",
|
|
263
|
+
/** Educational focus with longer, learning-oriented responses. */
|
|
264
|
+
LEARNING_GUIDE: "learning_guide",
|
|
265
|
+
/** Short, concise answers. */
|
|
266
|
+
CONCISE: "concise",
|
|
267
|
+
/** Verbose, detailed answers. */
|
|
268
|
+
DETAILED: "detailed"
|
|
269
|
+
};
|
|
270
|
+
CHAT_MODE_PARAMS = {
|
|
271
|
+
default: [1, 1],
|
|
272
|
+
learning_guide: [3, 4],
|
|
273
|
+
concise: [1, 5],
|
|
274
|
+
detailed: [1, 4]
|
|
275
|
+
};
|
|
276
|
+
exports.ChatGoal = {
|
|
277
|
+
/** General purpose research and brainstorming. */
|
|
278
|
+
DEFAULT: 1,
|
|
279
|
+
/** Custom instructions (up to 10,000 characters). */
|
|
280
|
+
CUSTOM: 2,
|
|
281
|
+
/** Educational focus with learning-oriented responses. */
|
|
282
|
+
LEARNING_GUIDE: 3
|
|
283
|
+
};
|
|
284
|
+
exports.ChatResponseLength = {
|
|
285
|
+
/** Standard response length. */
|
|
286
|
+
DEFAULT: 1,
|
|
287
|
+
/** Verbose, detailed responses. */
|
|
288
|
+
LONGER: 4,
|
|
289
|
+
/** Concise, brief responses. */
|
|
290
|
+
SHORTER: 5
|
|
291
|
+
};
|
|
246
292
|
exports.ShareAccess = {
|
|
247
293
|
/** Only explicitly shared users can access */
|
|
248
294
|
RESTRICTED: 0,
|
|
@@ -765,6 +811,30 @@ var ArtifactsAPI = class {
|
|
|
765
811
|
const artifacts = await this.list(notebookId);
|
|
766
812
|
return artifacts.find((a) => a.id === artifactId) ?? null;
|
|
767
813
|
}
|
|
814
|
+
async listAudio(notebookId) {
|
|
815
|
+
return (await this.list(notebookId)).filter((a) => a.kind === "audio");
|
|
816
|
+
}
|
|
817
|
+
async listVideo(notebookId) {
|
|
818
|
+
return (await this.list(notebookId)).filter((a) => a.kind === "video");
|
|
819
|
+
}
|
|
820
|
+
async listReports(notebookId) {
|
|
821
|
+
return (await this.list(notebookId)).filter((a) => a.kind === "report");
|
|
822
|
+
}
|
|
823
|
+
async listQuizzes(notebookId) {
|
|
824
|
+
return (await this.list(notebookId)).filter((a) => a.kind === "quiz");
|
|
825
|
+
}
|
|
826
|
+
async listFlashcards(notebookId) {
|
|
827
|
+
return (await this.list(notebookId)).filter((a) => a.kind === "flashcards");
|
|
828
|
+
}
|
|
829
|
+
async listInfographics(notebookId) {
|
|
830
|
+
return (await this.list(notebookId)).filter((a) => a.kind === "infographic");
|
|
831
|
+
}
|
|
832
|
+
async listSlideDecks(notebookId) {
|
|
833
|
+
return (await this.list(notebookId)).filter((a) => a.kind === "slide_deck");
|
|
834
|
+
}
|
|
835
|
+
async listDataTables(notebookId) {
|
|
836
|
+
return (await this.list(notebookId)).filter((a) => a.kind === "data_table");
|
|
837
|
+
}
|
|
768
838
|
async delete(notebookId, artifactId) {
|
|
769
839
|
const params = [[2], notebookId, artifactId];
|
|
770
840
|
await this.rpc.call(exports.RPCMethod.DELETE_ARTIFACT, params, {
|
|
@@ -1133,6 +1203,39 @@ ${opts.extraInstructions}` : cfg.prompt;
|
|
|
1133
1203
|
if (!url) throw new exports.ArtifactNotReadyError("infographic", { artifactId });
|
|
1134
1204
|
return this._fetchMediaWithCookies(url);
|
|
1135
1205
|
}
|
|
1206
|
+
/** Get AI-suggested report formats based on notebook content. */
|
|
1207
|
+
async suggestReports(notebookId) {
|
|
1208
|
+
const params = [[2], notebookId];
|
|
1209
|
+
const result = await this.rpc.call(exports.RPCMethod.GET_SUGGESTED_REPORTS, params, {
|
|
1210
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1211
|
+
allowNull: true,
|
|
1212
|
+
timeoutMs: 12e4
|
|
1213
|
+
});
|
|
1214
|
+
if (!Array.isArray(result) || !result.length) return [];
|
|
1215
|
+
const items = Array.isArray(result[0]) ? result[0] : result;
|
|
1216
|
+
const suggestions = [];
|
|
1217
|
+
for (const item of items) {
|
|
1218
|
+
if (Array.isArray(item) && item.length >= 5) {
|
|
1219
|
+
suggestions.push({
|
|
1220
|
+
title: typeof item[0] === "string" ? item[0] : "",
|
|
1221
|
+
description: typeof item[1] === "string" ? item[1] : "",
|
|
1222
|
+
prompt: typeof item[4] === "string" ? item[4] : "",
|
|
1223
|
+
audienceLevel: typeof item[5] === "number" ? item[5] : 2
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
return suggestions;
|
|
1228
|
+
}
|
|
1229
|
+
/** Revise an individual slide in a completed slide deck using a prompt. */
|
|
1230
|
+
async reviseSlide(notebookId, artifactId, slideIndex, prompt) {
|
|
1231
|
+
if (slideIndex < 0) throw new Error("slideIndex must be >= 0");
|
|
1232
|
+
const params = [[2], artifactId, [[[slideIndex, prompt]]]];
|
|
1233
|
+
const result = await this.rpc.call(exports.RPCMethod.REVISE_SLIDE, params, {
|
|
1234
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1235
|
+
allowNull: true
|
|
1236
|
+
});
|
|
1237
|
+
return this._parseGenerationResult(result);
|
|
1238
|
+
}
|
|
1136
1239
|
/** Get parsed headers and rows from a completed data table artifact. */
|
|
1137
1240
|
async getDataTableContent(notebookId, artifactId) {
|
|
1138
1241
|
const artifacts = await this._listRaw(notebookId);
|
|
@@ -1356,6 +1459,36 @@ var ChatAPI = class {
|
|
|
1356
1459
|
}
|
|
1357
1460
|
return null;
|
|
1358
1461
|
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Low-level chat configuration. Set goal, response length, and optional
|
|
1464
|
+
* custom instructions directly. Persists on the server per notebook.
|
|
1465
|
+
* Use `setMode()` for preset combinations instead.
|
|
1466
|
+
*/
|
|
1467
|
+
async configure(notebookId, goal, length, customPrompt) {
|
|
1468
|
+
if (goal === exports.ChatGoal.CUSTOM && !customPrompt) {
|
|
1469
|
+
throw new Error("customPrompt is required when goal is ChatGoal.CUSTOM");
|
|
1470
|
+
}
|
|
1471
|
+
const goalArray = goal === exports.ChatGoal.CUSTOM ? [goal, customPrompt] : [goal];
|
|
1472
|
+
const chatSettings = [goalArray, [length]];
|
|
1473
|
+
const params = [notebookId, [[null, null, null, null, null, null, null, chatSettings]]];
|
|
1474
|
+
await this.rpc.call(exports.RPCMethod.RENAME_NOTEBOOK, params, {
|
|
1475
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1476
|
+
allowNull: true
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Set the chat mode for a notebook. Persists on the server — affects all
|
|
1481
|
+
* subsequent `ask()` calls until changed.
|
|
1482
|
+
*/
|
|
1483
|
+
async setMode(notebookId, mode) {
|
|
1484
|
+
const [goal, length] = chatModeToParams(mode);
|
|
1485
|
+
const chatSettings = [[goal], [length]];
|
|
1486
|
+
const params = [notebookId, [[null, null, null, null, null, null, null, chatSettings]]];
|
|
1487
|
+
await this.rpc.call(exports.RPCMethod.RENAME_NOTEBOOK, params, {
|
|
1488
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
1489
|
+
allowNull: true
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1359
1492
|
clearCache(conversationId) {
|
|
1360
1493
|
if (conversationId) {
|
|
1361
1494
|
this.conversationCache.delete(conversationId);
|
|
@@ -1526,6 +1659,9 @@ var NotebooksAPI = class {
|
|
|
1526
1659
|
}
|
|
1527
1660
|
return "";
|
|
1528
1661
|
}
|
|
1662
|
+
async removeFromRecent(notebookId) {
|
|
1663
|
+
await this.rpc.call(exports.RPCMethod.REMOVE_RECENTLY_VIEWED, [notebookId], { allowNull: true });
|
|
1664
|
+
}
|
|
1529
1665
|
async getDescription(notebookId) {
|
|
1530
1666
|
const params = [notebookId, [2]];
|
|
1531
1667
|
const result = await this.rpc.call(exports.RPCMethod.SUMMARIZE, params, {
|
|
@@ -2051,6 +2187,44 @@ var SourcesAPI = class {
|
|
|
2051
2187
|
_typeCode: null
|
|
2052
2188
|
};
|
|
2053
2189
|
}
|
|
2190
|
+
async addDrive(notebookId, fileId, title, mimeType = exports.DriveMimeType.GOOGLE_DOC, opts = {}) {
|
|
2191
|
+
const sourceData = [
|
|
2192
|
+
[fileId, mimeType, 1, title],
|
|
2193
|
+
null,
|
|
2194
|
+
null,
|
|
2195
|
+
null,
|
|
2196
|
+
null,
|
|
2197
|
+
null,
|
|
2198
|
+
null,
|
|
2199
|
+
null,
|
|
2200
|
+
null,
|
|
2201
|
+
null,
|
|
2202
|
+
1
|
|
2203
|
+
];
|
|
2204
|
+
const params = [
|
|
2205
|
+
[sourceData],
|
|
2206
|
+
notebookId,
|
|
2207
|
+
[2],
|
|
2208
|
+
[1, null, null, null, null, null, null, null, null, null, [1]]
|
|
2209
|
+
];
|
|
2210
|
+
const result = await this.rpc.call(exports.RPCMethod.ADD_SOURCE, params, {
|
|
2211
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
2212
|
+
allowNull: true
|
|
2213
|
+
});
|
|
2214
|
+
const sourceId = extractSourceId(result);
|
|
2215
|
+
if (opts.waitUntilReady) {
|
|
2216
|
+
return this.waitUntilReady(notebookId, sourceId, opts.waitTimeout);
|
|
2217
|
+
}
|
|
2218
|
+
return {
|
|
2219
|
+
id: sourceId,
|
|
2220
|
+
title,
|
|
2221
|
+
url: null,
|
|
2222
|
+
kind: "unknown",
|
|
2223
|
+
createdAt: null,
|
|
2224
|
+
status: "processing",
|
|
2225
|
+
_typeCode: null
|
|
2226
|
+
};
|
|
2227
|
+
}
|
|
2054
2228
|
async addFile(notebookId, filePath, mimeType, opts = {}) {
|
|
2055
2229
|
const fileData = fs.readFileSync(filePath);
|
|
2056
2230
|
const fileName = filePath.split("/").pop() ?? "file";
|
|
@@ -2133,6 +2307,74 @@ var SourcesAPI = class {
|
|
|
2133
2307
|
const uploadResult = await uploadResp.text();
|
|
2134
2308
|
return uploadResult.trim();
|
|
2135
2309
|
}
|
|
2310
|
+
/** Get the AI-generated Source Guide (summary + keywords) for a source. */
|
|
2311
|
+
async getGuide(notebookId, sourceId) {
|
|
2312
|
+
const params = [[[[sourceId]]]];
|
|
2313
|
+
const result = await this.rpc.call(exports.RPCMethod.GET_SOURCE_GUIDE, params, {
|
|
2314
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
2315
|
+
allowNull: true,
|
|
2316
|
+
timeoutMs: 12e4
|
|
2317
|
+
});
|
|
2318
|
+
let summary = "";
|
|
2319
|
+
let keywords = [];
|
|
2320
|
+
if (Array.isArray(result) && result.length > 0) {
|
|
2321
|
+
const outer = result[0];
|
|
2322
|
+
if (Array.isArray(outer) && outer.length > 0) {
|
|
2323
|
+
const inner = outer[0];
|
|
2324
|
+
if (Array.isArray(inner)) {
|
|
2325
|
+
if (inner.length > 1 && Array.isArray(inner[1]) && typeof inner[1][0] === "string") {
|
|
2326
|
+
summary = inner[1][0];
|
|
2327
|
+
}
|
|
2328
|
+
if (inner.length > 2 && Array.isArray(inner[2]) && Array.isArray(inner[2][0])) {
|
|
2329
|
+
keywords = inner[2][0].filter((k) => typeof k === "string");
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
return { summary, keywords };
|
|
2335
|
+
}
|
|
2336
|
+
/** Get the full indexed text content of a source. */
|
|
2337
|
+
async getFulltext(notebookId, sourceId) {
|
|
2338
|
+
const params = [[sourceId], [2], [2]];
|
|
2339
|
+
const result = await this.rpc.call(exports.RPCMethod.GET_SOURCE, params, {
|
|
2340
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
2341
|
+
allowNull: true
|
|
2342
|
+
});
|
|
2343
|
+
if (!Array.isArray(result) || !result.length) {
|
|
2344
|
+
throw new Error(`Source ${sourceId} not found in notebook ${notebookId}`);
|
|
2345
|
+
}
|
|
2346
|
+
let title = "";
|
|
2347
|
+
let url = null;
|
|
2348
|
+
if (Array.isArray(result[0]) && result[0].length > 1) {
|
|
2349
|
+
title = typeof result[0][1] === "string" ? result[0][1] : "";
|
|
2350
|
+
const meta = result[0][2];
|
|
2351
|
+
if (Array.isArray(meta) && meta.length > 7 && Array.isArray(meta[7]) && typeof meta[7][0] === "string") {
|
|
2352
|
+
url = meta[7][0];
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
let content = "";
|
|
2356
|
+
if (Array.isArray(result[3]) && result[3].length > 0) {
|
|
2357
|
+
const texts = extractAllText(result[3][0]);
|
|
2358
|
+
content = texts.join("\n");
|
|
2359
|
+
}
|
|
2360
|
+
return { sourceId, title, content, url, charCount: content.length };
|
|
2361
|
+
}
|
|
2362
|
+
/** Check if a source has newer content available. Returns true if fresh, false if stale. */
|
|
2363
|
+
async checkFreshness(notebookId, sourceId) {
|
|
2364
|
+
const params = [null, [sourceId], [2]];
|
|
2365
|
+
const result = await this.rpc.call(exports.RPCMethod.CHECK_SOURCE_FRESHNESS, params, {
|
|
2366
|
+
sourcePath: `/notebook/${notebookId}`,
|
|
2367
|
+
allowNull: true
|
|
2368
|
+
});
|
|
2369
|
+
if (result === true) return true;
|
|
2370
|
+
if (result === false) return false;
|
|
2371
|
+
if (Array.isArray(result)) {
|
|
2372
|
+
if (result.length === 0) return true;
|
|
2373
|
+
const first = result[0];
|
|
2374
|
+
if (Array.isArray(first) && first.length > 1 && first[1] === true) return true;
|
|
2375
|
+
}
|
|
2376
|
+
return false;
|
|
2377
|
+
}
|
|
2136
2378
|
async delete(notebookId, sourceId) {
|
|
2137
2379
|
const params = [notebookId, [sourceId], [2]];
|
|
2138
2380
|
await this.rpc.call(exports.RPCMethod.DELETE_SOURCE, params, {
|
|
@@ -2187,6 +2429,15 @@ function extractSourceId(result) {
|
|
|
2187
2429
|
console.log("extractSourceId debug info: could not parse:", JSON.stringify(result, null, 2));
|
|
2188
2430
|
throw new Error("Could not extract source ID from API response");
|
|
2189
2431
|
}
|
|
2432
|
+
function extractAllText(data, maxDepth = 100) {
|
|
2433
|
+
if (maxDepth <= 0) return [];
|
|
2434
|
+
const texts = [];
|
|
2435
|
+
for (const item of data) {
|
|
2436
|
+
if (typeof item === "string" && item.length > 0) texts.push(item);
|
|
2437
|
+
else if (Array.isArray(item)) texts.push(...extractAllText(item, maxDepth - 1));
|
|
2438
|
+
}
|
|
2439
|
+
return texts;
|
|
2440
|
+
}
|
|
2190
2441
|
function sleep2(ms) {
|
|
2191
2442
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2192
2443
|
}
|