@telvok/librarian-mcp 1.5.4 → 2.3.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/schemas.d.ts +6 -6
- package/dist/library/sensitive-scanner.d.ts +20 -0
- package/dist/library/sensitive-scanner.js +56 -0
- 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 +104 -54
- package/dist/tools/adopt.d.ts +1 -0
- package/dist/tools/adopt.js +37 -10
- package/dist/tools/audit.d.ts +27 -0
- package/dist/tools/audit.js +126 -0
- 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 +117 -0
- package/dist/tools/library-publish.js +447 -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
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Marketplace Publish Tool
|
|
3
|
+
// Publish local entries as a book on Telvok marketplace
|
|
4
|
+
// ============================================================================
|
|
5
|
+
import * as fs from 'fs/promises';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { glob } from 'glob';
|
|
8
|
+
import matter from 'gray-matter';
|
|
9
|
+
import { loadApiKey } from './auth.js';
|
|
10
|
+
import { getLibraryPath, getLocalPath } from '../library/storage.js';
|
|
11
|
+
const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Tool Definition
|
|
14
|
+
// ============================================================================
|
|
15
|
+
export const marketplacePublishTool = {
|
|
16
|
+
name: 'marketplace_publish',
|
|
17
|
+
description: `Publish local entries as a book on Telvok marketplace.
|
|
18
|
+
|
|
19
|
+
Collects entries from .librarian/local/ and publishes them as a book.
|
|
20
|
+
Users can browse and purchase your book on telvok.com.
|
|
21
|
+
|
|
22
|
+
Required fields:
|
|
23
|
+
- name: Book title
|
|
24
|
+
- pricing: { type: "open" | "one_time" | "subscription", price_cents? }
|
|
25
|
+
- consumption: How buyers access content ("inline" | "reference" | "download")
|
|
26
|
+
- attestation: { original_work: true, no_secrets: true, terms_accepted: true }
|
|
27
|
+
|
|
28
|
+
Consumption types:
|
|
29
|
+
- "inline": Full content in API responses (all pricing)
|
|
30
|
+
- "reference": README + pointers to entries (all pricing)
|
|
31
|
+
- "download": Download to local library (only for free/open books)
|
|
32
|
+
|
|
33
|
+
Use preview: true to see what would be published without actually publishing.
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
- Preview: marketplace_publish({ name: "My Book", pricing: { type: "open" }, preview: true })
|
|
37
|
+
- Publish: marketplace_publish({ name: "My Book", pricing: { type: "open" }, consumption: "download", attestation: { original_work: true, no_secrets: true, terms_accepted: true } })`,
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
name: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: 'Book title (3-100 characters)',
|
|
44
|
+
},
|
|
45
|
+
description: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
description: 'Short description of the book (optional, max 500 chars)',
|
|
48
|
+
},
|
|
49
|
+
pricing: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
type: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
enum: ['open', 'one_time', 'subscription'],
|
|
55
|
+
description: 'Pricing model',
|
|
56
|
+
},
|
|
57
|
+
price_cents: {
|
|
58
|
+
type: 'number',
|
|
59
|
+
description: 'Price in cents (required for paid, min 100 = $1.00)',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
required: ['type'],
|
|
63
|
+
description: 'Pricing configuration',
|
|
64
|
+
},
|
|
65
|
+
consumption: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
enum: ['inline', 'reference', 'download'],
|
|
68
|
+
description: 'How buyers access content. download only for free books.',
|
|
69
|
+
},
|
|
70
|
+
attestation: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
original_work: { type: 'boolean', description: 'Confirm this is original work' },
|
|
74
|
+
no_secrets: { type: 'boolean', description: 'Confirm no secrets/credentials' },
|
|
75
|
+
terms_accepted: { type: 'boolean', description: 'Accept marketplace terms' },
|
|
76
|
+
},
|
|
77
|
+
required: ['original_work', 'no_secrets', 'terms_accepted'],
|
|
78
|
+
description: 'Required confirmations before publishing',
|
|
79
|
+
},
|
|
80
|
+
preview: {
|
|
81
|
+
type: 'boolean',
|
|
82
|
+
description: 'If true, show what would be published without publishing',
|
|
83
|
+
},
|
|
84
|
+
entries: {
|
|
85
|
+
type: 'array',
|
|
86
|
+
items: { type: 'string' },
|
|
87
|
+
description: 'Specific entry filenames to include (omit for all local/)',
|
|
88
|
+
},
|
|
89
|
+
tags: {
|
|
90
|
+
type: 'array',
|
|
91
|
+
items: { type: 'string' },
|
|
92
|
+
description: 'Category/topic tags (max 10)',
|
|
93
|
+
},
|
|
94
|
+
license: {
|
|
95
|
+
type: 'string',
|
|
96
|
+
enum: ['open', 'open_attributed', 'personal'],
|
|
97
|
+
description: 'License type (default: personal)',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
required: ['name', 'pricing'],
|
|
101
|
+
},
|
|
102
|
+
async handler(args) {
|
|
103
|
+
const { name, description, pricing, consumption, attestation, preview, entries: entryFilter, tags, license } = args;
|
|
104
|
+
// Validate name
|
|
105
|
+
if (!name || typeof name !== 'string' || name.trim().length < 3) {
|
|
106
|
+
throw new Error('Book name is required (minimum 3 characters)');
|
|
107
|
+
}
|
|
108
|
+
if (name.trim().length > 100) {
|
|
109
|
+
throw new Error('Book name must be 100 characters or less');
|
|
110
|
+
}
|
|
111
|
+
// Validate pricing
|
|
112
|
+
if (!pricing || !pricing.type) {
|
|
113
|
+
throw new Error('Pricing type is required');
|
|
114
|
+
}
|
|
115
|
+
if (!['open', 'one_time', 'subscription'].includes(pricing.type)) {
|
|
116
|
+
throw new Error('Pricing type must be: open, one_time, or subscription');
|
|
117
|
+
}
|
|
118
|
+
if (pricing.type !== 'open' && (!pricing.price_cents || pricing.price_cents < 100)) {
|
|
119
|
+
throw new Error('Paid books require price_cents >= 100 ($1.00)');
|
|
120
|
+
}
|
|
121
|
+
// Collect entries from local/ (needed for preview and publish)
|
|
122
|
+
const collectedEntries = await collectLocalEntries(entryFilter);
|
|
123
|
+
if (collectedEntries.length === 0) {
|
|
124
|
+
return {
|
|
125
|
+
success: false,
|
|
126
|
+
message: 'No entries found in .librarian/local/. Use record() to create entries first.',
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// Format pricing display
|
|
130
|
+
const pricingDisplay = pricing.type === 'open'
|
|
131
|
+
? 'Free'
|
|
132
|
+
: `$${((pricing.price_cents || 0) / 100).toFixed(2)}`;
|
|
133
|
+
// Handle preview mode - return summary without publishing
|
|
134
|
+
if (preview) {
|
|
135
|
+
return {
|
|
136
|
+
success: true,
|
|
137
|
+
preview: true,
|
|
138
|
+
message: `Preview of "${name.trim()}" - NOT published`,
|
|
139
|
+
summary: {
|
|
140
|
+
name: name.trim(),
|
|
141
|
+
pricing: { type: pricing.type, display: pricingDisplay },
|
|
142
|
+
entries_count: collectedEntries.length,
|
|
143
|
+
entries: collectedEntries.map(e => ({
|
|
144
|
+
title: e.title,
|
|
145
|
+
file: path.basename(e.originalPath),
|
|
146
|
+
})),
|
|
147
|
+
},
|
|
148
|
+
next_steps: 'To publish, add consumption type and attestation fields.',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// Validate consumption type (required for actual publish)
|
|
152
|
+
if (!consumption) {
|
|
153
|
+
return {
|
|
154
|
+
success: false,
|
|
155
|
+
message: 'Consumption type required. Choose how buyers access your content:',
|
|
156
|
+
options: {
|
|
157
|
+
inline: 'Content returned in API responses (best for small entries)',
|
|
158
|
+
reference: 'README + pointers to entries (best for larger books)',
|
|
159
|
+
download: 'Download to local library (only for free/open books)',
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
if (!['inline', 'reference', 'download'].includes(consumption)) {
|
|
164
|
+
return {
|
|
165
|
+
success: false,
|
|
166
|
+
message: 'Invalid consumption type. Must be: inline, reference, or download',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (consumption === 'download' && pricing.type !== 'open') {
|
|
170
|
+
return {
|
|
171
|
+
success: false,
|
|
172
|
+
message: 'Download is only for free books. Paid content uses inline or reference.',
|
|
173
|
+
next_steps: "Use pricing.type: 'open' for download, or consumption: 'inline'/'reference' for paid.",
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
// Validate attestation (required for actual publish)
|
|
177
|
+
if (!attestation) {
|
|
178
|
+
return {
|
|
179
|
+
success: false,
|
|
180
|
+
message: 'Attestation required. Please confirm:',
|
|
181
|
+
required: {
|
|
182
|
+
original_work: 'This is my original work or I have rights to publish',
|
|
183
|
+
no_secrets: 'Contains no secrets, credentials, or sensitive data',
|
|
184
|
+
terms_accepted: 'I accept the Telvok marketplace terms',
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const failedAttestations = [];
|
|
189
|
+
if (!attestation.original_work)
|
|
190
|
+
failedAttestations.push('original_work');
|
|
191
|
+
if (!attestation.no_secrets)
|
|
192
|
+
failedAttestations.push('no_secrets');
|
|
193
|
+
if (!attestation.terms_accepted)
|
|
194
|
+
failedAttestations.push('terms_accepted');
|
|
195
|
+
if (failedAttestations.length > 0) {
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
message: `All attestation fields must be true to publish. Failed: ${failedAttestations.join(', ')}`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// Check authentication
|
|
202
|
+
const apiKey = await loadApiKey();
|
|
203
|
+
if (!apiKey) {
|
|
204
|
+
return {
|
|
205
|
+
success: false,
|
|
206
|
+
message: 'Not authenticated. Run auth({ action: "login" }) to connect your Telvok account first.',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
// Format entries for API
|
|
210
|
+
const apiEntries = collectedEntries.map(e => ({
|
|
211
|
+
title: e.title,
|
|
212
|
+
content: e.content,
|
|
213
|
+
intent: e.intent,
|
|
214
|
+
context: e.context,
|
|
215
|
+
reasoning: e.reasoning,
|
|
216
|
+
example: e.example,
|
|
217
|
+
}));
|
|
218
|
+
// Build request body
|
|
219
|
+
const requestBody = {
|
|
220
|
+
name: name.trim(),
|
|
221
|
+
description: description?.trim(),
|
|
222
|
+
pricing,
|
|
223
|
+
consumption,
|
|
224
|
+
entries: apiEntries,
|
|
225
|
+
tags: tags || [],
|
|
226
|
+
license_type: license || 'personal',
|
|
227
|
+
attestation,
|
|
228
|
+
};
|
|
229
|
+
try {
|
|
230
|
+
const response = await fetch(`${TELVOK_API_URL}/api/publish`, {
|
|
231
|
+
method: 'POST',
|
|
232
|
+
headers: {
|
|
233
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
234
|
+
'Content-Type': 'application/json',
|
|
235
|
+
},
|
|
236
|
+
body: JSON.stringify(requestBody),
|
|
237
|
+
});
|
|
238
|
+
const data = await response.json();
|
|
239
|
+
if (!response.ok) {
|
|
240
|
+
// Handle Stripe Connect requirement
|
|
241
|
+
if (data.error === 'stripe_connect_required') {
|
|
242
|
+
return {
|
|
243
|
+
success: false,
|
|
244
|
+
message: `Stripe Connect required to sell paid content.\n\nComplete setup at: ${data.setup_url}`,
|
|
245
|
+
setup_url: data.setup_url,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
// Handle validation errors
|
|
249
|
+
if (data.error === 'validation_error') {
|
|
250
|
+
const details = Object.entries(data.details || {})
|
|
251
|
+
.map(([k, v]) => ` - ${k}: ${v}`)
|
|
252
|
+
.join('\n');
|
|
253
|
+
return {
|
|
254
|
+
success: false,
|
|
255
|
+
message: `Validation failed:\n${details}`,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
success: false,
|
|
260
|
+
message: data.error || `Publish failed: HTTP ${response.status}`,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
success: true,
|
|
265
|
+
message: data.message || `Published "${data.book?.name}" with ${data.entries_count} entries`,
|
|
266
|
+
book: data.book,
|
|
267
|
+
entries_count: data.entries_count,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
272
|
+
throw new Error(`Publish failed: ${message}`);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
// ============================================================================
|
|
277
|
+
// Entry Collection
|
|
278
|
+
// ============================================================================
|
|
279
|
+
async function collectLocalEntries(filter) {
|
|
280
|
+
const libraryPath = getLibraryPath();
|
|
281
|
+
const localPath = getLocalPath(libraryPath);
|
|
282
|
+
const entries = [];
|
|
283
|
+
try {
|
|
284
|
+
const files = await glob(path.join(localPath, '**/*.md'), { nodir: true });
|
|
285
|
+
for (const filePath of files) {
|
|
286
|
+
const filename = path.basename(filePath);
|
|
287
|
+
// If filter specified, only include matching files
|
|
288
|
+
if (filter && filter.length > 0) {
|
|
289
|
+
const matchesFilter = filter.some(f => filename === f ||
|
|
290
|
+
filename === f + '.md' ||
|
|
291
|
+
filePath.endsWith(f) ||
|
|
292
|
+
filePath.endsWith(f + '.md'));
|
|
293
|
+
if (!matchesFilter)
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
try {
|
|
297
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
298
|
+
const parsed = parseEntryFile(content, filePath);
|
|
299
|
+
if (parsed) {
|
|
300
|
+
entries.push({
|
|
301
|
+
...parsed,
|
|
302
|
+
originalPath: filePath,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
// Skip files that can't be parsed
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
// No local directory yet
|
|
313
|
+
}
|
|
314
|
+
return entries;
|
|
315
|
+
}
|
|
316
|
+
function parseEntryFile(content, filePath) {
|
|
317
|
+
const { data: frontmatter, content: body } = matter(content);
|
|
318
|
+
const trimmedBody = body.trim();
|
|
319
|
+
if (!trimmedBody)
|
|
320
|
+
return null;
|
|
321
|
+
// Extract title from frontmatter, H1, or filename
|
|
322
|
+
let title = frontmatter.title;
|
|
323
|
+
if (!title) {
|
|
324
|
+
const headingMatch = trimmedBody.match(/^#\s+(.+)$/m);
|
|
325
|
+
if (headingMatch) {
|
|
326
|
+
title = headingMatch[1].trim();
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
// Use filename as title, converting hyphens to spaces
|
|
330
|
+
title = path.basename(filePath, '.md')
|
|
331
|
+
.replace(/-/g, ' ')
|
|
332
|
+
.replace(/\b\w/g, l => l.toUpperCase());
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// Extract sections from body
|
|
336
|
+
const sections = extractSections(trimmedBody);
|
|
337
|
+
return {
|
|
338
|
+
title,
|
|
339
|
+
content: sections.main || trimmedBody,
|
|
340
|
+
intent: frontmatter.intent || undefined,
|
|
341
|
+
context: frontmatter.context || undefined,
|
|
342
|
+
reasoning: sections.reasoning,
|
|
343
|
+
example: sections.example,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function extractSections(body) {
|
|
347
|
+
const result = {
|
|
348
|
+
main: body,
|
|
349
|
+
};
|
|
350
|
+
// Find ## Reasoning section
|
|
351
|
+
const reasoningMatch = body.match(/##\s*Reasoning\s*\n([\s\S]*?)(?=##|$)/i);
|
|
352
|
+
if (reasoningMatch) {
|
|
353
|
+
result.reasoning = reasoningMatch[1].trim();
|
|
354
|
+
}
|
|
355
|
+
// Find ## Example section
|
|
356
|
+
const exampleMatch = body.match(/##\s*Example\s*\n([\s\S]*?)(?=##|$)/i);
|
|
357
|
+
if (exampleMatch) {
|
|
358
|
+
result.example = exampleMatch[1].trim();
|
|
359
|
+
}
|
|
360
|
+
// Main content is everything after title until first ## section
|
|
361
|
+
const mainMatch = body.match(/^#\s+.+\n\n?([\s\S]*?)(?=##|$)/);
|
|
362
|
+
if (mainMatch) {
|
|
363
|
+
result.main = mainMatch[1].trim();
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
// If no H1 header, take content before first ## section
|
|
367
|
+
const beforeSections = body.match(/^([\s\S]*?)(?=##)/);
|
|
368
|
+
if (beforeSections) {
|
|
369
|
+
result.main = beforeSections[1].trim();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
// ============================================================================
|
|
375
|
+
// Export
|
|
376
|
+
// ============================================================================
|
|
377
|
+
export default marketplacePublishTool;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
interface BookResult {
|
|
2
|
+
slug: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
pricing: 'open' | 'one_time' | 'subscription';
|
|
6
|
+
price: string;
|
|
7
|
+
entries: number;
|
|
8
|
+
rating: number | null;
|
|
9
|
+
tags: string[];
|
|
10
|
+
}
|
|
11
|
+
interface SearchResult {
|
|
12
|
+
books: BookResult[];
|
|
13
|
+
total: number;
|
|
14
|
+
message: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const marketplaceSearchTool: {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: "object";
|
|
21
|
+
properties: {
|
|
22
|
+
query: {
|
|
23
|
+
type: string;
|
|
24
|
+
description: string;
|
|
25
|
+
};
|
|
26
|
+
filters: {
|
|
27
|
+
type: string;
|
|
28
|
+
properties: {
|
|
29
|
+
pricing: {
|
|
30
|
+
type: string;
|
|
31
|
+
enum: string[];
|
|
32
|
+
description: string;
|
|
33
|
+
};
|
|
34
|
+
tags: {
|
|
35
|
+
type: string;
|
|
36
|
+
items: {
|
|
37
|
+
type: string;
|
|
38
|
+
};
|
|
39
|
+
description: string;
|
|
40
|
+
};
|
|
41
|
+
min_rating: {
|
|
42
|
+
type: string;
|
|
43
|
+
description: string;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
description: string;
|
|
47
|
+
};
|
|
48
|
+
limit: {
|
|
49
|
+
type: string;
|
|
50
|
+
description: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
required: never[];
|
|
54
|
+
};
|
|
55
|
+
handler(args: unknown): Promise<SearchResult>;
|
|
56
|
+
};
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Marketplace Search Tool
|
|
3
|
+
// Search for books on the Telvok marketplace
|
|
4
|
+
// ============================================================================
|
|
5
|
+
const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Tool Definition
|
|
8
|
+
// ============================================================================
|
|
9
|
+
export const marketplaceSearchTool = {
|
|
10
|
+
name: 'marketplace_search',
|
|
11
|
+
description: `Search Telvok marketplace for knowledge books.
|
|
12
|
+
|
|
13
|
+
Find books created by other users that you can import into your library.
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
- marketplace_search({ query: "react hooks" })
|
|
17
|
+
- marketplace_search({ query: "python", filters: { pricing: "open" } })
|
|
18
|
+
- marketplace_search({ query: "auth", limit: 5 })
|
|
19
|
+
|
|
20
|
+
Filters:
|
|
21
|
+
- pricing: "open" (free), "one_time" (paid once), "subscription" (ongoing)
|
|
22
|
+
- tags: Array of tags to match
|
|
23
|
+
- min_rating: Minimum quality score (0-5)`,
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
query: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'Search terms to find books',
|
|
30
|
+
},
|
|
31
|
+
filters: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
pricing: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
enum: ['open', 'one_time', 'subscription'],
|
|
37
|
+
description: 'Filter by pricing type',
|
|
38
|
+
},
|
|
39
|
+
tags: {
|
|
40
|
+
type: 'array',
|
|
41
|
+
items: { type: 'string' },
|
|
42
|
+
description: 'Filter by tags',
|
|
43
|
+
},
|
|
44
|
+
min_rating: {
|
|
45
|
+
type: 'number',
|
|
46
|
+
description: 'Minimum quality rating (0-5)',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
description: 'Optional filters to narrow results',
|
|
50
|
+
},
|
|
51
|
+
limit: {
|
|
52
|
+
type: 'number',
|
|
53
|
+
description: 'Maximum results to return (default: 10)',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
required: [], // Query is optional - empty returns all books (browse mode)
|
|
57
|
+
},
|
|
58
|
+
async handler(args) {
|
|
59
|
+
const { query = '', filters, limit = 10 } = args;
|
|
60
|
+
try {
|
|
61
|
+
const response = await fetch(`${TELVOK_API_URL}/api/search`, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: { 'Content-Type': 'application/json' },
|
|
64
|
+
body: JSON.stringify({ query, filters, limit }),
|
|
65
|
+
});
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const error = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
68
|
+
throw new Error(error.error || `Search failed: HTTP ${response.status}`);
|
|
69
|
+
}
|
|
70
|
+
const data = await response.json();
|
|
71
|
+
// Format results for agent consumption
|
|
72
|
+
const books = data.books || [];
|
|
73
|
+
const total = data.total || 0;
|
|
74
|
+
if (books.length === 0) {
|
|
75
|
+
const queryMsg = query ? `for "${query}"` : 'matching your filters';
|
|
76
|
+
return {
|
|
77
|
+
books: [],
|
|
78
|
+
total: 0,
|
|
79
|
+
message: `No books found ${queryMsg}. Try different search terms or filters.`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// Build helpful summary
|
|
83
|
+
const summary = books.map((b, i) => `${i + 1}. **${b.name}** (${b.price}) - ${b.entries} entries\n ${b.description?.slice(0, 100)}${b.description?.length > 100 ? '...' : ''}`).join('\n');
|
|
84
|
+
const queryMsg = query ? `for "${query}"` : 'in marketplace';
|
|
85
|
+
return {
|
|
86
|
+
books,
|
|
87
|
+
total,
|
|
88
|
+
message: `Found ${total} book(s) ${queryMsg}:\n\n${summary}\n\nUse marketplace_buy({ slug: "..." }) to purchase a book.`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
93
|
+
throw new Error(`Marketplace search failed: ${message}`);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
interface PublishedBook {
|
|
2
|
+
slug: string;
|
|
3
|
+
name: string;
|
|
4
|
+
entries_count: number;
|
|
5
|
+
pricing: {
|
|
6
|
+
type: string;
|
|
7
|
+
display: string;
|
|
8
|
+
};
|
|
9
|
+
created_at: string;
|
|
10
|
+
url: string;
|
|
11
|
+
}
|
|
12
|
+
interface PurchasedBook {
|
|
13
|
+
slug: string;
|
|
14
|
+
name: string;
|
|
15
|
+
author: string;
|
|
16
|
+
entries_count: number;
|
|
17
|
+
pricing: {
|
|
18
|
+
type: string;
|
|
19
|
+
display: string;
|
|
20
|
+
};
|
|
21
|
+
purchased_at: string;
|
|
22
|
+
status: string;
|
|
23
|
+
}
|
|
24
|
+
interface MyBooksResult {
|
|
25
|
+
success: boolean;
|
|
26
|
+
message: string;
|
|
27
|
+
published?: PublishedBook[];
|
|
28
|
+
purchased?: PurchasedBook[];
|
|
29
|
+
summary?: {
|
|
30
|
+
published_count: number;
|
|
31
|
+
purchased_count: number;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export declare const myBooksTool: {
|
|
35
|
+
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: "object";
|
|
39
|
+
properties: {
|
|
40
|
+
filter: {
|
|
41
|
+
type: string;
|
|
42
|
+
enum: string[];
|
|
43
|
+
description: string;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
required: never[];
|
|
47
|
+
};
|
|
48
|
+
handler(args: unknown): Promise<MyBooksResult>;
|
|
49
|
+
};
|
|
50
|
+
export default myBooksTool;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// My Books Tool
|
|
3
|
+
// View published and purchased books from Telvok marketplace
|
|
4
|
+
// ============================================================================
|
|
5
|
+
import { loadApiKey } from './auth.js';
|
|
6
|
+
const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Tool Definition
|
|
9
|
+
// ============================================================================
|
|
10
|
+
export const myBooksTool = {
|
|
11
|
+
name: 'my_books',
|
|
12
|
+
description: `View your published and purchased books from Telvok marketplace.
|
|
13
|
+
|
|
14
|
+
Shows:
|
|
15
|
+
- Published: Books you've created and published
|
|
16
|
+
- Purchased: Books you've bought or claimed (active status)
|
|
17
|
+
|
|
18
|
+
Requires authentication. Run auth({ action: "login" }) first if not connected.
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
- my_books() - Show all your books
|
|
22
|
+
- my_books({ filter: "published" }) - Only your published books
|
|
23
|
+
- my_books({ filter: "purchased" }) - Only books you've bought`,
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
filter: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
enum: ['all', 'published', 'purchased'],
|
|
30
|
+
description: 'Filter results (default: all)',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
required: [],
|
|
34
|
+
},
|
|
35
|
+
async handler(args) {
|
|
36
|
+
const { filter = 'all' } = (args || {});
|
|
37
|
+
// Validate filter
|
|
38
|
+
if (filter && !['all', 'published', 'purchased'].includes(filter)) {
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
message: 'Invalid filter. Must be: all, published, or purchased',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// Check authentication
|
|
45
|
+
const apiKey = await loadApiKey();
|
|
46
|
+
if (!apiKey) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
message: 'Not authenticated. Run auth({ action: "login" }) to connect your Telvok account first.',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const url = new URL(`${TELVOK_API_URL}/api/my-books`);
|
|
54
|
+
if (filter && filter !== 'all') {
|
|
55
|
+
url.searchParams.set('filter', filter);
|
|
56
|
+
}
|
|
57
|
+
const response = await fetch(url.toString(), {
|
|
58
|
+
method: 'GET',
|
|
59
|
+
headers: {
|
|
60
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
const data = await response.json();
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
message: data.error || `Failed to fetch books: HTTP ${response.status}`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Format output message
|
|
71
|
+
const publishedCount = data.published?.length || 0;
|
|
72
|
+
const purchasedCount = data.purchased?.length || 0;
|
|
73
|
+
let message = '';
|
|
74
|
+
if (filter === 'published') {
|
|
75
|
+
message = publishedCount === 0
|
|
76
|
+
? 'You haven\'t published any books yet.'
|
|
77
|
+
: `You have ${publishedCount} published book${publishedCount === 1 ? '' : 's'}.`;
|
|
78
|
+
}
|
|
79
|
+
else if (filter === 'purchased') {
|
|
80
|
+
message = purchasedCount === 0
|
|
81
|
+
? 'You haven\'t purchased any books yet.'
|
|
82
|
+
: `You have ${purchasedCount} purchased book${purchasedCount === 1 ? '' : 's'}.`;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const total = publishedCount + purchasedCount;
|
|
86
|
+
message = total === 0
|
|
87
|
+
? 'No books yet. Publish with marketplace_publish() or browse with marketplace_search().'
|
|
88
|
+
: `${publishedCount} published, ${purchasedCount} purchased.`;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
success: true,
|
|
92
|
+
message,
|
|
93
|
+
published: data.published,
|
|
94
|
+
purchased: data.purchased,
|
|
95
|
+
summary: data.summary,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
100
|
+
throw new Error(`Failed to fetch books: ${message}`);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// Export
|
|
106
|
+
// ============================================================================
|
|
107
|
+
export default myBooksTool;
|