@telvok/librarian-mcp 1.5.3 → 2.0.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/dist/library/errors.d.ts +48 -0
- package/dist/library/errors.js +80 -0
- package/dist/library/parsers/jsonl.d.ts +9 -4
- package/dist/library/parsers/jsonl.js +52 -20
- package/dist/library/schemas.d.ts +6 -6
- package/dist/library/storage.d.ts +2 -2
- package/dist/library/storage.js +2 -2
- package/dist/library 2/embeddings.d.ts +21 -0
- package/dist/library 2/embeddings.js +86 -0
- package/dist/library 2/manager.d.ts +42 -0
- package/dist/library 2/manager.js +218 -0
- package/dist/library 2/parsers/cursor.d.ts +15 -0
- package/dist/library 2/parsers/cursor.js +168 -0
- package/dist/library 2/parsers/index.d.ts +6 -0
- package/dist/library 2/parsers/index.js +5 -0
- package/dist/library 2/parsers/json.d.ts +11 -0
- package/dist/library 2/parsers/json.js +95 -0
- package/dist/library 2/parsers/jsonl.d.ts +14 -0
- package/dist/library 2/parsers/jsonl.js +85 -0
- package/dist/library 2/parsers/markdown.d.ts +15 -0
- package/dist/library 2/parsers/markdown.js +77 -0
- package/dist/library 2/parsers/sqlite.d.ts +8 -0
- package/dist/library 2/parsers/sqlite.js +123 -0
- package/dist/library 2/parsers/types.d.ts +21 -0
- package/dist/library 2/parsers/types.js +4 -0
- package/dist/library 2/query.d.ts +26 -0
- package/dist/library 2/query.js +104 -0
- package/dist/library 2/schemas.d.ts +324 -0
- package/dist/library 2/schemas.js +79 -0
- package/dist/library 2/storage.d.ts +22 -0
- package/dist/library 2/storage.js +36 -0
- package/dist/library 2/vector-index.d.ts +55 -0
- package/dist/library 2/vector-index.js +160 -0
- package/dist/server 2.js +199 -0
- package/dist/server.d 2.ts +2 -0
- package/dist/server.js +102 -54
- package/dist/tools/adopt.d.ts +1 -0
- package/dist/tools/adopt.js +37 -10
- package/dist/tools/auth.d.ts +69 -0
- package/dist/tools/auth.js +379 -0
- package/dist/tools/bounty-claim.d.ts +28 -0
- package/dist/tools/bounty-claim.js +92 -0
- package/dist/tools/bounty-create.d.ts +47 -0
- package/dist/tools/bounty-create.js +118 -0
- package/dist/tools/bounty-list.d.ts +50 -0
- package/dist/tools/bounty-list.js +116 -0
- package/dist/tools/bounty-submit.d.ts +34 -0
- package/dist/tools/bounty-submit.js +94 -0
- package/dist/tools/brief.d.ts +94 -0
- package/dist/tools/brief.js +234 -15
- package/dist/tools/delete.d.ts +87 -0
- package/dist/tools/delete.js +266 -0
- package/dist/tools/feedback.d.ts +27 -0
- package/dist/tools/feedback.js +98 -0
- package/dist/tools/help.d.ts +22 -0
- package/dist/tools/help.js +482 -0
- package/dist/tools/import-memories.d.ts +1 -0
- package/dist/tools/import-memories.js +18 -13
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.js +12 -0
- package/dist/tools/library-buy.d.ts +31 -0
- package/dist/tools/library-buy.js +104 -0
- package/dist/tools/library-download.d.ts +27 -0
- package/dist/tools/library-download.js +177 -0
- package/dist/tools/library-publish.d.ts +112 -0
- package/dist/tools/library-publish.js +387 -0
- package/dist/tools/library-search.d.ts +110 -0
- package/dist/tools/library-search.js +132 -0
- package/dist/tools/mark-hit.d.ts +1 -0
- package/dist/tools/mark-hit.js +83 -5
- package/dist/tools/my-books.d.ts +51 -0
- package/dist/tools/my-books.js +115 -0
- package/dist/tools/my-bounties.d.ts +43 -0
- package/dist/tools/my-bounties.js +126 -0
- package/dist/tools/rate-book.d.ts +40 -0
- package/dist/tools/rate-book.js +147 -0
- package/dist/tools/rebuild-index.d.ts +1 -0
- package/dist/tools/rebuild-index.js +40 -8
- package/dist/tools/record.d.ts +18 -0
- package/dist/tools/record.js +30 -26
- package/dist/tools/seller-analytics.d.ts +53 -0
- package/dist/tools/seller-analytics.js +180 -0
- package/dist/tools/sync.d.ts +55 -0
- package/dist/tools/sync.js +304 -0
- package/dist/tools/unsubscribe.d.ts +48 -0
- package/dist/tools/unsubscribe.js +120 -0
- package/dist/tools 2/adopt.d.ts +24 -0
- package/dist/tools 2/adopt.js +154 -0
- package/dist/tools 2/auth.d.ts +35 -0
- package/dist/tools 2/auth.js +229 -0
- package/dist/tools 2/brief.d.ts +56 -0
- package/dist/tools 2/brief.js +414 -0
- package/dist/tools 2/help.d.ts +21 -0
- package/dist/tools 2/help.js +267 -0
- package/dist/tools 2/import-memories.d.ts +32 -0
- package/dist/tools 2/import-memories.js +231 -0
- package/dist/tools 2/index.d.ts +12 -0
- package/dist/tools 2/index.js +12 -0
- package/dist/tools 2/mark-hit.d.ts +20 -0
- package/dist/tools 2/mark-hit.js +71 -0
- package/dist/tools 2/marketplace-buy.d.ts +30 -0
- package/dist/tools 2/marketplace-buy.js +97 -0
- package/dist/tools 2/marketplace-download.d.ts +26 -0
- package/dist/tools 2/marketplace-download.js +160 -0
- package/dist/tools 2/marketplace-publish.d.ts +111 -0
- package/dist/tools 2/marketplace-publish.js +377 -0
- package/dist/tools 2/marketplace-search.d.ts +57 -0
- package/dist/tools 2/marketplace-search.js +96 -0
- package/dist/tools 2/my-books.d.ts +50 -0
- package/dist/tools 2/my-books.js +107 -0
- package/dist/tools 2/rate-book.d.ts +39 -0
- package/dist/tools 2/rate-book.js +139 -0
- package/dist/tools 2/rebuild-index.d.ts +23 -0
- package/dist/tools 2/rebuild-index.js +107 -0
- package/dist/tools 2/record.d.ts +40 -0
- package/dist/tools 2/record.js +205 -0
- package/dist/tools 2/seller-analytics.d.ts +35 -0
- package/dist/tools 2/seller-analytics.js +102 -0
- package/dist/tools 2/sync.d.ts +54 -0
- package/dist/tools 2/sync.js +298 -0
- package/package.json +1 -1
|
@@ -9,16 +9,24 @@ import { saveIndex, addToIndex, getIndexStats } from '../library/vector-index.js
|
|
|
9
9
|
// ============================================================================
|
|
10
10
|
export const rebuildIndexTool = {
|
|
11
11
|
name: 'rebuild_index',
|
|
12
|
-
|
|
12
|
+
title: 'Rebuild Search Index',
|
|
13
|
+
description: `Rebuild the semantic search index.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
USE THIS TOOL WHEN:
|
|
16
|
+
- brief() returns poor or no results when entries exist
|
|
17
|
+
- After import_memories() to index new content
|
|
18
|
+
- User reports search seems broken
|
|
19
|
+
- Manually edited .librarian/ files outside normal workflow
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
- Upgrading to v1.2.0 (existing entries need embeddings)
|
|
18
|
-
- Importing memories from other tools
|
|
19
|
-
- If semantic search seems broken
|
|
21
|
+
Reads all .md entries and regenerates embeddings. May take a minute.
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
TRIGGER PATTERNS:
|
|
24
|
+
- brief() returning nothing when it shouldn't → rebuild_index()
|
|
25
|
+
- After bulk import → rebuild_index()
|
|
26
|
+
- "Search seems broken" → rebuild_index()
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
- rebuild_index()`,
|
|
22
30
|
inputSchema: {
|
|
23
31
|
type: 'object',
|
|
24
32
|
properties: {},
|
|
@@ -39,6 +47,8 @@ Reads all .md entries and generates embeddings. May take a minute on first run.`
|
|
|
39
47
|
let indexed = 0;
|
|
40
48
|
let skipped = 0;
|
|
41
49
|
const errors = [];
|
|
50
|
+
// Progress file for status checks during rebuild
|
|
51
|
+
const progressFile = path.join(libraryPath, '.rebuild-progress.json');
|
|
42
52
|
// Collect all .md files from all directories
|
|
43
53
|
const allDirs = [localPath, importedPath, packagesPath];
|
|
44
54
|
const allFiles = [];
|
|
@@ -51,8 +61,28 @@ Reads all .md entries and generates embeddings. May take a minute on first run.`
|
|
|
51
61
|
// Directory doesn't exist, skip
|
|
52
62
|
}
|
|
53
63
|
}
|
|
64
|
+
// Write initial progress
|
|
65
|
+
await fs.writeFile(progressFile, JSON.stringify({
|
|
66
|
+
status: 'processing',
|
|
67
|
+
current: 0,
|
|
68
|
+
total: allFiles.length,
|
|
69
|
+
currentFile: '',
|
|
70
|
+
percent: 0,
|
|
71
|
+
startedAt: new Date().toISOString(),
|
|
72
|
+
}), 'utf-8').catch(() => { });
|
|
54
73
|
// Process each file
|
|
55
|
-
for (
|
|
74
|
+
for (let i = 0; i < allFiles.length; i++) {
|
|
75
|
+
const filePath = allFiles[i];
|
|
76
|
+
// Update progress file
|
|
77
|
+
await fs.writeFile(progressFile, JSON.stringify({
|
|
78
|
+
status: 'processing',
|
|
79
|
+
current: i + 1,
|
|
80
|
+
total: allFiles.length,
|
|
81
|
+
currentFile: path.basename(filePath),
|
|
82
|
+
percent: Math.round(((i + 1) / allFiles.length) * 100),
|
|
83
|
+
indexed,
|
|
84
|
+
skipped,
|
|
85
|
+
}), 'utf-8').catch(() => { });
|
|
56
86
|
try {
|
|
57
87
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
58
88
|
const { data, content: body } = matter(content);
|
|
@@ -91,6 +121,8 @@ Reads all .md entries and generates embeddings. May take a minute on first run.`
|
|
|
91
121
|
}
|
|
92
122
|
// Save the index
|
|
93
123
|
await saveIndex(index);
|
|
124
|
+
// Cleanup progress file
|
|
125
|
+
await fs.unlink(progressFile).catch(() => { });
|
|
94
126
|
const stats = getIndexStats(index);
|
|
95
127
|
const message = indexed > 0
|
|
96
128
|
? `Rebuilt index with ${indexed} entries (${stats.chunkCount} chunks). ${skipped} skipped.`
|
package/dist/tools/record.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface RecordResult {
|
|
|
5
5
|
}
|
|
6
6
|
export declare const recordTool: {
|
|
7
7
|
name: string;
|
|
8
|
+
title: string;
|
|
8
9
|
description: string;
|
|
9
10
|
inputSchema: {
|
|
10
11
|
type: "object";
|
|
@@ -36,5 +37,22 @@ export declare const recordTool: {
|
|
|
36
37
|
};
|
|
37
38
|
required: string[];
|
|
38
39
|
};
|
|
40
|
+
outputSchema: {
|
|
41
|
+
type: "object";
|
|
42
|
+
properties: {
|
|
43
|
+
success: {
|
|
44
|
+
type: string;
|
|
45
|
+
};
|
|
46
|
+
path: {
|
|
47
|
+
type: string;
|
|
48
|
+
description: string;
|
|
49
|
+
};
|
|
50
|
+
title: {
|
|
51
|
+
type: string;
|
|
52
|
+
description: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
required: string[];
|
|
56
|
+
};
|
|
39
57
|
handler(args: unknown): Promise<RecordResult>;
|
|
40
58
|
};
|
package/dist/tools/record.js
CHANGED
|
@@ -7,42 +7,37 @@ import { loadIndex, saveIndex, addToIndex } from '../library/vector-index.js';
|
|
|
7
7
|
// ============================================================================
|
|
8
8
|
export const recordTool = {
|
|
9
9
|
name: 'record',
|
|
10
|
-
|
|
10
|
+
title: 'Save New Insight',
|
|
11
|
+
description: `CALL THIS IMMEDIATELY when you learn something worth keeping. Do NOT wait to be asked.
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
13
|
+
USE THIS TOOL WHEN:
|
|
14
|
+
- You solve a problem (BEFORE moving to the next thing)
|
|
15
|
+
- You make a decision (capture the WHY now, not later)
|
|
16
|
+
- You hit a gotcha or unexpected behavior
|
|
17
|
+
- You learn something non-obvious about this codebase
|
|
18
|
+
- You discover credentials, API keys, or configuration
|
|
19
|
+
- You find out how something actually works vs how you assumed
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
DO NOT BATCH insights. One insight = one record() call.
|
|
22
|
+
DO NOT SKIP because it seems minor. If it cost us 5+ minutes, record it.
|
|
20
23
|
|
|
21
24
|
Quality bar: "I wish we knew this yesterday"
|
|
22
25
|
|
|
26
|
+
TRIGGER PATTERNS:
|
|
27
|
+
- Fixed a bug → record({ insight: "The issue was X because Y" })
|
|
28
|
+
- Found credentials → record({ insight: "Auth uses X", context: "credentials" })
|
|
29
|
+
- Made architectural choice → record({ insight: "Chose X over Y", reasoning: "..." })
|
|
30
|
+
- Something didn't work as expected → record({ insight: "X actually does Y, not Z" })
|
|
31
|
+
|
|
23
32
|
Good entries:
|
|
24
33
|
- "Stripe retries webhooks but doesn't dedupe - always check idempotency key"
|
|
25
|
-
- "
|
|
26
|
-
- "
|
|
34
|
+
- "Next.js 16 renamed middleware.ts to proxy.ts"
|
|
35
|
+
- "API key format is tvk_ prefix + 32 chars"
|
|
27
36
|
|
|
28
37
|
Not worth recording:
|
|
29
38
|
- Generic docs (search exists)
|
|
30
39
|
- Temporary hacks
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
Examples:
|
|
34
|
-
|
|
35
|
-
Quick:
|
|
36
|
-
- record({ insight: "Stripe webhooks need idempotency checks" })
|
|
37
|
-
|
|
38
|
-
Rich:
|
|
39
|
-
- record({
|
|
40
|
-
intent: "Setting up GitHub org for Telvok",
|
|
41
|
-
insight: "GitHub org names are first-come-first-served regardless of domain ownership",
|
|
42
|
-
context: "GitHub, npm, branding",
|
|
43
|
-
reasoning: "We owned telvok.com but someone squatted telvok org years ago",
|
|
44
|
-
example: "Had to use telvokdev instead of telvok"
|
|
45
|
-
})`,
|
|
40
|
+
- Things that'll change next week`,
|
|
46
41
|
inputSchema: {
|
|
47
42
|
type: 'object',
|
|
48
43
|
properties: {
|
|
@@ -73,9 +68,18 @@ Rich:
|
|
|
73
68
|
},
|
|
74
69
|
required: ['insight'],
|
|
75
70
|
},
|
|
71
|
+
outputSchema: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
properties: {
|
|
74
|
+
success: { type: 'boolean' },
|
|
75
|
+
path: { type: 'string', description: 'Path to the created entry file' },
|
|
76
|
+
title: { type: 'string', description: 'Title of the created entry' },
|
|
77
|
+
},
|
|
78
|
+
required: ['success', 'path', 'title'],
|
|
79
|
+
},
|
|
76
80
|
async handler(args) {
|
|
77
81
|
const { insight, intent, reasoning, context, example, title: providedTitle } = args;
|
|
78
|
-
if (!insight) {
|
|
82
|
+
if (!insight || !insight.trim()) {
|
|
79
83
|
throw new Error('insight is required');
|
|
80
84
|
}
|
|
81
85
|
const libraryPath = getLibraryPath();
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
interface SellerAnalyticsResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
message: string;
|
|
4
|
+
overview?: {
|
|
5
|
+
revenue: string;
|
|
6
|
+
downloads: number;
|
|
7
|
+
hits: number;
|
|
8
|
+
avgRating: string;
|
|
9
|
+
libraryCount: number;
|
|
10
|
+
};
|
|
11
|
+
books?: Array<{
|
|
12
|
+
name: string;
|
|
13
|
+
revenue: string;
|
|
14
|
+
downloads: number;
|
|
15
|
+
hits: number;
|
|
16
|
+
rating: string;
|
|
17
|
+
topEntries?: Array<{
|
|
18
|
+
title: string;
|
|
19
|
+
hits: number;
|
|
20
|
+
trend: string;
|
|
21
|
+
}>;
|
|
22
|
+
searchTerms?: Array<{
|
|
23
|
+
term: string;
|
|
24
|
+
count: number;
|
|
25
|
+
}>;
|
|
26
|
+
}>;
|
|
27
|
+
recent_reviews?: Array<{
|
|
28
|
+
book: string;
|
|
29
|
+
rating: string;
|
|
30
|
+
title: string;
|
|
31
|
+
reviewer: string;
|
|
32
|
+
}>;
|
|
33
|
+
insights?: {
|
|
34
|
+
totalSearches: number;
|
|
35
|
+
uniqueSearchTerms: number;
|
|
36
|
+
gapAnalysis?: Array<{
|
|
37
|
+
term: string;
|
|
38
|
+
count: number;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export declare const sellerAnalyticsTool: {
|
|
43
|
+
name: string;
|
|
44
|
+
title: string;
|
|
45
|
+
description: string;
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: "object";
|
|
48
|
+
properties: {};
|
|
49
|
+
required: never[];
|
|
50
|
+
};
|
|
51
|
+
handler(): Promise<SellerAnalyticsResult>;
|
|
52
|
+
};
|
|
53
|
+
export default sellerAnalyticsTool;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Seller Analytics Tool
|
|
3
|
+
// View your seller analytics - revenue, downloads, hits, ratings
|
|
4
|
+
// ============================================================================
|
|
5
|
+
import { loadApiKey } from './auth.js';
|
|
6
|
+
const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Helper Functions
|
|
9
|
+
// ============================================================================
|
|
10
|
+
function formatGhostModeMessage(data) {
|
|
11
|
+
const lines = [];
|
|
12
|
+
lines.push(`Analytics for ${data.overview.libraryCount} book${data.overview.libraryCount === 1 ? '' : 's'}`);
|
|
13
|
+
lines.push('');
|
|
14
|
+
lines.push(`Overview:`);
|
|
15
|
+
lines.push(` Revenue: $${data.overview.revenue.toFixed(2)}`);
|
|
16
|
+
lines.push(` Downloads: ${data.overview.downloads}`);
|
|
17
|
+
lines.push(` Hits: ${data.overview.hits}`);
|
|
18
|
+
if (data.overview.avgRating) {
|
|
19
|
+
lines.push(` Avg Rating: ${data.overview.avgRating}/5`);
|
|
20
|
+
}
|
|
21
|
+
// Per-book breakdown with Ghost Mode data
|
|
22
|
+
if (data.byLibrary.length > 0) {
|
|
23
|
+
lines.push('');
|
|
24
|
+
lines.push('━'.repeat(40));
|
|
25
|
+
for (const book of data.byLibrary) {
|
|
26
|
+
lines.push('');
|
|
27
|
+
lines.push(`📚 ${book.name} - $${book.revenue.toFixed(2)} - ${book.hits} hits`);
|
|
28
|
+
// Top entries
|
|
29
|
+
if (book.topEntries && book.topEntries.length > 0) {
|
|
30
|
+
lines.push('');
|
|
31
|
+
lines.push(' Top Entries:');
|
|
32
|
+
book.topEntries.slice(0, 5).forEach((entry, i) => {
|
|
33
|
+
lines.push(` ${i + 1}. ${entry.title} - ${entry.hits} hits (${entry.trend})`);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// Search terms
|
|
37
|
+
if (book.searchTerms && book.searchTerms.length > 0) {
|
|
38
|
+
lines.push('');
|
|
39
|
+
lines.push(' Users searched for:');
|
|
40
|
+
book.searchTerms.slice(0, 5).forEach(term => {
|
|
41
|
+
lines.push(` • "${term.term}" (${term.count} queries)`);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Gap analysis
|
|
47
|
+
if (data.insights?.zeroResultSearches && data.insights.zeroResultSearches.length > 0) {
|
|
48
|
+
lines.push('');
|
|
49
|
+
lines.push('━'.repeat(40));
|
|
50
|
+
lines.push('');
|
|
51
|
+
lines.push('💡 Gap Analysis (searches with no results):');
|
|
52
|
+
data.insights.zeroResultSearches.slice(0, 5).forEach(z => {
|
|
53
|
+
lines.push(` • "${z.term}" (${z.count} searches)`);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return lines.join('\n');
|
|
57
|
+
}
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// Tool Definition
|
|
60
|
+
// ============================================================================
|
|
61
|
+
export const sellerAnalyticsTool = {
|
|
62
|
+
name: 'seller_analytics',
|
|
63
|
+
title: 'View Seller Analytics',
|
|
64
|
+
description: `View seller analytics for your published books.
|
|
65
|
+
|
|
66
|
+
USE THIS TOOL WHEN:
|
|
67
|
+
- User asks "how are my books doing" or "show my sales"
|
|
68
|
+
- Checking revenue, downloads, or ratings
|
|
69
|
+
- User wants to see which entries are most useful (Ghost Mode data)
|
|
70
|
+
|
|
71
|
+
Shows revenue, downloads, hits, ratings, top entries, and search terms.
|
|
72
|
+
|
|
73
|
+
TRIGGER PATTERNS:
|
|
74
|
+
- "How are my books selling?" → seller_analytics()
|
|
75
|
+
- "Show my revenue" → seller_analytics()
|
|
76
|
+
- "What are people searching for?" → seller_analytics() (shows search terms)
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
- seller_analytics() - View all your analytics`,
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: 'object',
|
|
82
|
+
properties: {},
|
|
83
|
+
required: [],
|
|
84
|
+
},
|
|
85
|
+
async handler() {
|
|
86
|
+
// Check authentication
|
|
87
|
+
const apiKey = await loadApiKey();
|
|
88
|
+
if (!apiKey) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
message: 'Not authenticated. Run auth({ action: "login" }) to connect your Telvok account first.',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const response = await fetch(`${TELVOK_API_URL}/api/seller/analytics`, {
|
|
96
|
+
method: 'GET',
|
|
97
|
+
headers: {
|
|
98
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
const data = await response.json();
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
return {
|
|
104
|
+
success: false,
|
|
105
|
+
message: data.error || `Failed to fetch analytics: HTTP ${response.status}`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// Check if user has any libraries
|
|
109
|
+
if (data.overview.libraryCount === 0) {
|
|
110
|
+
return {
|
|
111
|
+
success: true,
|
|
112
|
+
message: 'You haven\'t published any books yet. Use library_publish() to publish your first book.',
|
|
113
|
+
overview: {
|
|
114
|
+
revenue: '$0.00',
|
|
115
|
+
downloads: 0,
|
|
116
|
+
hits: 0,
|
|
117
|
+
avgRating: 'N/A',
|
|
118
|
+
libraryCount: 0,
|
|
119
|
+
},
|
|
120
|
+
books: [],
|
|
121
|
+
recent_reviews: [],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// Format for terminal display with Ghost Mode data
|
|
125
|
+
const result = {
|
|
126
|
+
success: true,
|
|
127
|
+
message: formatGhostModeMessage(data),
|
|
128
|
+
overview: {
|
|
129
|
+
revenue: `$${data.overview.revenue.toFixed(2)}`,
|
|
130
|
+
downloads: data.overview.downloads,
|
|
131
|
+
hits: data.overview.hits,
|
|
132
|
+
avgRating: data.overview.avgRating ? `${data.overview.avgRating}/5` : 'No ratings yet',
|
|
133
|
+
libraryCount: data.overview.libraryCount,
|
|
134
|
+
},
|
|
135
|
+
books: data.byLibrary.map(b => ({
|
|
136
|
+
name: b.name,
|
|
137
|
+
revenue: `$${b.revenue.toFixed(2)}`,
|
|
138
|
+
downloads: b.downloads,
|
|
139
|
+
hits: b.hits,
|
|
140
|
+
rating: b.rating ? `${b.rating}/5` : 'No ratings',
|
|
141
|
+
topEntries: b.topEntries?.slice(0, 5).map(e => ({
|
|
142
|
+
title: e.title,
|
|
143
|
+
hits: e.hits,
|
|
144
|
+
trend: e.trend,
|
|
145
|
+
})),
|
|
146
|
+
searchTerms: b.searchTerms?.slice(0, 5).map(t => ({
|
|
147
|
+
term: t.term,
|
|
148
|
+
count: t.count,
|
|
149
|
+
})),
|
|
150
|
+
})),
|
|
151
|
+
recent_reviews: data.recentReviews.slice(0, 5).map(r => ({
|
|
152
|
+
book: r.library.name,
|
|
153
|
+
rating: `${r.rating}/5`,
|
|
154
|
+
title: r.title || '(no title)',
|
|
155
|
+
reviewer: r.reviewer?.name || 'Anonymous',
|
|
156
|
+
})),
|
|
157
|
+
};
|
|
158
|
+
// Add Ghost Mode insights if available
|
|
159
|
+
if (data.insights) {
|
|
160
|
+
result.insights = {
|
|
161
|
+
totalSearches: data.insights.totalSearches,
|
|
162
|
+
uniqueSearchTerms: data.insights.uniqueSearchTerms,
|
|
163
|
+
gapAnalysis: data.insights.zeroResultSearches?.slice(0, 10).map(z => ({
|
|
164
|
+
term: z.term,
|
|
165
|
+
count: z.count,
|
|
166
|
+
})),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
173
|
+
throw new Error(`Failed to fetch analytics: ${message}`);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
// ============================================================================
|
|
178
|
+
// Export
|
|
179
|
+
// ============================================================================
|
|
180
|
+
export default sellerAnalyticsTool;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
interface SyncedBook {
|
|
2
|
+
slug: string;
|
|
3
|
+
name: string;
|
|
4
|
+
new_entries: number;
|
|
5
|
+
modified_entries: number;
|
|
6
|
+
access: 'cloud' | 'downloaded';
|
|
7
|
+
version?: string;
|
|
8
|
+
synced_from?: string;
|
|
9
|
+
}
|
|
10
|
+
interface SyncResult {
|
|
11
|
+
success: boolean;
|
|
12
|
+
message: string;
|
|
13
|
+
synced?: SyncedBook[];
|
|
14
|
+
available?: {
|
|
15
|
+
slug: string;
|
|
16
|
+
name: string;
|
|
17
|
+
new_entries: number;
|
|
18
|
+
}[];
|
|
19
|
+
pinned?: {
|
|
20
|
+
slug: string;
|
|
21
|
+
name: string;
|
|
22
|
+
}[];
|
|
23
|
+
up_to_date?: number;
|
|
24
|
+
}
|
|
25
|
+
export declare const syncTool: {
|
|
26
|
+
name: string;
|
|
27
|
+
title: string;
|
|
28
|
+
description: string;
|
|
29
|
+
inputSchema: {
|
|
30
|
+
type: "object";
|
|
31
|
+
properties: {
|
|
32
|
+
slug: {
|
|
33
|
+
type: string;
|
|
34
|
+
description: string;
|
|
35
|
+
};
|
|
36
|
+
options: {
|
|
37
|
+
type: string;
|
|
38
|
+
properties: {
|
|
39
|
+
force: {
|
|
40
|
+
type: string;
|
|
41
|
+
description: string;
|
|
42
|
+
};
|
|
43
|
+
download: {
|
|
44
|
+
type: string;
|
|
45
|
+
description: string;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
description: string;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
required: never[];
|
|
52
|
+
};
|
|
53
|
+
handler(args: unknown): Promise<SyncResult>;
|
|
54
|
+
};
|
|
55
|
+
export default syncTool;
|