@pan-sec/notebooklm-mcp 1.4.0 → 1.7.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/README.md +61 -7
- package/SECURITY.md +40 -6
- package/dist/auth/mcp-auth.d.ts.map +1 -1
- package/dist/auth/mcp-auth.js +3 -6
- package/dist/auth/mcp-auth.js.map +1 -1
- package/dist/compliance/alert-manager.d.ts +120 -0
- package/dist/compliance/alert-manager.d.ts.map +1 -0
- package/dist/compliance/alert-manager.js +420 -0
- package/dist/compliance/alert-manager.js.map +1 -0
- package/dist/compliance/breach-detection.d.ts +134 -0
- package/dist/compliance/breach-detection.d.ts.map +1 -0
- package/dist/compliance/breach-detection.js +456 -0
- package/dist/compliance/breach-detection.js.map +1 -0
- package/dist/compliance/change-log.d.ts +113 -0
- package/dist/compliance/change-log.d.ts.map +1 -0
- package/dist/compliance/change-log.js +275 -0
- package/dist/compliance/change-log.js.map +1 -0
- package/dist/compliance/compliance-logger.d.ts +136 -0
- package/dist/compliance/compliance-logger.d.ts.map +1 -0
- package/dist/compliance/compliance-logger.js +425 -0
- package/dist/compliance/compliance-logger.js.map +1 -0
- package/dist/compliance/compliance-tools.d.ts +18 -0
- package/dist/compliance/compliance-tools.d.ts.map +1 -0
- package/dist/compliance/compliance-tools.js +673 -0
- package/dist/compliance/compliance-tools.js.map +1 -0
- package/dist/compliance/consent-manager.d.ts +130 -0
- package/dist/compliance/consent-manager.d.ts.map +1 -0
- package/dist/compliance/consent-manager.js +386 -0
- package/dist/compliance/consent-manager.js.map +1 -0
- package/dist/compliance/dashboard.d.ts +243 -0
- package/dist/compliance/dashboard.d.ts.map +1 -0
- package/dist/compliance/dashboard.js +519 -0
- package/dist/compliance/dashboard.js.map +1 -0
- package/dist/compliance/data-classification.d.ts +117 -0
- package/dist/compliance/data-classification.d.ts.map +1 -0
- package/dist/compliance/data-classification.js +469 -0
- package/dist/compliance/data-classification.js.map +1 -0
- package/dist/compliance/data-erasure.d.ts +110 -0
- package/dist/compliance/data-erasure.d.ts.map +1 -0
- package/dist/compliance/data-erasure.js +501 -0
- package/dist/compliance/data-erasure.js.map +1 -0
- package/dist/compliance/data-export.d.ts +85 -0
- package/dist/compliance/data-export.d.ts.map +1 -0
- package/dist/compliance/data-export.js +394 -0
- package/dist/compliance/data-export.js.map +1 -0
- package/dist/compliance/data-inventory.d.ts +136 -0
- package/dist/compliance/data-inventory.d.ts.map +1 -0
- package/dist/compliance/data-inventory.js +335 -0
- package/dist/compliance/data-inventory.js.map +1 -0
- package/dist/compliance/dsar-handler.d.ts +123 -0
- package/dist/compliance/dsar-handler.d.ts.map +1 -0
- package/dist/compliance/dsar-handler.js +371 -0
- package/dist/compliance/dsar-handler.js.map +1 -0
- package/dist/compliance/evidence-collector.d.ts +187 -0
- package/dist/compliance/evidence-collector.d.ts.map +1 -0
- package/dist/compliance/evidence-collector.js +656 -0
- package/dist/compliance/evidence-collector.js.map +1 -0
- package/dist/compliance/health-monitor.d.ts +111 -0
- package/dist/compliance/health-monitor.d.ts.map +1 -0
- package/dist/compliance/health-monitor.js +509 -0
- package/dist/compliance/health-monitor.js.map +1 -0
- package/dist/compliance/incident-manager.d.ts +131 -0
- package/dist/compliance/incident-manager.d.ts.map +1 -0
- package/dist/compliance/incident-manager.js +418 -0
- package/dist/compliance/incident-manager.js.map +1 -0
- package/dist/compliance/index.d.ts +32 -0
- package/dist/compliance/index.d.ts.map +1 -0
- package/dist/compliance/index.js +35 -0
- package/dist/compliance/index.js.map +1 -0
- package/dist/compliance/policy-docs.d.ts +108 -0
- package/dist/compliance/policy-docs.d.ts.map +1 -0
- package/dist/compliance/policy-docs.js +464 -0
- package/dist/compliance/policy-docs.js.map +1 -0
- package/dist/compliance/privacy-notice-text.d.ts +58 -0
- package/dist/compliance/privacy-notice-text.d.ts.map +1 -0
- package/dist/compliance/privacy-notice-text.js +161 -0
- package/dist/compliance/privacy-notice-text.js.map +1 -0
- package/dist/compliance/privacy-notice.d.ts +128 -0
- package/dist/compliance/privacy-notice.d.ts.map +1 -0
- package/dist/compliance/privacy-notice.js +250 -0
- package/dist/compliance/privacy-notice.js.map +1 -0
- package/dist/compliance/report-generator.d.ts +168 -0
- package/dist/compliance/report-generator.d.ts.map +1 -0
- package/dist/compliance/report-generator.js +830 -0
- package/dist/compliance/report-generator.js.map +1 -0
- package/dist/compliance/retention-engine.d.ts +130 -0
- package/dist/compliance/retention-engine.d.ts.map +1 -0
- package/dist/compliance/retention-engine.js +510 -0
- package/dist/compliance/retention-engine.js.map +1 -0
- package/dist/compliance/siem-exporter.d.ts +150 -0
- package/dist/compliance/siem-exporter.d.ts.map +1 -0
- package/dist/compliance/siem-exporter.js +509 -0
- package/dist/compliance/siem-exporter.js.map +1 -0
- package/dist/compliance/types.d.ts +601 -0
- package/dist/compliance/types.d.ts.map +1 -0
- package/dist/compliance/types.js +22 -0
- package/dist/compliance/types.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/events/event-emitter.d.ts +45 -0
- package/dist/events/event-emitter.d.ts.map +1 -0
- package/dist/events/event-emitter.js +100 -0
- package/dist/events/event-emitter.js.map +1 -0
- package/dist/events/event-types.d.ts +124 -0
- package/dist/events/event-types.d.ts.map +1 -0
- package/dist/events/event-types.js +18 -0
- package/dist/events/event-types.js.map +1 -0
- package/dist/index.js +59 -2
- package/dist/index.js.map +1 -1
- package/dist/library/notebook-library.d.ts +25 -2
- package/dist/library/notebook-library.d.ts.map +1 -1
- package/dist/library/notebook-library.js +144 -3
- package/dist/library/notebook-library.js.map +1 -1
- package/dist/library/types.d.ts +15 -0
- package/dist/library/types.d.ts.map +1 -1
- package/dist/notebook-creation/audio-manager.d.ts +56 -0
- package/dist/notebook-creation/audio-manager.d.ts.map +1 -0
- package/dist/notebook-creation/audio-manager.js +335 -0
- package/dist/notebook-creation/audio-manager.js.map +1 -0
- package/dist/notebook-creation/discover-creation-flow.d.ts +8 -0
- package/dist/notebook-creation/discover-creation-flow.d.ts.map +1 -0
- package/dist/notebook-creation/discover-creation-flow.js +177 -0
- package/dist/notebook-creation/discover-creation-flow.js.map +1 -0
- package/dist/notebook-creation/discover-quota.d.ts +8 -0
- package/dist/notebook-creation/discover-quota.d.ts.map +1 -0
- package/dist/notebook-creation/discover-quota.js +195 -0
- package/dist/notebook-creation/discover-quota.js.map +1 -0
- package/dist/notebook-creation/discover-source-dialog.d.ts +8 -0
- package/dist/notebook-creation/discover-source-dialog.d.ts.map +1 -0
- package/dist/notebook-creation/discover-source-dialog.js +134 -0
- package/dist/notebook-creation/discover-source-dialog.js.map +1 -0
- package/dist/notebook-creation/discover-sources.d.ts +8 -0
- package/dist/notebook-creation/discover-sources.d.ts.map +1 -0
- package/dist/notebook-creation/discover-sources.js +273 -0
- package/dist/notebook-creation/discover-sources.js.map +1 -0
- package/dist/notebook-creation/discover-text-input.d.ts +7 -0
- package/dist/notebook-creation/discover-text-input.d.ts.map +1 -0
- package/dist/notebook-creation/discover-text-input.js +135 -0
- package/dist/notebook-creation/discover-text-input.js.map +1 -0
- package/dist/notebook-creation/index.d.ts +12 -0
- package/dist/notebook-creation/index.d.ts.map +1 -0
- package/dist/notebook-creation/index.js +12 -0
- package/dist/notebook-creation/index.js.map +1 -0
- package/dist/notebook-creation/notebook-creator.d.ts +95 -0
- package/dist/notebook-creation/notebook-creator.d.ts.map +1 -0
- package/dist/notebook-creation/notebook-creator.js +689 -0
- package/dist/notebook-creation/notebook-creator.js.map +1 -0
- package/dist/notebook-creation/notebook-sync.d.ts +93 -0
- package/dist/notebook-creation/notebook-sync.d.ts.map +1 -0
- package/dist/notebook-creation/notebook-sync.js +370 -0
- package/dist/notebook-creation/notebook-sync.js.map +1 -0
- package/dist/notebook-creation/run-discovery.d.ts +11 -0
- package/dist/notebook-creation/run-discovery.d.ts.map +1 -0
- package/dist/notebook-creation/run-discovery.js +151 -0
- package/dist/notebook-creation/run-discovery.js.map +1 -0
- package/dist/notebook-creation/selector-discovery.d.ts +65 -0
- package/dist/notebook-creation/selector-discovery.d.ts.map +1 -0
- package/dist/notebook-creation/selector-discovery.js +421 -0
- package/dist/notebook-creation/selector-discovery.js.map +1 -0
- package/dist/notebook-creation/selectors.d.ts +150 -0
- package/dist/notebook-creation/selectors.d.ts.map +1 -0
- package/dist/notebook-creation/selectors.js +225 -0
- package/dist/notebook-creation/selectors.js.map +1 -0
- package/dist/notebook-creation/source-manager.d.ts +73 -0
- package/dist/notebook-creation/source-manager.d.ts.map +1 -0
- package/dist/notebook-creation/source-manager.js +486 -0
- package/dist/notebook-creation/source-manager.js.map +1 -0
- package/dist/notebook-creation/test-create.d.ts +8 -0
- package/dist/notebook-creation/test-create.d.ts.map +1 -0
- package/dist/notebook-creation/test-create.js +72 -0
- package/dist/notebook-creation/test-create.js.map +1 -0
- package/dist/notebook-creation/types.d.ts +173 -0
- package/dist/notebook-creation/types.d.ts.map +1 -0
- package/dist/notebook-creation/types.js +5 -0
- package/dist/notebook-creation/types.js.map +1 -0
- package/dist/quota/index.d.ts +8 -0
- package/dist/quota/index.d.ts.map +1 -0
- package/dist/quota/index.js +8 -0
- package/dist/quota/index.js.map +1 -0
- package/dist/quota/quota-manager.d.ts +125 -0
- package/dist/quota/quota-manager.d.ts.map +1 -0
- package/dist/quota/quota-manager.js +330 -0
- package/dist/quota/quota-manager.js.map +1 -0
- package/dist/session/session-manager.d.ts +5 -0
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manager.js +6 -0
- package/dist/session/session-manager.js.map +1 -1
- package/dist/session/shared-context-manager.d.ts.map +1 -1
- package/dist/session/shared-context-manager.js +2 -1
- package/dist/session/shared-context-manager.js.map +1 -1
- package/dist/tools/definitions/notebook-management.d.ts.map +1 -1
- package/dist/tools/definitions/notebook-management.js +525 -0
- package/dist/tools/definitions/notebook-management.js.map +1 -1
- package/dist/tools/definitions/system.d.ts.map +1 -1
- package/dist/tools/definitions/system.js +158 -0
- package/dist/tools/definitions/system.js.map +1 -1
- package/dist/tools/handlers.d.ts +225 -0
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +911 -0
- package/dist/tools/handlers.js.map +1 -1
- package/dist/utils/audit-logger.d.ts +21 -1
- package/dist/utils/audit-logger.d.ts.map +1 -1
- package/dist/utils/audit-logger.js +53 -4
- package/dist/utils/audit-logger.js.map +1 -1
- package/dist/utils/crypto.d.ts.map +1 -1
- package/dist/utils/crypto.js +8 -15
- package/dist/utils/crypto.js.map +1 -1
- package/dist/utils/file-permissions.d.ts +85 -0
- package/dist/utils/file-permissions.d.ts.map +1 -0
- package/dist/utils/file-permissions.js +180 -0
- package/dist/utils/file-permissions.js.map +1 -0
- package/dist/utils/settings-manager.d.ts.map +1 -1
- package/dist/utils/settings-manager.js +6 -11
- package/dist/utils/settings-manager.js.map +1 -1
- package/dist/webhooks/index.d.ts +8 -0
- package/dist/webhooks/index.d.ts.map +1 -0
- package/dist/webhooks/index.js +8 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/types.d.ts +57 -0
- package/dist/webhooks/types.d.ts.map +1 -0
- package/dist/webhooks/types.js +5 -0
- package/dist/webhooks/types.js.map +1 -0
- package/dist/webhooks/webhook-dispatcher.d.ts +120 -0
- package/dist/webhooks/webhook-dispatcher.d.ts.map +1 -0
- package/dist/webhooks/webhook-dispatcher.js +519 -0
- package/dist/webhooks/webhook-dispatcher.js.map +1 -0
- package/docs/COMPLIANCE-SPEC.md +1452 -0
- package/package.json +30 -4
package/dist/tools/handlers.js
CHANGED
|
@@ -10,6 +10,12 @@ import { validateNotebookUrl, validateNotebookId, validateSessionId, validateQue
|
|
|
10
10
|
import { audit } from "../utils/audit-logger.js";
|
|
11
11
|
import { validateResponse } from "../utils/response-validator.js";
|
|
12
12
|
import { CleanupManager } from "../utils/cleanup-manager.js";
|
|
13
|
+
import { NotebookCreator } from "../notebook-creation/notebook-creator.js";
|
|
14
|
+
import { NotebookSync } from "../notebook-creation/notebook-sync.js";
|
|
15
|
+
import { SourceManager } from "../notebook-creation/source-manager.js";
|
|
16
|
+
import { AudioManager } from "../notebook-creation/audio-manager.js";
|
|
17
|
+
import { getWebhookDispatcher } from "../webhooks/index.js";
|
|
18
|
+
import { getQuotaManager } from "../quota/index.js";
|
|
13
19
|
const FOLLOW_UP_REMINDER = "\n\nEXTREMELY IMPORTANT: Is that ALL you need to know? You can always ask another question using the same session ID! Think about it carefully: before you reply to the user, review their original request and this answer. If anything is still unclear or missing, ask me another question first.";
|
|
14
20
|
/**
|
|
15
21
|
* MCP Tool Handlers
|
|
@@ -71,6 +77,17 @@ export class ToolHandlers {
|
|
|
71
77
|
error: `Rate limit exceeded. Please wait before making more requests. Remaining: ${this.rateLimiter.getRemaining(rateLimitKey)}`,
|
|
72
78
|
};
|
|
73
79
|
}
|
|
80
|
+
// === QUOTA CHECK ===
|
|
81
|
+
const quotaManager = getQuotaManager();
|
|
82
|
+
const canQuery = quotaManager.canMakeQuery();
|
|
83
|
+
if (!canQuery.allowed) {
|
|
84
|
+
log.warning(`⚠️ Quota limit: ${canQuery.reason}`);
|
|
85
|
+
await audit.tool("ask_question", args, false, Date.now() - startTime, canQuery.reason || "Query quota exceeded");
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
error: canQuery.reason || "Daily query limit reached. Try again tomorrow or upgrade your plan.",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
74
91
|
}
|
|
75
92
|
catch (error) {
|
|
76
93
|
if (error instanceof SecurityError) {
|
|
@@ -173,6 +190,8 @@ export class ToolHandlers {
|
|
|
173
190
|
// Progress: Complete
|
|
174
191
|
await sendProgress?.("Question answered successfully!", 5, 5);
|
|
175
192
|
log.success(`✅ [TOOL] ask_question completed successfully`);
|
|
193
|
+
// Update quota tracking
|
|
194
|
+
getQuotaManager().incrementQueryCount();
|
|
176
195
|
// Audit: successful tool call
|
|
177
196
|
await audit.tool("ask_question", {
|
|
178
197
|
question_length: safeQuestion.length,
|
|
@@ -733,6 +752,427 @@ export class ToolHandlers {
|
|
|
733
752
|
};
|
|
734
753
|
}
|
|
735
754
|
}
|
|
755
|
+
/**
|
|
756
|
+
* Handle export_library tool
|
|
757
|
+
*
|
|
758
|
+
* Exports notebook library to a backup file (JSON or CSV).
|
|
759
|
+
*/
|
|
760
|
+
async handleExportLibrary(args) {
|
|
761
|
+
const format = args.format || "json";
|
|
762
|
+
log.info(`🔧 [TOOL] export_library called`);
|
|
763
|
+
log.info(` Format: ${format}`);
|
|
764
|
+
try {
|
|
765
|
+
const notebooks = this.library.listNotebooks();
|
|
766
|
+
const stats = this.library.getStats();
|
|
767
|
+
// Generate default output path if not provided
|
|
768
|
+
const date = new Date().toISOString().split("T")[0];
|
|
769
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
770
|
+
const defaultPath = `${homeDir}/notebooklm-library-backup-${date}.${format}`;
|
|
771
|
+
const outputPath = args.output_path || defaultPath;
|
|
772
|
+
let content;
|
|
773
|
+
if (format === "csv") {
|
|
774
|
+
// CSV format: name, url, topics, last_used, use_count
|
|
775
|
+
const headers = ["name", "url", "topics", "description", "last_used", "use_count"];
|
|
776
|
+
const rows = notebooks.map((nb) => [
|
|
777
|
+
`"${(nb.name || "").replace(/"/g, '""')}"`,
|
|
778
|
+
`"${nb.url}"`,
|
|
779
|
+
`"${(nb.topics || []).join("; ")}"`,
|
|
780
|
+
`"${(nb.description || "").replace(/"/g, '""')}"`,
|
|
781
|
+
nb.last_used || "",
|
|
782
|
+
String(nb.use_count || 0),
|
|
783
|
+
]);
|
|
784
|
+
content = [headers.join(","), ...rows.map((r) => r.join(","))].join("\n");
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
// JSON format: full library data
|
|
788
|
+
content = JSON.stringify({
|
|
789
|
+
exported_at: new Date().toISOString(),
|
|
790
|
+
version: "1.0",
|
|
791
|
+
stats: {
|
|
792
|
+
total_notebooks: stats.total_notebooks,
|
|
793
|
+
total_queries: stats.total_queries,
|
|
794
|
+
},
|
|
795
|
+
notebooks: notebooks,
|
|
796
|
+
}, null, 2);
|
|
797
|
+
}
|
|
798
|
+
// Write file with secure permissions
|
|
799
|
+
const fs = await import("fs");
|
|
800
|
+
fs.writeFileSync(outputPath, content, { mode: 0o600 });
|
|
801
|
+
const fileStats = fs.statSync(outputPath);
|
|
802
|
+
log.success(`✅ [TOOL] export_library completed: ${outputPath}`);
|
|
803
|
+
return {
|
|
804
|
+
success: true,
|
|
805
|
+
data: {
|
|
806
|
+
file_path: outputPath,
|
|
807
|
+
format,
|
|
808
|
+
notebook_count: notebooks.length,
|
|
809
|
+
size_bytes: fileStats.size,
|
|
810
|
+
},
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
catch (error) {
|
|
814
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
815
|
+
log.error(`❌ [TOOL] export_library failed: ${errorMessage}`);
|
|
816
|
+
return {
|
|
817
|
+
success: false,
|
|
818
|
+
error: errorMessage,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Handle get_project_info tool
|
|
824
|
+
*
|
|
825
|
+
* Returns current project context and library location.
|
|
826
|
+
*/
|
|
827
|
+
async handleGetProjectInfo() {
|
|
828
|
+
log.info(`🔧 [TOOL] get_project_info called`);
|
|
829
|
+
try {
|
|
830
|
+
// Get info from the library instance
|
|
831
|
+
const projectInfo = this.library.getProjectInfo();
|
|
832
|
+
const libraryPath = this.library.getLibraryPath();
|
|
833
|
+
const isProjectLibrary = this.library.isProjectLibrary();
|
|
834
|
+
// Also detect what project would be detected from cwd
|
|
835
|
+
const { NotebookLibrary: NL } = await import("../library/notebook-library.js");
|
|
836
|
+
const detectedProject = NL.detectCurrentProject();
|
|
837
|
+
log.success(`✅ [TOOL] get_project_info completed`);
|
|
838
|
+
return {
|
|
839
|
+
success: true,
|
|
840
|
+
data: {
|
|
841
|
+
project: projectInfo,
|
|
842
|
+
library_path: libraryPath,
|
|
843
|
+
is_project_library: isProjectLibrary,
|
|
844
|
+
detected_project: detectedProject,
|
|
845
|
+
},
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
catch (error) {
|
|
849
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
850
|
+
log.error(`❌ [TOOL] get_project_info failed: ${errorMessage}`);
|
|
851
|
+
return {
|
|
852
|
+
success: false,
|
|
853
|
+
error: errorMessage,
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Handle get_quota tool
|
|
859
|
+
*
|
|
860
|
+
* Returns current quota status including license tier, usage, and limits.
|
|
861
|
+
*/
|
|
862
|
+
async handleGetQuota() {
|
|
863
|
+
log.info(`🔧 [TOOL] get_quota called`);
|
|
864
|
+
try {
|
|
865
|
+
const quotaManager = getQuotaManager();
|
|
866
|
+
const status = quotaManager.getStatus();
|
|
867
|
+
const settings = quotaManager.getSettings();
|
|
868
|
+
log.success(`✅ [TOOL] get_quota completed (tier: ${status.tier})`);
|
|
869
|
+
return {
|
|
870
|
+
success: true,
|
|
871
|
+
data: {
|
|
872
|
+
tier: status.tier,
|
|
873
|
+
notebooks: status.notebooks,
|
|
874
|
+
sources: status.sources,
|
|
875
|
+
queries: status.queries,
|
|
876
|
+
auto_detected: settings.autoDetected,
|
|
877
|
+
last_updated: settings.usage.lastUpdated,
|
|
878
|
+
},
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
catch (error) {
|
|
882
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
883
|
+
log.error(`❌ [TOOL] get_quota failed: ${errorMessage}`);
|
|
884
|
+
return {
|
|
885
|
+
success: false,
|
|
886
|
+
error: errorMessage,
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Handle set_quota_tier tool
|
|
892
|
+
*
|
|
893
|
+
* Manually set the license tier to override auto-detection.
|
|
894
|
+
*/
|
|
895
|
+
async handleSetQuotaTier(args) {
|
|
896
|
+
log.info(`🔧 [TOOL] set_quota_tier called`);
|
|
897
|
+
log.info(` Tier: ${args.tier}`);
|
|
898
|
+
try {
|
|
899
|
+
const quotaManager = getQuotaManager();
|
|
900
|
+
quotaManager.setTier(args.tier);
|
|
901
|
+
const settings = quotaManager.getSettings();
|
|
902
|
+
log.success(`✅ [TOOL] set_quota_tier completed (tier: ${args.tier})`);
|
|
903
|
+
return {
|
|
904
|
+
success: true,
|
|
905
|
+
data: {
|
|
906
|
+
tier: settings.tier,
|
|
907
|
+
limits: {
|
|
908
|
+
notebooks: settings.limits.notebooks,
|
|
909
|
+
sourcesPerNotebook: settings.limits.sourcesPerNotebook,
|
|
910
|
+
queriesPerDay: settings.limits.queriesPerDay,
|
|
911
|
+
},
|
|
912
|
+
message: `License tier set to ${args.tier}. Limits updated accordingly.`,
|
|
913
|
+
},
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
catch (error) {
|
|
917
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
918
|
+
log.error(`❌ [TOOL] set_quota_tier failed: ${errorMessage}`);
|
|
919
|
+
return {
|
|
920
|
+
success: false,
|
|
921
|
+
error: errorMessage,
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Handle create_notebook tool
|
|
927
|
+
*
|
|
928
|
+
* Creates a new NotebookLM notebook with sources programmatically.
|
|
929
|
+
*/
|
|
930
|
+
async handleCreateNotebook(args, sendProgress) {
|
|
931
|
+
log.info(`🔧 [TOOL] create_notebook called`);
|
|
932
|
+
log.info(` Name: ${args.name}`);
|
|
933
|
+
log.info(` Sources: ${args.sources?.length || 0}`);
|
|
934
|
+
try {
|
|
935
|
+
// Validate inputs
|
|
936
|
+
if (!args.name || typeof args.name !== "string") {
|
|
937
|
+
throw new Error("Notebook name is required");
|
|
938
|
+
}
|
|
939
|
+
if (!args.sources || !Array.isArray(args.sources) || args.sources.length === 0) {
|
|
940
|
+
throw new Error("At least one source is required");
|
|
941
|
+
}
|
|
942
|
+
// Validate each source
|
|
943
|
+
for (const source of args.sources) {
|
|
944
|
+
if (!source.type || !["url", "text", "file"].includes(source.type)) {
|
|
945
|
+
throw new Error(`Invalid source type: ${source.type}. Must be url, text, or file.`);
|
|
946
|
+
}
|
|
947
|
+
if (!source.value || typeof source.value !== "string") {
|
|
948
|
+
throw new Error("Source value is required");
|
|
949
|
+
}
|
|
950
|
+
if (source.type === "url") {
|
|
951
|
+
try {
|
|
952
|
+
new URL(source.value);
|
|
953
|
+
}
|
|
954
|
+
catch {
|
|
955
|
+
throw new Error(`Invalid URL: ${source.value}`);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
// === QUOTA CHECK ===
|
|
960
|
+
const quotaManager = getQuotaManager();
|
|
961
|
+
const canCreate = quotaManager.canCreateNotebook();
|
|
962
|
+
if (!canCreate.allowed) {
|
|
963
|
+
log.warning(`⚠️ Quota limit: ${canCreate.reason}`);
|
|
964
|
+
return {
|
|
965
|
+
success: false,
|
|
966
|
+
error: canCreate.reason || "Notebook quota limit reached",
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
// Check source limit
|
|
970
|
+
const sourceLimits = quotaManager.getLimits();
|
|
971
|
+
if (args.sources.length > sourceLimits.sourcesPerNotebook) {
|
|
972
|
+
const reason = `Too many sources (${args.sources.length}). Limit is ${sourceLimits.sourcesPerNotebook} per notebook.`;
|
|
973
|
+
log.warning(`⚠️ Quota limit: ${reason}`);
|
|
974
|
+
return {
|
|
975
|
+
success: false,
|
|
976
|
+
error: reason,
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
// Get the shared context manager from session manager
|
|
980
|
+
const contextManager = this.sessionManager.getContextManager();
|
|
981
|
+
// Create notebook
|
|
982
|
+
const creator = new NotebookCreator(this.authManager, contextManager);
|
|
983
|
+
const result = await creator.createNotebook({
|
|
984
|
+
name: args.name,
|
|
985
|
+
sources: args.sources,
|
|
986
|
+
sendProgress,
|
|
987
|
+
browserOptions: args.browser_options || (args.show_browser ? { show: true } : undefined),
|
|
988
|
+
});
|
|
989
|
+
// Auto-add to library if requested (default: true)
|
|
990
|
+
if (args.auto_add_to_library !== false) {
|
|
991
|
+
try {
|
|
992
|
+
this.library.addNotebook({
|
|
993
|
+
url: result.url,
|
|
994
|
+
name: args.name,
|
|
995
|
+
description: args.description || `Created ${new Date().toLocaleDateString()}`,
|
|
996
|
+
topics: args.topics || [],
|
|
997
|
+
});
|
|
998
|
+
log.success(`✅ Added notebook to library: ${args.name}`);
|
|
999
|
+
}
|
|
1000
|
+
catch (libError) {
|
|
1001
|
+
log.warning(`⚠️ Failed to add to library: ${libError}`);
|
|
1002
|
+
// Don't fail the whole operation
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
// Update quota tracking
|
|
1006
|
+
quotaManager.incrementNotebookCount();
|
|
1007
|
+
// Audit log
|
|
1008
|
+
await audit.tool("create_notebook", {
|
|
1009
|
+
name: args.name,
|
|
1010
|
+
sourceCount: args.sources.length,
|
|
1011
|
+
url: result.url,
|
|
1012
|
+
}, true, 0);
|
|
1013
|
+
log.success(`✅ [TOOL] create_notebook completed: ${result.url}`);
|
|
1014
|
+
return {
|
|
1015
|
+
success: true,
|
|
1016
|
+
data: result,
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
catch (error) {
|
|
1020
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1021
|
+
log.error(`❌ [TOOL] create_notebook failed: ${errorMessage}`);
|
|
1022
|
+
await audit.tool("create_notebook", {
|
|
1023
|
+
name: args.name,
|
|
1024
|
+
error: errorMessage,
|
|
1025
|
+
}, false, 0, errorMessage);
|
|
1026
|
+
return {
|
|
1027
|
+
success: false,
|
|
1028
|
+
error: errorMessage,
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Handle batch_create_notebooks tool
|
|
1034
|
+
*
|
|
1035
|
+
* Creates multiple notebooks in a single batch operation.
|
|
1036
|
+
*/
|
|
1037
|
+
async handleBatchCreateNotebooks(args, sendProgress) {
|
|
1038
|
+
log.info(`🔧 [TOOL] batch_create_notebooks called`);
|
|
1039
|
+
log.info(` Notebooks: ${args.notebooks.length}`);
|
|
1040
|
+
log.info(` Stop on error: ${args.stop_on_error || false}`);
|
|
1041
|
+
try {
|
|
1042
|
+
// Validate input
|
|
1043
|
+
if (!args.notebooks || !Array.isArray(args.notebooks)) {
|
|
1044
|
+
throw new Error("notebooks array is required");
|
|
1045
|
+
}
|
|
1046
|
+
if (args.notebooks.length === 0) {
|
|
1047
|
+
throw new Error("At least one notebook is required");
|
|
1048
|
+
}
|
|
1049
|
+
if (args.notebooks.length > 10) {
|
|
1050
|
+
throw new Error("Maximum 10 notebooks per batch");
|
|
1051
|
+
}
|
|
1052
|
+
const results = [];
|
|
1053
|
+
const total = args.notebooks.length;
|
|
1054
|
+
let succeeded = 0;
|
|
1055
|
+
let failed = 0;
|
|
1056
|
+
for (let i = 0; i < args.notebooks.length; i++) {
|
|
1057
|
+
const notebook = args.notebooks[i];
|
|
1058
|
+
await sendProgress?.(`Creating notebook ${i + 1}/${total}: ${notebook.name}`, i, total);
|
|
1059
|
+
log.info(` 📓 Creating notebook ${i + 1}/${total}: ${notebook.name}`);
|
|
1060
|
+
try {
|
|
1061
|
+
const result = await this.handleCreateNotebook({
|
|
1062
|
+
name: notebook.name,
|
|
1063
|
+
sources: notebook.sources,
|
|
1064
|
+
description: notebook.description,
|
|
1065
|
+
topics: notebook.topics,
|
|
1066
|
+
auto_add_to_library: true,
|
|
1067
|
+
show_browser: args.show_browser,
|
|
1068
|
+
});
|
|
1069
|
+
if (result.success && result.data) {
|
|
1070
|
+
results.push({
|
|
1071
|
+
name: notebook.name,
|
|
1072
|
+
success: true,
|
|
1073
|
+
url: result.data.url,
|
|
1074
|
+
});
|
|
1075
|
+
succeeded++;
|
|
1076
|
+
log.success(` ✅ Created: ${result.data.url}`);
|
|
1077
|
+
}
|
|
1078
|
+
else {
|
|
1079
|
+
results.push({
|
|
1080
|
+
name: notebook.name,
|
|
1081
|
+
success: false,
|
|
1082
|
+
error: result.error || "Unknown error",
|
|
1083
|
+
});
|
|
1084
|
+
failed++;
|
|
1085
|
+
log.error(` ❌ Failed: ${result.error}`);
|
|
1086
|
+
if (args.stop_on_error) {
|
|
1087
|
+
log.warning(` ⚠️ Stopping batch due to error (stop_on_error=true)`);
|
|
1088
|
+
break;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
catch (error) {
|
|
1093
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1094
|
+
results.push({
|
|
1095
|
+
name: notebook.name,
|
|
1096
|
+
success: false,
|
|
1097
|
+
error: errorMessage,
|
|
1098
|
+
});
|
|
1099
|
+
failed++;
|
|
1100
|
+
log.error(` ❌ Exception: ${errorMessage}`);
|
|
1101
|
+
if (args.stop_on_error) {
|
|
1102
|
+
log.warning(` ⚠️ Stopping batch due to exception (stop_on_error=true)`);
|
|
1103
|
+
break;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
// Delay between notebooks to avoid rate limiting
|
|
1107
|
+
if (i < args.notebooks.length - 1) {
|
|
1108
|
+
const delay = 2000 + Math.random() * 2000; // 2-4 seconds
|
|
1109
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
await sendProgress?.(`Batch complete: ${succeeded}/${total} succeeded`, total, total);
|
|
1113
|
+
log.success(`✅ [TOOL] batch_create_notebooks completed: ${succeeded}/${total} succeeded`);
|
|
1114
|
+
return {
|
|
1115
|
+
success: failed === 0,
|
|
1116
|
+
data: {
|
|
1117
|
+
total,
|
|
1118
|
+
succeeded,
|
|
1119
|
+
failed,
|
|
1120
|
+
results,
|
|
1121
|
+
},
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
catch (error) {
|
|
1125
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1126
|
+
log.error(`❌ [TOOL] batch_create_notebooks failed: ${errorMessage}`);
|
|
1127
|
+
return {
|
|
1128
|
+
success: false,
|
|
1129
|
+
error: errorMessage,
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Handle sync_library tool
|
|
1135
|
+
*
|
|
1136
|
+
* Syncs local library with actual NotebookLM notebooks.
|
|
1137
|
+
*/
|
|
1138
|
+
async handleSyncLibrary(args) {
|
|
1139
|
+
log.info(`🔧 [TOOL] sync_library called`);
|
|
1140
|
+
log.info(` Auto-fix: ${args.auto_fix || false}`);
|
|
1141
|
+
log.info(` Show browser: ${args.show_browser || false}`);
|
|
1142
|
+
try {
|
|
1143
|
+
// Get the shared context manager from session manager
|
|
1144
|
+
const contextManager = this.sessionManager.getContextManager();
|
|
1145
|
+
// Sync library
|
|
1146
|
+
const sync = new NotebookSync(this.authManager, contextManager, this.library);
|
|
1147
|
+
const result = await sync.syncLibrary({
|
|
1148
|
+
autoFix: args.auto_fix,
|
|
1149
|
+
showBrowser: args.show_browser,
|
|
1150
|
+
});
|
|
1151
|
+
// Audit log
|
|
1152
|
+
await audit.tool("sync_library", {
|
|
1153
|
+
matched: result.matched.length,
|
|
1154
|
+
stale: result.staleEntries.length,
|
|
1155
|
+
missing: result.missingNotebooks.length,
|
|
1156
|
+
autoFix: args.auto_fix,
|
|
1157
|
+
}, true, 0);
|
|
1158
|
+
log.success(`✅ [TOOL] sync_library completed`);
|
|
1159
|
+
return {
|
|
1160
|
+
success: true,
|
|
1161
|
+
data: result,
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
catch (error) {
|
|
1165
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1166
|
+
log.error(`❌ [TOOL] sync_library failed: ${errorMessage}`);
|
|
1167
|
+
await audit.tool("sync_library", {
|
|
1168
|
+
error: errorMessage,
|
|
1169
|
+
}, false, 0, errorMessage);
|
|
1170
|
+
return {
|
|
1171
|
+
success: false,
|
|
1172
|
+
error: errorMessage,
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
736
1176
|
/**
|
|
737
1177
|
* Handle cleanup_data tool
|
|
738
1178
|
*
|
|
@@ -801,6 +1241,477 @@ export class ToolHandlers {
|
|
|
801
1241
|
};
|
|
802
1242
|
}
|
|
803
1243
|
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Handle list_sources tool
|
|
1246
|
+
*
|
|
1247
|
+
* List all sources in a NotebookLM notebook.
|
|
1248
|
+
*/
|
|
1249
|
+
async handleListSources(args) {
|
|
1250
|
+
log.info(`🔧 [TOOL] list_sources called`);
|
|
1251
|
+
try {
|
|
1252
|
+
// Resolve notebook URL
|
|
1253
|
+
let notebookUrl = args.notebook_url;
|
|
1254
|
+
if (!notebookUrl && args.notebook_id) {
|
|
1255
|
+
const notebook = this.library.getNotebook(args.notebook_id);
|
|
1256
|
+
if (!notebook) {
|
|
1257
|
+
throw new Error(`Notebook not found in library: ${args.notebook_id}`);
|
|
1258
|
+
}
|
|
1259
|
+
notebookUrl = notebook.url;
|
|
1260
|
+
log.info(` Resolved notebook: ${notebook.name}`);
|
|
1261
|
+
}
|
|
1262
|
+
else if (!notebookUrl) {
|
|
1263
|
+
const active = this.library.getActiveNotebook();
|
|
1264
|
+
if (active) {
|
|
1265
|
+
notebookUrl = active.url;
|
|
1266
|
+
log.info(` Using active notebook: ${active.name}`);
|
|
1267
|
+
}
|
|
1268
|
+
else {
|
|
1269
|
+
throw new Error("No notebook specified. Provide notebook_id or notebook_url.");
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
// Validate URL
|
|
1273
|
+
const safeUrl = validateNotebookUrl(notebookUrl);
|
|
1274
|
+
// Get the shared context manager from session manager
|
|
1275
|
+
const contextManager = this.sessionManager.getContextManager();
|
|
1276
|
+
// List sources
|
|
1277
|
+
const sourceManager = new SourceManager(this.authManager, contextManager);
|
|
1278
|
+
const result = await sourceManager.listSources(safeUrl);
|
|
1279
|
+
log.success(`✅ [TOOL] list_sources completed (${result.count} sources)`);
|
|
1280
|
+
return {
|
|
1281
|
+
success: true,
|
|
1282
|
+
data: result,
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
catch (error) {
|
|
1286
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1287
|
+
log.error(`❌ [TOOL] list_sources failed: ${errorMessage}`);
|
|
1288
|
+
return {
|
|
1289
|
+
success: false,
|
|
1290
|
+
error: errorMessage,
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Handle add_source tool
|
|
1296
|
+
*
|
|
1297
|
+
* Add a source to an existing NotebookLM notebook.
|
|
1298
|
+
*/
|
|
1299
|
+
async handleAddSource(args) {
|
|
1300
|
+
log.info(`🔧 [TOOL] add_source called`);
|
|
1301
|
+
log.info(` Source type: ${args.source?.type}`);
|
|
1302
|
+
try {
|
|
1303
|
+
// Validate source
|
|
1304
|
+
if (!args.source || !args.source.type || !args.source.value) {
|
|
1305
|
+
throw new Error("Source with type and value is required");
|
|
1306
|
+
}
|
|
1307
|
+
if (!["url", "text", "file"].includes(args.source.type)) {
|
|
1308
|
+
throw new Error(`Invalid source type: ${args.source.type}. Must be url, text, or file.`);
|
|
1309
|
+
}
|
|
1310
|
+
// Resolve notebook URL
|
|
1311
|
+
let notebookUrl = args.notebook_url;
|
|
1312
|
+
if (!notebookUrl && args.notebook_id) {
|
|
1313
|
+
const notebook = this.library.getNotebook(args.notebook_id);
|
|
1314
|
+
if (!notebook) {
|
|
1315
|
+
throw new Error(`Notebook not found in library: ${args.notebook_id}`);
|
|
1316
|
+
}
|
|
1317
|
+
notebookUrl = notebook.url;
|
|
1318
|
+
log.info(` Resolved notebook: ${notebook.name}`);
|
|
1319
|
+
}
|
|
1320
|
+
else if (!notebookUrl) {
|
|
1321
|
+
const active = this.library.getActiveNotebook();
|
|
1322
|
+
if (active) {
|
|
1323
|
+
notebookUrl = active.url;
|
|
1324
|
+
log.info(` Using active notebook: ${active.name}`);
|
|
1325
|
+
}
|
|
1326
|
+
else {
|
|
1327
|
+
throw new Error("No notebook specified. Provide notebook_id or notebook_url.");
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
// Validate URL
|
|
1331
|
+
const safeUrl = validateNotebookUrl(notebookUrl);
|
|
1332
|
+
// Get the shared context manager from session manager
|
|
1333
|
+
const contextManager = this.sessionManager.getContextManager();
|
|
1334
|
+
// Add source
|
|
1335
|
+
const sourceManager = new SourceManager(this.authManager, contextManager);
|
|
1336
|
+
const result = await sourceManager.addSource(safeUrl, args.source);
|
|
1337
|
+
if (result.success) {
|
|
1338
|
+
log.success(`✅ [TOOL] add_source completed`);
|
|
1339
|
+
}
|
|
1340
|
+
else {
|
|
1341
|
+
log.warning(`⚠️ [TOOL] add_source failed: ${result.error}`);
|
|
1342
|
+
}
|
|
1343
|
+
return {
|
|
1344
|
+
success: result.success,
|
|
1345
|
+
data: result,
|
|
1346
|
+
...(result.error && { error: result.error }),
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
catch (error) {
|
|
1350
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1351
|
+
log.error(`❌ [TOOL] add_source failed: ${errorMessage}`);
|
|
1352
|
+
return {
|
|
1353
|
+
success: false,
|
|
1354
|
+
error: errorMessage,
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Handle remove_source tool
|
|
1360
|
+
*
|
|
1361
|
+
* Remove a source from a NotebookLM notebook.
|
|
1362
|
+
*/
|
|
1363
|
+
async handleRemoveSource(args) {
|
|
1364
|
+
log.info(`🔧 [TOOL] remove_source called`);
|
|
1365
|
+
log.info(` Source ID: ${args.source_id}`);
|
|
1366
|
+
try {
|
|
1367
|
+
// Validate source_id
|
|
1368
|
+
if (!args.source_id) {
|
|
1369
|
+
throw new Error("source_id is required");
|
|
1370
|
+
}
|
|
1371
|
+
// Resolve notebook URL
|
|
1372
|
+
let notebookUrl = args.notebook_url;
|
|
1373
|
+
if (!notebookUrl && args.notebook_id) {
|
|
1374
|
+
const notebook = this.library.getNotebook(args.notebook_id);
|
|
1375
|
+
if (!notebook) {
|
|
1376
|
+
throw new Error(`Notebook not found in library: ${args.notebook_id}`);
|
|
1377
|
+
}
|
|
1378
|
+
notebookUrl = notebook.url;
|
|
1379
|
+
log.info(` Resolved notebook: ${notebook.name}`);
|
|
1380
|
+
}
|
|
1381
|
+
else if (!notebookUrl) {
|
|
1382
|
+
const active = this.library.getActiveNotebook();
|
|
1383
|
+
if (active) {
|
|
1384
|
+
notebookUrl = active.url;
|
|
1385
|
+
log.info(` Using active notebook: ${active.name}`);
|
|
1386
|
+
}
|
|
1387
|
+
else {
|
|
1388
|
+
throw new Error("No notebook specified. Provide notebook_id or notebook_url.");
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
// Validate URL
|
|
1392
|
+
const safeUrl = validateNotebookUrl(notebookUrl);
|
|
1393
|
+
// Get the shared context manager from session manager
|
|
1394
|
+
const contextManager = this.sessionManager.getContextManager();
|
|
1395
|
+
// Remove source
|
|
1396
|
+
const sourceManager = new SourceManager(this.authManager, contextManager);
|
|
1397
|
+
const result = await sourceManager.removeSource(safeUrl, args.source_id);
|
|
1398
|
+
if (result.success) {
|
|
1399
|
+
log.success(`✅ [TOOL] remove_source completed`);
|
|
1400
|
+
}
|
|
1401
|
+
else {
|
|
1402
|
+
log.warning(`⚠️ [TOOL] remove_source failed: ${result.error}`);
|
|
1403
|
+
}
|
|
1404
|
+
return {
|
|
1405
|
+
success: result.success,
|
|
1406
|
+
data: result,
|
|
1407
|
+
...(result.error && { error: result.error }),
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
catch (error) {
|
|
1411
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1412
|
+
log.error(`❌ [TOOL] remove_source failed: ${errorMessage}`);
|
|
1413
|
+
return {
|
|
1414
|
+
success: false,
|
|
1415
|
+
error: errorMessage,
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Handle generate_audio_overview tool
|
|
1421
|
+
*
|
|
1422
|
+
* Triggers audio overview generation for a notebook.
|
|
1423
|
+
*/
|
|
1424
|
+
async handleGenerateAudioOverview(args) {
|
|
1425
|
+
log.info(`🔧 [TOOL] generate_audio_overview called`);
|
|
1426
|
+
try {
|
|
1427
|
+
// Resolve notebook URL
|
|
1428
|
+
let notebookUrl = args.notebook_url;
|
|
1429
|
+
if (!notebookUrl && args.notebook_id) {
|
|
1430
|
+
const notebook = this.library.getNotebook(args.notebook_id);
|
|
1431
|
+
if (!notebook) {
|
|
1432
|
+
throw new Error(`Notebook not found in library: ${args.notebook_id}`);
|
|
1433
|
+
}
|
|
1434
|
+
notebookUrl = notebook.url;
|
|
1435
|
+
log.info(` Resolved notebook: ${notebook.name}`);
|
|
1436
|
+
}
|
|
1437
|
+
else if (!notebookUrl) {
|
|
1438
|
+
const active = this.library.getActiveNotebook();
|
|
1439
|
+
if (active) {
|
|
1440
|
+
notebookUrl = active.url;
|
|
1441
|
+
log.info(` Using active notebook: ${active.name}`);
|
|
1442
|
+
}
|
|
1443
|
+
else {
|
|
1444
|
+
throw new Error("No notebook specified. Provide notebook_id or notebook_url.");
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
// Validate URL
|
|
1448
|
+
const safeUrl = validateNotebookUrl(notebookUrl);
|
|
1449
|
+
// Get the shared context manager from session manager
|
|
1450
|
+
const contextManager = this.sessionManager.getContextManager();
|
|
1451
|
+
// Generate audio
|
|
1452
|
+
const audioManager = new AudioManager(this.authManager, contextManager);
|
|
1453
|
+
const result = await audioManager.generateAudioOverview(safeUrl);
|
|
1454
|
+
if (result.success) {
|
|
1455
|
+
log.success(`✅ [TOOL] generate_audio_overview completed (status: ${result.status.status})`);
|
|
1456
|
+
}
|
|
1457
|
+
else {
|
|
1458
|
+
log.warning(`⚠️ [TOOL] generate_audio_overview: ${result.error}`);
|
|
1459
|
+
}
|
|
1460
|
+
return {
|
|
1461
|
+
success: result.success,
|
|
1462
|
+
data: result,
|
|
1463
|
+
...(result.error && { error: result.error }),
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
catch (error) {
|
|
1467
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1468
|
+
log.error(`❌ [TOOL] generate_audio_overview failed: ${errorMessage}`);
|
|
1469
|
+
return {
|
|
1470
|
+
success: false,
|
|
1471
|
+
error: errorMessage,
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Handle get_audio_status tool
|
|
1477
|
+
*
|
|
1478
|
+
* Checks the audio generation status for a notebook.
|
|
1479
|
+
*/
|
|
1480
|
+
async handleGetAudioStatus(args) {
|
|
1481
|
+
log.info(`🔧 [TOOL] get_audio_status called`);
|
|
1482
|
+
try {
|
|
1483
|
+
// Resolve notebook URL
|
|
1484
|
+
let notebookUrl = args.notebook_url;
|
|
1485
|
+
if (!notebookUrl && args.notebook_id) {
|
|
1486
|
+
const notebook = this.library.getNotebook(args.notebook_id);
|
|
1487
|
+
if (!notebook) {
|
|
1488
|
+
throw new Error(`Notebook not found in library: ${args.notebook_id}`);
|
|
1489
|
+
}
|
|
1490
|
+
notebookUrl = notebook.url;
|
|
1491
|
+
log.info(` Resolved notebook: ${notebook.name}`);
|
|
1492
|
+
}
|
|
1493
|
+
else if (!notebookUrl) {
|
|
1494
|
+
const active = this.library.getActiveNotebook();
|
|
1495
|
+
if (active) {
|
|
1496
|
+
notebookUrl = active.url;
|
|
1497
|
+
log.info(` Using active notebook: ${active.name}`);
|
|
1498
|
+
}
|
|
1499
|
+
else {
|
|
1500
|
+
throw new Error("No notebook specified. Provide notebook_id or notebook_url.");
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
// Validate URL
|
|
1504
|
+
const safeUrl = validateNotebookUrl(notebookUrl);
|
|
1505
|
+
// Get the shared context manager from session manager
|
|
1506
|
+
const contextManager = this.sessionManager.getContextManager();
|
|
1507
|
+
// Get status
|
|
1508
|
+
const audioManager = new AudioManager(this.authManager, contextManager);
|
|
1509
|
+
const status = await audioManager.getAudioStatus(safeUrl);
|
|
1510
|
+
log.success(`✅ [TOOL] get_audio_status completed (status: ${status.status})`);
|
|
1511
|
+
return {
|
|
1512
|
+
success: true,
|
|
1513
|
+
data: status,
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
catch (error) {
|
|
1517
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1518
|
+
log.error(`❌ [TOOL] get_audio_status failed: ${errorMessage}`);
|
|
1519
|
+
return {
|
|
1520
|
+
success: false,
|
|
1521
|
+
error: errorMessage,
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* Handle download_audio tool
|
|
1527
|
+
*
|
|
1528
|
+
* Downloads the generated audio file.
|
|
1529
|
+
*/
|
|
1530
|
+
async handleDownloadAudio(args) {
|
|
1531
|
+
log.info(`🔧 [TOOL] download_audio called`);
|
|
1532
|
+
try {
|
|
1533
|
+
// Resolve notebook URL
|
|
1534
|
+
let notebookUrl = args.notebook_url;
|
|
1535
|
+
if (!notebookUrl && args.notebook_id) {
|
|
1536
|
+
const notebook = this.library.getNotebook(args.notebook_id);
|
|
1537
|
+
if (!notebook) {
|
|
1538
|
+
throw new Error(`Notebook not found in library: ${args.notebook_id}`);
|
|
1539
|
+
}
|
|
1540
|
+
notebookUrl = notebook.url;
|
|
1541
|
+
log.info(` Resolved notebook: ${notebook.name}`);
|
|
1542
|
+
}
|
|
1543
|
+
else if (!notebookUrl) {
|
|
1544
|
+
const active = this.library.getActiveNotebook();
|
|
1545
|
+
if (active) {
|
|
1546
|
+
notebookUrl = active.url;
|
|
1547
|
+
log.info(` Using active notebook: ${active.name}`);
|
|
1548
|
+
}
|
|
1549
|
+
else {
|
|
1550
|
+
throw new Error("No notebook specified. Provide notebook_id or notebook_url.");
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
// Validate URL
|
|
1554
|
+
const safeUrl = validateNotebookUrl(notebookUrl);
|
|
1555
|
+
// Get the shared context manager from session manager
|
|
1556
|
+
const contextManager = this.sessionManager.getContextManager();
|
|
1557
|
+
// Download audio
|
|
1558
|
+
const audioManager = new AudioManager(this.authManager, contextManager);
|
|
1559
|
+
const result = await audioManager.downloadAudio(safeUrl, args.output_path);
|
|
1560
|
+
if (result.success) {
|
|
1561
|
+
log.success(`✅ [TOOL] download_audio completed: ${result.filePath}`);
|
|
1562
|
+
}
|
|
1563
|
+
else {
|
|
1564
|
+
log.warning(`⚠️ [TOOL] download_audio: ${result.error}`);
|
|
1565
|
+
}
|
|
1566
|
+
return {
|
|
1567
|
+
success: result.success,
|
|
1568
|
+
data: result,
|
|
1569
|
+
...(result.error && { error: result.error }),
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
catch (error) {
|
|
1573
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1574
|
+
log.error(`❌ [TOOL] download_audio failed: ${errorMessage}`);
|
|
1575
|
+
return {
|
|
1576
|
+
success: false,
|
|
1577
|
+
error: errorMessage,
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Handle configure_webhook tool
|
|
1583
|
+
*
|
|
1584
|
+
* Add or update a webhook endpoint.
|
|
1585
|
+
*/
|
|
1586
|
+
async handleConfigureWebhook(args) {
|
|
1587
|
+
log.info(`🔧 [TOOL] configure_webhook called`);
|
|
1588
|
+
log.info(` Name: ${args.name}`);
|
|
1589
|
+
try {
|
|
1590
|
+
const dispatcher = getWebhookDispatcher();
|
|
1591
|
+
if (args.id) {
|
|
1592
|
+
// Update existing
|
|
1593
|
+
const updated = dispatcher.updateWebhook({
|
|
1594
|
+
id: args.id,
|
|
1595
|
+
name: args.name,
|
|
1596
|
+
url: args.url,
|
|
1597
|
+
enabled: args.enabled,
|
|
1598
|
+
events: args.events,
|
|
1599
|
+
format: args.format,
|
|
1600
|
+
secret: args.secret,
|
|
1601
|
+
});
|
|
1602
|
+
if (!updated) {
|
|
1603
|
+
throw new Error(`Webhook not found: ${args.id}`);
|
|
1604
|
+
}
|
|
1605
|
+
log.success(`✅ [TOOL] configure_webhook updated: ${updated.name}`);
|
|
1606
|
+
return { success: true, data: updated };
|
|
1607
|
+
}
|
|
1608
|
+
else {
|
|
1609
|
+
// Create new
|
|
1610
|
+
const webhook = dispatcher.addWebhook({
|
|
1611
|
+
name: args.name,
|
|
1612
|
+
url: args.url,
|
|
1613
|
+
events: args.events,
|
|
1614
|
+
format: args.format,
|
|
1615
|
+
secret: args.secret,
|
|
1616
|
+
});
|
|
1617
|
+
log.success(`✅ [TOOL] configure_webhook created: ${webhook.name}`);
|
|
1618
|
+
return { success: true, data: webhook };
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
catch (error) {
|
|
1622
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1623
|
+
log.error(`❌ [TOOL] configure_webhook failed: ${errorMessage}`);
|
|
1624
|
+
return { success: false, error: errorMessage };
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Handle list_webhooks tool
|
|
1629
|
+
*
|
|
1630
|
+
* List all configured webhooks.
|
|
1631
|
+
*/
|
|
1632
|
+
async handleListWebhooks() {
|
|
1633
|
+
log.info(`🔧 [TOOL] list_webhooks called`);
|
|
1634
|
+
try {
|
|
1635
|
+
const dispatcher = getWebhookDispatcher();
|
|
1636
|
+
const webhooks = dispatcher.listWebhooks();
|
|
1637
|
+
const stats = dispatcher.getStats();
|
|
1638
|
+
log.success(`✅ [TOOL] list_webhooks completed (${webhooks.length} webhooks)`);
|
|
1639
|
+
return {
|
|
1640
|
+
success: true,
|
|
1641
|
+
data: { webhooks, stats },
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
catch (error) {
|
|
1645
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1646
|
+
log.error(`❌ [TOOL] list_webhooks failed: ${errorMessage}`);
|
|
1647
|
+
return { success: false, error: errorMessage };
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Handle test_webhook tool
|
|
1652
|
+
*
|
|
1653
|
+
* Send a test event to a webhook.
|
|
1654
|
+
*/
|
|
1655
|
+
async handleTestWebhook(args) {
|
|
1656
|
+
log.info(`🔧 [TOOL] test_webhook called`);
|
|
1657
|
+
log.info(` ID: ${args.id}`);
|
|
1658
|
+
try {
|
|
1659
|
+
const dispatcher = getWebhookDispatcher();
|
|
1660
|
+
const result = await dispatcher.testWebhook(args.id);
|
|
1661
|
+
if (result.success) {
|
|
1662
|
+
log.success(`✅ [TOOL] test_webhook succeeded`);
|
|
1663
|
+
return {
|
|
1664
|
+
success: true,
|
|
1665
|
+
data: { success: true, message: "Test event delivered successfully" },
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
else {
|
|
1669
|
+
log.warning(`⚠️ [TOOL] test_webhook failed: ${result.error}`);
|
|
1670
|
+
return {
|
|
1671
|
+
success: false,
|
|
1672
|
+
data: { success: false, message: result.error || "Test failed" },
|
|
1673
|
+
error: result.error,
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
catch (error) {
|
|
1678
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1679
|
+
log.error(`❌ [TOOL] test_webhook failed: ${errorMessage}`);
|
|
1680
|
+
return { success: false, error: errorMessage };
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Handle remove_webhook tool
|
|
1685
|
+
*
|
|
1686
|
+
* Remove a configured webhook.
|
|
1687
|
+
*/
|
|
1688
|
+
async handleRemoveWebhook(args) {
|
|
1689
|
+
log.info(`🔧 [TOOL] remove_webhook called`);
|
|
1690
|
+
log.info(` ID: ${args.id}`);
|
|
1691
|
+
try {
|
|
1692
|
+
const dispatcher = getWebhookDispatcher();
|
|
1693
|
+
const removed = dispatcher.removeWebhook(args.id);
|
|
1694
|
+
if (removed) {
|
|
1695
|
+
log.success(`✅ [TOOL] remove_webhook completed`);
|
|
1696
|
+
return {
|
|
1697
|
+
success: true,
|
|
1698
|
+
data: { removed: true, id: args.id },
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
else {
|
|
1702
|
+
log.warning(`⚠️ [TOOL] Webhook not found: ${args.id}`);
|
|
1703
|
+
return {
|
|
1704
|
+
success: false,
|
|
1705
|
+
error: `Webhook not found: ${args.id}`,
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
catch (error) {
|
|
1710
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1711
|
+
log.error(`❌ [TOOL] remove_webhook failed: ${errorMessage}`);
|
|
1712
|
+
return { success: false, error: errorMessage };
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
804
1715
|
/**
|
|
805
1716
|
* Cleanup all resources (called on server shutdown)
|
|
806
1717
|
*/
|