@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.
Files changed (121) hide show
  1. package/dist/library/errors.d.ts +48 -0
  2. package/dist/library/errors.js +80 -0
  3. package/dist/library/parsers/jsonl.d.ts +9 -4
  4. package/dist/library/parsers/jsonl.js +52 -20
  5. package/dist/library/schemas.d.ts +6 -6
  6. package/dist/library/storage.d.ts +2 -2
  7. package/dist/library/storage.js +2 -2
  8. package/dist/library 2/embeddings.d.ts +21 -0
  9. package/dist/library 2/embeddings.js +86 -0
  10. package/dist/library 2/manager.d.ts +42 -0
  11. package/dist/library 2/manager.js +218 -0
  12. package/dist/library 2/parsers/cursor.d.ts +15 -0
  13. package/dist/library 2/parsers/cursor.js +168 -0
  14. package/dist/library 2/parsers/index.d.ts +6 -0
  15. package/dist/library 2/parsers/index.js +5 -0
  16. package/dist/library 2/parsers/json.d.ts +11 -0
  17. package/dist/library 2/parsers/json.js +95 -0
  18. package/dist/library 2/parsers/jsonl.d.ts +14 -0
  19. package/dist/library 2/parsers/jsonl.js +85 -0
  20. package/dist/library 2/parsers/markdown.d.ts +15 -0
  21. package/dist/library 2/parsers/markdown.js +77 -0
  22. package/dist/library 2/parsers/sqlite.d.ts +8 -0
  23. package/dist/library 2/parsers/sqlite.js +123 -0
  24. package/dist/library 2/parsers/types.d.ts +21 -0
  25. package/dist/library 2/parsers/types.js +4 -0
  26. package/dist/library 2/query.d.ts +26 -0
  27. package/dist/library 2/query.js +104 -0
  28. package/dist/library 2/schemas.d.ts +324 -0
  29. package/dist/library 2/schemas.js +79 -0
  30. package/dist/library 2/storage.d.ts +22 -0
  31. package/dist/library 2/storage.js +36 -0
  32. package/dist/library 2/vector-index.d.ts +55 -0
  33. package/dist/library 2/vector-index.js +160 -0
  34. package/dist/server 2.js +199 -0
  35. package/dist/server.d 2.ts +2 -0
  36. package/dist/server.js +102 -54
  37. package/dist/tools/adopt.d.ts +1 -0
  38. package/dist/tools/adopt.js +37 -10
  39. package/dist/tools/auth.d.ts +69 -0
  40. package/dist/tools/auth.js +379 -0
  41. package/dist/tools/bounty-claim.d.ts +28 -0
  42. package/dist/tools/bounty-claim.js +92 -0
  43. package/dist/tools/bounty-create.d.ts +47 -0
  44. package/dist/tools/bounty-create.js +118 -0
  45. package/dist/tools/bounty-list.d.ts +50 -0
  46. package/dist/tools/bounty-list.js +116 -0
  47. package/dist/tools/bounty-submit.d.ts +34 -0
  48. package/dist/tools/bounty-submit.js +94 -0
  49. package/dist/tools/brief.d.ts +94 -0
  50. package/dist/tools/brief.js +234 -15
  51. package/dist/tools/delete.d.ts +87 -0
  52. package/dist/tools/delete.js +266 -0
  53. package/dist/tools/feedback.d.ts +27 -0
  54. package/dist/tools/feedback.js +98 -0
  55. package/dist/tools/help.d.ts +22 -0
  56. package/dist/tools/help.js +482 -0
  57. package/dist/tools/import-memories.d.ts +1 -0
  58. package/dist/tools/import-memories.js +18 -13
  59. package/dist/tools/index.d.ts +11 -0
  60. package/dist/tools/index.js +12 -0
  61. package/dist/tools/library-buy.d.ts +31 -0
  62. package/dist/tools/library-buy.js +104 -0
  63. package/dist/tools/library-download.d.ts +27 -0
  64. package/dist/tools/library-download.js +177 -0
  65. package/dist/tools/library-publish.d.ts +112 -0
  66. package/dist/tools/library-publish.js +387 -0
  67. package/dist/tools/library-search.d.ts +110 -0
  68. package/dist/tools/library-search.js +132 -0
  69. package/dist/tools/mark-hit.d.ts +1 -0
  70. package/dist/tools/mark-hit.js +83 -5
  71. package/dist/tools/my-books.d.ts +51 -0
  72. package/dist/tools/my-books.js +115 -0
  73. package/dist/tools/my-bounties.d.ts +43 -0
  74. package/dist/tools/my-bounties.js +126 -0
  75. package/dist/tools/rate-book.d.ts +40 -0
  76. package/dist/tools/rate-book.js +147 -0
  77. package/dist/tools/rebuild-index.d.ts +1 -0
  78. package/dist/tools/rebuild-index.js +40 -8
  79. package/dist/tools/record.d.ts +18 -0
  80. package/dist/tools/record.js +30 -26
  81. package/dist/tools/seller-analytics.d.ts +53 -0
  82. package/dist/tools/seller-analytics.js +180 -0
  83. package/dist/tools/sync.d.ts +55 -0
  84. package/dist/tools/sync.js +304 -0
  85. package/dist/tools/unsubscribe.d.ts +48 -0
  86. package/dist/tools/unsubscribe.js +120 -0
  87. package/dist/tools 2/adopt.d.ts +24 -0
  88. package/dist/tools 2/adopt.js +154 -0
  89. package/dist/tools 2/auth.d.ts +35 -0
  90. package/dist/tools 2/auth.js +229 -0
  91. package/dist/tools 2/brief.d.ts +56 -0
  92. package/dist/tools 2/brief.js +414 -0
  93. package/dist/tools 2/help.d.ts +21 -0
  94. package/dist/tools 2/help.js +267 -0
  95. package/dist/tools 2/import-memories.d.ts +32 -0
  96. package/dist/tools 2/import-memories.js +231 -0
  97. package/dist/tools 2/index.d.ts +12 -0
  98. package/dist/tools 2/index.js +12 -0
  99. package/dist/tools 2/mark-hit.d.ts +20 -0
  100. package/dist/tools 2/mark-hit.js +71 -0
  101. package/dist/tools 2/marketplace-buy.d.ts +30 -0
  102. package/dist/tools 2/marketplace-buy.js +97 -0
  103. package/dist/tools 2/marketplace-download.d.ts +26 -0
  104. package/dist/tools 2/marketplace-download.js +160 -0
  105. package/dist/tools 2/marketplace-publish.d.ts +111 -0
  106. package/dist/tools 2/marketplace-publish.js +377 -0
  107. package/dist/tools 2/marketplace-search.d.ts +57 -0
  108. package/dist/tools 2/marketplace-search.js +96 -0
  109. package/dist/tools 2/my-books.d.ts +50 -0
  110. package/dist/tools 2/my-books.js +107 -0
  111. package/dist/tools 2/rate-book.d.ts +39 -0
  112. package/dist/tools 2/rate-book.js +139 -0
  113. package/dist/tools 2/rebuild-index.d.ts +23 -0
  114. package/dist/tools 2/rebuild-index.js +107 -0
  115. package/dist/tools 2/record.d.ts +40 -0
  116. package/dist/tools 2/record.js +205 -0
  117. package/dist/tools 2/seller-analytics.d.ts +35 -0
  118. package/dist/tools 2/seller-analytics.js +102 -0
  119. package/dist/tools 2/sync.d.ts +54 -0
  120. package/dist/tools 2/sync.js +298 -0
  121. package/package.json +1 -1
@@ -0,0 +1,379 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import { getLibraryPath } from '../library/storage.js';
4
+ // ============================================================================
5
+ // Constants
6
+ // ============================================================================
7
+ const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
8
+ // ============================================================================
9
+ // Tool Definition
10
+ // ============================================================================
11
+ export const authTool = {
12
+ name: 'auth',
13
+ title: 'Manage Authentication',
14
+ description: `Handle Telvok library authentication.
15
+
16
+ USE THIS TOOL WHEN:
17
+ - Any library operation fails with "authentication required"
18
+ - User wants to access marketplace features (buy, publish, sync)
19
+ - Checking if we're logged in before marketplace operations
20
+ - Key is expiring soon (< 7 days) → use refresh action
21
+
22
+ Actions:
23
+ - login: Start device code flow. Returns code for telvok.com/device
24
+ - complete: After user authorizes, finish login and save credentials
25
+ - refresh: Rotate API key before expiration (call when < 7 days left)
26
+ - status: Check if authenticated and show expiration
27
+ - logout: Remove credentials AND revoke key on server (key becomes immediately invalid)
28
+ - revoke: Same as logout - immediately invalidates the API key everywhere
29
+
30
+ TRIGGER PATTERNS:
31
+ - "auth required" error → auth({ action: 'login' })
32
+ - User completed browser auth → auth({ action: 'complete' })
33
+ - Expiring soon warning → auth({ action: 'refresh' })
34
+ - Key compromised → auth({ action: 'revoke' }) to immediately invalidate`,
35
+ inputSchema: {
36
+ type: 'object',
37
+ properties: {
38
+ action: {
39
+ type: 'string',
40
+ enum: ['login', 'complete', 'logout', 'status', 'refresh', 'revoke'],
41
+ description: 'Auth action to perform',
42
+ },
43
+ },
44
+ required: ['action'],
45
+ },
46
+ outputSchema: {
47
+ type: 'object',
48
+ properties: {
49
+ authenticated: { type: 'boolean' },
50
+ user_email: { type: 'string' },
51
+ user_id: { type: 'string' },
52
+ message: { type: 'string' },
53
+ verification_url: { type: 'string' },
54
+ user_code: { type: 'string' },
55
+ expires_at: { type: 'string' },
56
+ days_until_expiry: { type: 'number' },
57
+ },
58
+ required: ['authenticated', 'message'],
59
+ },
60
+ async handler(args) {
61
+ const { action } = args;
62
+ const libraryPath = getLibraryPath();
63
+ const authFile = path.join(libraryPath, '.auth');
64
+ const pendingFile = path.join(libraryPath, '.auth-pending');
65
+ switch (action) {
66
+ case 'status':
67
+ return await checkStatus(authFile);
68
+ case 'logout':
69
+ case 'revoke':
70
+ // Both logout and revoke now invalidate the key server-side
71
+ return await revokeAndLogout(authFile);
72
+ case 'login':
73
+ return await login(authFile, pendingFile);
74
+ case 'complete':
75
+ return await completeLogin(authFile, pendingFile);
76
+ case 'refresh':
77
+ return await refreshKey(authFile);
78
+ default:
79
+ throw new Error(`Unknown action: ${action}`);
80
+ }
81
+ },
82
+ };
83
+ // ============================================================================
84
+ // Action Handlers
85
+ // ============================================================================
86
+ async function checkStatus(authFile) {
87
+ try {
88
+ const content = await fs.readFile(authFile, 'utf-8');
89
+ const data = JSON.parse(content);
90
+ let message = `Authenticated as ${data.user_email}`;
91
+ let daysUntilExpiry;
92
+ // Check expiration
93
+ if (data.expires_at) {
94
+ const expiresAt = new Date(data.expires_at);
95
+ const now = new Date();
96
+ const msUntilExpiry = expiresAt.getTime() - now.getTime();
97
+ daysUntilExpiry = Math.floor(msUntilExpiry / (1000 * 60 * 60 * 24));
98
+ if (daysUntilExpiry <= 0) {
99
+ return {
100
+ authenticated: false,
101
+ message: 'API key has expired. Use auth({ action: "login" }) to get a new key.',
102
+ };
103
+ }
104
+ else if (daysUntilExpiry <= 7) {
105
+ message += ` ⚠️ Key expires in ${daysUntilExpiry} days! Use auth({ action: "refresh" }) to renew.`;
106
+ }
107
+ else {
108
+ message += ` (expires in ${daysUntilExpiry} days)`;
109
+ }
110
+ }
111
+ return {
112
+ authenticated: true,
113
+ user_email: data.user_email,
114
+ user_id: data.user_id,
115
+ message,
116
+ expires_at: data.expires_at,
117
+ days_until_expiry: daysUntilExpiry,
118
+ };
119
+ }
120
+ catch {
121
+ return {
122
+ authenticated: false,
123
+ message: 'Not authenticated. Use auth({ action: "login" }) to connect your Telvok account.',
124
+ };
125
+ }
126
+ }
127
+ async function revokeAndLogout(authFile) {
128
+ // Read current credentials to revoke server-side
129
+ let authData = null;
130
+ try {
131
+ const content = await fs.readFile(authFile, 'utf-8');
132
+ authData = JSON.parse(content);
133
+ }
134
+ catch {
135
+ return {
136
+ authenticated: false,
137
+ message: 'Already logged out.',
138
+ };
139
+ }
140
+ // Try to revoke on server (but don't fail if server is down)
141
+ let serverRevoked = false;
142
+ if (authData?.api_key) {
143
+ try {
144
+ const response = await fetch(`${TELVOK_API_URL}/api/auth/revoke`, {
145
+ method: 'POST',
146
+ headers: {
147
+ 'Content-Type': 'application/json',
148
+ 'Authorization': `Bearer ${authData.api_key}`,
149
+ },
150
+ });
151
+ if (response.ok) {
152
+ serverRevoked = true;
153
+ }
154
+ else if (response.status === 401) {
155
+ // Key already invalid, that's fine
156
+ serverRevoked = true;
157
+ }
158
+ }
159
+ catch {
160
+ // Server unreachable, still delete local file
161
+ }
162
+ }
163
+ // Always delete local file
164
+ try {
165
+ await fs.unlink(authFile);
166
+ }
167
+ catch {
168
+ // File already gone
169
+ }
170
+ const message = serverRevoked
171
+ ? 'Logged out and API key revoked. The key is now invalid everywhere.'
172
+ : 'Logged out locally. Note: Could not reach server to revoke key (it will expire in 90 days).';
173
+ return {
174
+ authenticated: false,
175
+ message,
176
+ };
177
+ }
178
+ async function login(authFile, pendingFile) {
179
+ // Check if already authenticated
180
+ try {
181
+ const content = await fs.readFile(authFile, 'utf-8');
182
+ const data = JSON.parse(content);
183
+ // Check if key is expired
184
+ if (data.expires_at && new Date(data.expires_at) < new Date()) {
185
+ // Key expired, allow new login
186
+ await fs.unlink(authFile).catch(() => { });
187
+ }
188
+ else {
189
+ return {
190
+ authenticated: true,
191
+ user_email: data.user_email,
192
+ user_id: data.user_id,
193
+ message: `Already authenticated as ${data.user_email}. Use auth({ action: "logout" }) first to switch accounts.`,
194
+ };
195
+ }
196
+ }
197
+ catch {
198
+ // Not authenticated, proceed with login
199
+ }
200
+ // Request a new device code
201
+ try {
202
+ const response = await fetch(`${TELVOK_API_URL}/api/auth/device`, {
203
+ method: 'POST',
204
+ headers: { 'Content-Type': 'application/json' },
205
+ });
206
+ if (!response.ok) {
207
+ const error = await response.json().catch(() => ({ error: 'Unknown error' }));
208
+ throw new Error(error.error || `HTTP ${response.status}`);
209
+ }
210
+ const data = await response.json();
211
+ // Save device code to pending file for later completion
212
+ const libraryPath = getLibraryPath();
213
+ await fs.mkdir(libraryPath, { recursive: true });
214
+ await fs.writeFile(pendingFile, JSON.stringify({
215
+ device_code: data.device_code,
216
+ user_code: data.user_code,
217
+ verification_url: data.verification_url,
218
+ created_at: new Date().toISOString(),
219
+ }, null, 2), 'utf-8');
220
+ // Return immediately with direct auth URL
221
+ const directAuthUrl = `${TELVOK_API_URL}/auth/${data.device_code}`;
222
+ return {
223
+ authenticated: false,
224
+ verification_url: directAuthUrl,
225
+ user_code: data.user_code,
226
+ message: `Click to authorize: ${directAuthUrl}\n\nAfter authorizing, call auth({ action: "complete" }) to finish.`,
227
+ };
228
+ }
229
+ catch (error) {
230
+ const message = error instanceof Error ? error.message : String(error);
231
+ throw new Error(`Failed to start login: ${message}`);
232
+ }
233
+ }
234
+ async function completeLogin(authFile, pendingFile) {
235
+ // Read pending device code
236
+ let deviceCode;
237
+ try {
238
+ const content = await fs.readFile(pendingFile, 'utf-8');
239
+ const pending = JSON.parse(content);
240
+ deviceCode = pending.device_code;
241
+ }
242
+ catch {
243
+ return {
244
+ authenticated: false,
245
+ message: 'No pending login. Call auth({ action: "login" }) first.',
246
+ };
247
+ }
248
+ // Poll for completion (try a few times)
249
+ for (let attempt = 0; attempt < 3; attempt++) {
250
+ try {
251
+ const response = await fetch(`${TELVOK_API_URL}/api/auth/device/poll`, {
252
+ method: 'POST',
253
+ headers: { 'Content-Type': 'application/json' },
254
+ body: JSON.stringify({ device_code: deviceCode }),
255
+ });
256
+ const data = await response.json();
257
+ if (data.status === 'pending') {
258
+ await sleep(2000);
259
+ continue;
260
+ }
261
+ if (data.status === 'expired') {
262
+ await fs.unlink(pendingFile).catch(() => { });
263
+ throw new Error('Code expired. Please call auth({ action: "login" }) to get a new code.');
264
+ }
265
+ if (data.status === 'success') {
266
+ // Save credentials including expiration
267
+ const authData = {
268
+ api_key: data.api_key,
269
+ user_email: data.user.email,
270
+ user_id: data.user.id,
271
+ created_at: new Date().toISOString(),
272
+ expires_at: data.expires_at,
273
+ };
274
+ await fs.writeFile(authFile, JSON.stringify(authData, null, 2), { encoding: 'utf-8', mode: 0o600 });
275
+ await fs.unlink(pendingFile).catch(() => { });
276
+ return {
277
+ authenticated: true,
278
+ user_email: data.user.email,
279
+ user_id: data.user.id,
280
+ expires_at: data.expires_at,
281
+ message: `Successfully authenticated as ${data.user.email}`,
282
+ };
283
+ }
284
+ throw new Error(`Unexpected status: ${data.status}`);
285
+ }
286
+ catch (error) {
287
+ if (error instanceof Error && (error.message.includes('expired') || error.message.includes('Unexpected'))) {
288
+ throw error;
289
+ }
290
+ // Network error, try again
291
+ await sleep(1000);
292
+ }
293
+ }
294
+ return {
295
+ authenticated: false,
296
+ message: 'Authorization not yet complete. Make sure you authorized at telvok.com/device, then call auth({ action: "complete" }) again.',
297
+ };
298
+ }
299
+ async function refreshKey(authFile) {
300
+ // Read current credentials
301
+ let authData;
302
+ try {
303
+ const content = await fs.readFile(authFile, 'utf-8');
304
+ authData = JSON.parse(content);
305
+ }
306
+ catch {
307
+ return {
308
+ authenticated: false,
309
+ message: 'Not authenticated. Use auth({ action: "login" }) first.',
310
+ };
311
+ }
312
+ // Call refresh endpoint
313
+ try {
314
+ const response = await fetch(`${TELVOK_API_URL}/api/auth/refresh`, {
315
+ method: 'POST',
316
+ headers: {
317
+ 'Content-Type': 'application/json',
318
+ 'Authorization': `Bearer ${authData.api_key}`,
319
+ },
320
+ });
321
+ if (!response.ok) {
322
+ const error = await response.json().catch(() => ({ error: 'Unknown error' }));
323
+ if (response.status === 401) {
324
+ // Key expired or invalid, need to re-login
325
+ await fs.unlink(authFile).catch(() => { });
326
+ return {
327
+ authenticated: false,
328
+ message: 'Key expired or invalid. Use auth({ action: "login" }) to get a new key.',
329
+ };
330
+ }
331
+ throw new Error(error.error || `HTTP ${response.status}`);
332
+ }
333
+ const data = await response.json();
334
+ // Update stored credentials
335
+ authData.api_key = data.api_key;
336
+ authData.expires_at = data.expires_at;
337
+ await fs.writeFile(authFile, JSON.stringify(authData, null, 2), { encoding: 'utf-8', mode: 0o600 });
338
+ const expiresAt = new Date(data.expires_at);
339
+ const daysUntilExpiry = Math.floor((expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
340
+ return {
341
+ authenticated: true,
342
+ user_email: authData.user_email,
343
+ user_id: authData.user_id,
344
+ expires_at: data.expires_at,
345
+ days_until_expiry: daysUntilExpiry,
346
+ message: `Key refreshed! New key expires in ${daysUntilExpiry} days.`,
347
+ };
348
+ }
349
+ catch (error) {
350
+ const message = error instanceof Error ? error.message : String(error);
351
+ throw new Error(`Failed to refresh key: ${message}`);
352
+ }
353
+ }
354
+ // ============================================================================
355
+ // Helpers
356
+ // ============================================================================
357
+ function sleep(ms) {
358
+ return new Promise(resolve => setTimeout(resolve, ms));
359
+ }
360
+ /**
361
+ * Load saved API key from auth file
362
+ * Used by other tools that need authenticated access
363
+ */
364
+ export async function loadApiKey() {
365
+ try {
366
+ const libraryPath = getLibraryPath();
367
+ const authFile = path.join(libraryPath, '.auth');
368
+ const content = await fs.readFile(authFile, 'utf-8');
369
+ const data = JSON.parse(content);
370
+ // Check if expired
371
+ if (data.expires_at && new Date(data.expires_at) < new Date()) {
372
+ return null;
373
+ }
374
+ return data.api_key;
375
+ }
376
+ catch {
377
+ return null;
378
+ }
379
+ }
@@ -0,0 +1,28 @@
1
+ interface BountyClaimResult {
2
+ success: boolean;
3
+ message: string;
4
+ bounty?: {
5
+ id: string;
6
+ title: string;
7
+ amount_cents: number;
8
+ claimed_at: string;
9
+ };
10
+ setup_url?: string;
11
+ }
12
+ export declare const bountyClaimTool: {
13
+ name: string;
14
+ title: string;
15
+ description: string;
16
+ inputSchema: {
17
+ type: "object";
18
+ properties: {
19
+ bounty_id: {
20
+ type: string;
21
+ description: string;
22
+ };
23
+ };
24
+ required: string[];
25
+ };
26
+ handler(args: unknown): Promise<BountyClaimResult>;
27
+ };
28
+ export {};
@@ -0,0 +1,92 @@
1
+ // ============================================================================
2
+ // Bounty Claim Tool
3
+ // Claim a bounty to work on it
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 bountyClaimTool = {
11
+ name: 'bounty_claim',
12
+ title: 'Claim Bounty',
13
+ description: `Claim a bounty to commit to fulfilling it.
14
+
15
+ USE THIS TOOL WHEN:
16
+ - User found a bounty they can fulfill from bounty_list()
17
+ - User says "I'll take that bounty" or "claim this"
18
+
19
+ After claiming: publish book → submit with bounty_submit() → get paid on approval.
20
+ First come, first served. Requires Stripe Connect for payment.
21
+
22
+ TRIGGER PATTERNS:
23
+ - "Claim that bounty" → bounty_claim({ bounty_id: "<id from bounty_list>" })
24
+ - User wants to fulfill a bounty → bounty_claim({ bounty_id: "..." })
25
+
26
+ Example:
27
+ - bounty_claim({ bounty_id: "abc123" })`,
28
+ inputSchema: {
29
+ type: 'object',
30
+ properties: {
31
+ bounty_id: {
32
+ type: 'string',
33
+ description: 'ID of the bounty to claim',
34
+ },
35
+ },
36
+ required: ['bounty_id'],
37
+ },
38
+ async handler(args) {
39
+ const { bounty_id } = args;
40
+ if (!bounty_id || typeof bounty_id !== 'string') {
41
+ return {
42
+ success: false,
43
+ message: 'bounty_id is required',
44
+ };
45
+ }
46
+ // Check authentication
47
+ const apiKey = await loadApiKey();
48
+ if (!apiKey) {
49
+ return {
50
+ success: false,
51
+ message: 'Not authenticated. Run auth({ action: "login" }) to connect your Telvok account first.',
52
+ };
53
+ }
54
+ try {
55
+ const response = await fetch(`${TELVOK_API_URL}/api/bounties/${bounty_id}/claim`, {
56
+ method: 'POST',
57
+ headers: {
58
+ 'Authorization': `Bearer ${apiKey}`,
59
+ 'Content-Type': 'application/json',
60
+ },
61
+ body: JSON.stringify({}),
62
+ });
63
+ const data = await response.json();
64
+ if (!response.ok) {
65
+ // Check for payment setup required
66
+ if (data.setup_url) {
67
+ return {
68
+ success: false,
69
+ message: `Payment setup required to claim bounties. Complete setup at:\n\n${data.setup_url}`,
70
+ setup_url: data.setup_url,
71
+ };
72
+ }
73
+ return {
74
+ success: false,
75
+ message: data.error || `Failed to claim bounty: HTTP ${response.status}`,
76
+ };
77
+ }
78
+ return {
79
+ success: true,
80
+ bounty: data.bounty,
81
+ message: data.message || `Bounty claimed! Now publish a book with library_publish() and submit it with bounty_submit({ bounty_id: "${bounty_id}", library_slug: "your-book-slug" })`,
82
+ };
83
+ }
84
+ catch (error) {
85
+ const message = error instanceof Error ? error.message : String(error);
86
+ return {
87
+ success: false,
88
+ message: `Failed to claim bounty: ${message}`,
89
+ };
90
+ }
91
+ },
92
+ };
@@ -0,0 +1,47 @@
1
+ interface BountyCreateResult {
2
+ success: boolean;
3
+ message: string;
4
+ bounty?: {
5
+ id: string;
6
+ title: string;
7
+ amount_cents: number;
8
+ expires_at: string;
9
+ };
10
+ checkout_url?: string;
11
+ }
12
+ export declare const bountyCreateTool: {
13
+ name: string;
14
+ title: string;
15
+ description: string;
16
+ inputSchema: {
17
+ type: "object";
18
+ properties: {
19
+ title: {
20
+ type: string;
21
+ description: string;
22
+ };
23
+ description: {
24
+ type: string;
25
+ description: string;
26
+ };
27
+ amount_cents: {
28
+ type: string;
29
+ description: string;
30
+ };
31
+ tags: {
32
+ type: string;
33
+ items: {
34
+ type: string;
35
+ };
36
+ description: string;
37
+ };
38
+ expires_days: {
39
+ type: string;
40
+ description: string;
41
+ };
42
+ };
43
+ required: string[];
44
+ };
45
+ handler(args: unknown): Promise<BountyCreateResult>;
46
+ };
47
+ export {};
@@ -0,0 +1,118 @@
1
+ // ============================================================================
2
+ // Bounty Create Tool
3
+ // Create a bounty on the 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 bountyCreateTool = {
11
+ name: 'bounty_create',
12
+ title: 'Create Bounty',
13
+ description: `Create a knowledge bounty when you need specific expertise.
14
+
15
+ USE THIS TOOL WHEN:
16
+ - User needs knowledge that doesn't exist in marketplace
17
+ - User says "I'd pay for someone to explain X"
18
+ - Searching shows no results for a topic user needs
19
+
20
+ Sellers claim bounties, publish relevant books, and get paid when you approve.
21
+ Bounty held in escrow until approval. Platform fee: 20%. Minimum: $5 (500 cents)
22
+
23
+ TRIGGER PATTERNS:
24
+ - Need expertise that doesn't exist → bounty_create({ title: "...", amount_cents: 2000 })
25
+ - "I'd pay for auth patterns" → bounty_create({ title: "Auth best practices", amount_cents: 2000 })
26
+
27
+ Examples:
28
+ - bounty_create({ title: "Stripe webhook patterns", amount_cents: 2000 })
29
+ - bounty_create({ title: "Auth best practices", description: "Need JWT refresh patterns", amount_cents: 5000, tags: ["auth", "jwt"] })`,
30
+ inputSchema: {
31
+ type: 'object',
32
+ properties: {
33
+ title: {
34
+ type: 'string',
35
+ description: 'What knowledge you need (3+ characters)',
36
+ },
37
+ description: {
38
+ type: 'string',
39
+ description: 'Additional details about what you need',
40
+ },
41
+ amount_cents: {
42
+ type: 'number',
43
+ description: 'Bounty amount in cents (minimum 500 = $5)',
44
+ },
45
+ tags: {
46
+ type: 'array',
47
+ items: { type: 'string' },
48
+ description: 'Tags to help sellers find your bounty',
49
+ },
50
+ expires_days: {
51
+ type: 'number',
52
+ description: 'Days until bounty expires (default: 30)',
53
+ },
54
+ },
55
+ required: ['title', 'amount_cents'],
56
+ },
57
+ async handler(args) {
58
+ const { title, description, amount_cents, tags, expires_days } = args;
59
+ // Validate inputs
60
+ if (!title || typeof title !== 'string' || title.length < 3) {
61
+ return {
62
+ success: false,
63
+ message: 'Title must be at least 3 characters',
64
+ };
65
+ }
66
+ if (!amount_cents || typeof amount_cents !== 'number' || amount_cents < 500) {
67
+ return {
68
+ success: false,
69
+ message: 'Minimum bounty amount is $5 (500 cents)',
70
+ };
71
+ }
72
+ // Check authentication
73
+ const apiKey = await loadApiKey();
74
+ if (!apiKey) {
75
+ return {
76
+ success: false,
77
+ message: 'Not authenticated. Run auth({ action: "login" }) to connect your Telvok account first.',
78
+ };
79
+ }
80
+ try {
81
+ const response = await fetch(`${TELVOK_API_URL}/api/bounties`, {
82
+ method: 'POST',
83
+ headers: {
84
+ 'Authorization': `Bearer ${apiKey}`,
85
+ 'Content-Type': 'application/json',
86
+ },
87
+ body: JSON.stringify({
88
+ title,
89
+ description,
90
+ amount_cents,
91
+ tags,
92
+ expires_days,
93
+ }),
94
+ });
95
+ const data = await response.json();
96
+ if (!response.ok) {
97
+ return {
98
+ success: false,
99
+ message: data.error || `Failed to create bounty: HTTP ${response.status}`,
100
+ };
101
+ }
102
+ const amount = `$${(amount_cents / 100).toFixed(2)}`;
103
+ return {
104
+ success: true,
105
+ bounty: data.bounty,
106
+ checkout_url: data.checkout_url,
107
+ message: `Bounty created! Complete payment to activate:\n\n${data.checkout_url}\n\nBounty: "${title}" (${amount})`,
108
+ };
109
+ }
110
+ catch (error) {
111
+ const message = error instanceof Error ? error.message : String(error);
112
+ return {
113
+ success: false,
114
+ message: `Failed to create bounty: ${message}`,
115
+ };
116
+ }
117
+ },
118
+ };