50c 2.2.1 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +7 -1
- package/lib/packs/beacon.js +87 -1
- package/lib/packs/librarian.js +496 -0
- package/lib/packs.js +53 -4
- package/lib/vault.js +352 -2
- package/package.json +6 -3
package/lib/index.js
CHANGED
|
@@ -39,6 +39,7 @@ async function getTools() {
|
|
|
39
39
|
...core.CORE_TOOLS,
|
|
40
40
|
...vault.VAULT_TOOLS,
|
|
41
41
|
...packs.PACK_TOOLS,
|
|
42
|
+
...beacon.ROADMAP_TOOLS, // Roadmap is FREE
|
|
42
43
|
];
|
|
43
44
|
|
|
44
45
|
// STARTER tier
|
|
@@ -67,10 +68,15 @@ async function handleTool(name, args = {}) {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
// Vault tools (FREE)
|
|
70
|
-
if (name.startsWith('vault_')) {
|
|
71
|
+
if (name.startsWith('vault_') || name.startsWith('dewey_')) {
|
|
71
72
|
return vault.handleTool(name, args);
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
// Roadmap tools (FREE - in beacon but always available)
|
|
76
|
+
if (name.startsWith('roadmap_')) {
|
|
77
|
+
return beacon.handleTool(name, args);
|
|
78
|
+
}
|
|
79
|
+
|
|
74
80
|
// Pack management tools (FREE)
|
|
75
81
|
if (name.startsWith('50c_')) {
|
|
76
82
|
return packs.handleTool(name, args);
|
package/lib/packs/beacon.js
CHANGED
|
@@ -46,6 +46,22 @@ async function learningStats() {
|
|
|
46
46
|
return apiRequest('GET', '/tools/learning_stats');
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
async function roadmapAdd(idea, priority = 'medium', tags = []) {
|
|
50
|
+
return apiRequest('POST', '/tools/roadmap', { action: 'add', idea, priority, tags });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function roadmapList(filter = null) {
|
|
54
|
+
return apiRequest('POST', '/tools/roadmap', { action: 'list', filter });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function roadmapUpdate(id, updates) {
|
|
58
|
+
return apiRequest('POST', '/tools/roadmap', { action: 'update', id, ...updates });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function roadmapDelete(id) {
|
|
62
|
+
return apiRequest('POST', '/tools/roadmap', { action: 'delete', id });
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
// Tool definitions - STARTER tier ($29/mo)
|
|
50
66
|
const BEACON_TOOLS = [
|
|
51
67
|
{
|
|
@@ -173,6 +189,62 @@ const BEACON_TOOLS = [
|
|
|
173
189
|
inputSchema: { type: 'object', properties: {} },
|
|
174
190
|
cost: 0,
|
|
175
191
|
tier: 'free'
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: 'roadmap_add',
|
|
195
|
+
description: 'Add idea to dev roadmap. FREE.',
|
|
196
|
+
inputSchema: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
properties: {
|
|
199
|
+
idea: { type: 'string', description: 'The idea or feature to add' },
|
|
200
|
+
priority: { type: 'string', enum: ['low', 'medium', 'high', 'critical'], default: 'medium' },
|
|
201
|
+
tags: { type: 'array', items: { type: 'string' }, description: 'Tags for categorization' }
|
|
202
|
+
},
|
|
203
|
+
required: ['idea']
|
|
204
|
+
},
|
|
205
|
+
cost: 0,
|
|
206
|
+
tier: 'free'
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'roadmap_list',
|
|
210
|
+
description: 'List roadmap ideas. FREE.',
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: 'object',
|
|
213
|
+
properties: {
|
|
214
|
+
filter: { type: 'string', description: 'Filter by priority or tag' }
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
cost: 0,
|
|
218
|
+
tier: 'free'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: 'roadmap_update',
|
|
222
|
+
description: 'Update roadmap item status. FREE.',
|
|
223
|
+
inputSchema: {
|
|
224
|
+
type: 'object',
|
|
225
|
+
properties: {
|
|
226
|
+
id: { type: 'string', description: 'Roadmap item ID' },
|
|
227
|
+
status: { type: 'string', enum: ['idea', 'planned', 'in_progress', 'done', 'archived'] },
|
|
228
|
+
priority: { type: 'string', enum: ['low', 'medium', 'high', 'critical'] },
|
|
229
|
+
notes: { type: 'string', description: 'Additional notes' }
|
|
230
|
+
},
|
|
231
|
+
required: ['id']
|
|
232
|
+
},
|
|
233
|
+
cost: 0,
|
|
234
|
+
tier: 'free'
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'roadmap_delete',
|
|
238
|
+
description: 'Delete roadmap item. FREE.',
|
|
239
|
+
inputSchema: {
|
|
240
|
+
type: 'object',
|
|
241
|
+
properties: {
|
|
242
|
+
id: { type: 'string', description: 'Roadmap item ID to delete' }
|
|
243
|
+
},
|
|
244
|
+
required: ['id']
|
|
245
|
+
},
|
|
246
|
+
cost: 0,
|
|
247
|
+
tier: 'free'
|
|
176
248
|
}
|
|
177
249
|
];
|
|
178
250
|
|
|
@@ -200,6 +272,14 @@ async function handleTool(name, args) {
|
|
|
200
272
|
return await ideConversation(args.session_id, args.message, args.mode);
|
|
201
273
|
case 'learning_stats':
|
|
202
274
|
return await learningStats();
|
|
275
|
+
case 'roadmap_add':
|
|
276
|
+
return await roadmapAdd(args.idea, args.priority, args.tags);
|
|
277
|
+
case 'roadmap_list':
|
|
278
|
+
return await roadmapList(args.filter);
|
|
279
|
+
case 'roadmap_update':
|
|
280
|
+
return await roadmapUpdate(args.id, args);
|
|
281
|
+
case 'roadmap_delete':
|
|
282
|
+
return await roadmapDelete(args.id);
|
|
203
283
|
default:
|
|
204
284
|
return { error: `Unknown beacon tool: ${name}` };
|
|
205
285
|
}
|
|
@@ -220,5 +300,11 @@ module.exports = {
|
|
|
220
300
|
priceIt,
|
|
221
301
|
compute,
|
|
222
302
|
ideConversation,
|
|
223
|
-
learningStats
|
|
303
|
+
learningStats,
|
|
304
|
+
roadmapAdd,
|
|
305
|
+
roadmapList,
|
|
306
|
+
roadmapUpdate,
|
|
307
|
+
roadmapDelete,
|
|
308
|
+
// Export roadmap tools separately for FREE tier
|
|
309
|
+
ROADMAP_TOOLS: BEACON_TOOLS.filter(t => t.name.startsWith('roadmap_'))
|
|
224
310
|
};
|
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 50c Librarian Pack - Digital Organization Tools (PRO tier)
|
|
3
|
+
* Bookmark cleanup, CSV organization, folder structure, file dedup
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { apiRequest } = require('../config');
|
|
7
|
+
|
|
8
|
+
// ═══════════════════════════════════════════════════════════════
|
|
9
|
+
// BOOKMARK MANAGEMENT
|
|
10
|
+
// ═══════════════════════════════════════════════════════════════
|
|
11
|
+
|
|
12
|
+
async function bookmarksPrune(bookmarks, options = {}) {
|
|
13
|
+
return apiRequest('POST', '/tools/librarian/bookmarks_prune', {
|
|
14
|
+
bookmarks,
|
|
15
|
+
check_dead: options.checkDead !== false,
|
|
16
|
+
remove_duplicates: options.removeDuplicates !== false
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function bookmarksOrganize(bookmarks, instruction) {
|
|
21
|
+
return apiRequest('POST', '/tools/librarian/bookmarks_organize', {
|
|
22
|
+
bookmarks,
|
|
23
|
+
instruction
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function bookmarksParse(filePath) {
|
|
28
|
+
return apiRequest('POST', '/tools/librarian/bookmarks_parse', { file_path: filePath });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ═══════════════════════════════════════════════════════════════
|
|
32
|
+
// CSV MANAGEMENT
|
|
33
|
+
// ═══════════════════════════════════════════════════════════════
|
|
34
|
+
|
|
35
|
+
async function csvClean(csvContent, options = {}) {
|
|
36
|
+
return apiRequest('POST', '/tools/librarian/csv_clean', {
|
|
37
|
+
csv: csvContent,
|
|
38
|
+
remove_empty: options.removeEmpty !== false,
|
|
39
|
+
trim_whitespace: options.trimWhitespace !== false,
|
|
40
|
+
remove_duplicates: options.removeDuplicates || false,
|
|
41
|
+
normalize_headers: options.normalizeHeaders || false
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function csvOrganize(csvContent, instruction) {
|
|
46
|
+
return apiRequest('POST', '/tools/librarian/csv_organize', {
|
|
47
|
+
csv: csvContent,
|
|
48
|
+
instruction
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function csvMerge(csvFiles, options = {}) {
|
|
53
|
+
return apiRequest('POST', '/tools/librarian/csv_merge', {
|
|
54
|
+
files: csvFiles,
|
|
55
|
+
deduplicate: options.deduplicate || false,
|
|
56
|
+
key_column: options.keyColumn
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function csvSplit(csvContent, options) {
|
|
61
|
+
return apiRequest('POST', '/tools/librarian/csv_split', {
|
|
62
|
+
csv: csvContent,
|
|
63
|
+
split_by: options.splitBy,
|
|
64
|
+
max_rows: options.maxRows
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ═══════════════════════════════════════════════════════════════
|
|
69
|
+
// FOLDER MANAGEMENT
|
|
70
|
+
// ═══════════════════════════════════════════════════════════════
|
|
71
|
+
|
|
72
|
+
async function folderAnalyze(fileList) {
|
|
73
|
+
return apiRequest('POST', '/tools/librarian/folder_analyze', { files: fileList });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function folderOrganize(fileList, instruction) {
|
|
77
|
+
return apiRequest('POST', '/tools/librarian/folder_organize', {
|
|
78
|
+
files: fileList,
|
|
79
|
+
instruction
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function folderDedup(fileList, options = {}) {
|
|
84
|
+
return apiRequest('POST', '/tools/librarian/folder_dedup', {
|
|
85
|
+
files: fileList,
|
|
86
|
+
by_name: options.byName || false,
|
|
87
|
+
by_hash: options.byHash || false,
|
|
88
|
+
by_size: options.bySize || true
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function folderPlan(fileList, instruction) {
|
|
93
|
+
return apiRequest('POST', '/tools/librarian/folder_plan', {
|
|
94
|
+
files: fileList,
|
|
95
|
+
instruction
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ═══════════════════════════════════════════════════════════════
|
|
100
|
+
// TOOL DEFINITIONS
|
|
101
|
+
// ═══════════════════════════════════════════════════════════════
|
|
102
|
+
|
|
103
|
+
const LIBRARIAN_TOOLS = [
|
|
104
|
+
{
|
|
105
|
+
name: 'bookmarks_prune',
|
|
106
|
+
description: 'Check bookmarks for dead links (404s) and duplicates. $0.05',
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: 'object',
|
|
109
|
+
properties: {
|
|
110
|
+
bookmarks: { type: 'array', items: { type: 'object' }, description: 'Array of {url, title, folder?}' },
|
|
111
|
+
check_dead: { type: 'boolean', default: true, description: 'Check for 404s' },
|
|
112
|
+
remove_duplicates: { type: 'boolean', default: true }
|
|
113
|
+
},
|
|
114
|
+
required: ['bookmarks']
|
|
115
|
+
},
|
|
116
|
+
cost: 0.05,
|
|
117
|
+
tier: 'pro'
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'bookmarks_organize',
|
|
121
|
+
description: 'Reorganize bookmarks into folders via LLM instruction. $0.10',
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {
|
|
125
|
+
bookmarks: { type: 'array', items: { type: 'object' }, description: 'Array of bookmarks' },
|
|
126
|
+
instruction: { type: 'string', description: 'How to organize (e.g., "by topic", "by project")' }
|
|
127
|
+
},
|
|
128
|
+
required: ['bookmarks', 'instruction']
|
|
129
|
+
},
|
|
130
|
+
cost: 0.10,
|
|
131
|
+
tier: 'pro'
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'bookmarks_parse',
|
|
135
|
+
description: 'Parse browser bookmark export file. $0.02',
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: 'object',
|
|
138
|
+
properties: {
|
|
139
|
+
file_path: { type: 'string', description: 'Path to bookmarks HTML file' }
|
|
140
|
+
},
|
|
141
|
+
required: ['file_path']
|
|
142
|
+
},
|
|
143
|
+
cost: 0.02,
|
|
144
|
+
tier: 'pro'
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'csv_clean',
|
|
148
|
+
description: 'Clean CSV: remove empty rows, trim whitespace, normalize. $0.03',
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
properties: {
|
|
152
|
+
csv: { type: 'string', description: 'CSV content' },
|
|
153
|
+
remove_empty: { type: 'boolean', default: true },
|
|
154
|
+
trim_whitespace: { type: 'boolean', default: true },
|
|
155
|
+
remove_duplicates: { type: 'boolean', default: false },
|
|
156
|
+
normalize_headers: { type: 'boolean', default: false }
|
|
157
|
+
},
|
|
158
|
+
required: ['csv']
|
|
159
|
+
},
|
|
160
|
+
cost: 0.03,
|
|
161
|
+
tier: 'pro'
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'csv_organize',
|
|
165
|
+
description: 'Reorganize CSV columns/rows via LLM instruction. $0.08',
|
|
166
|
+
inputSchema: {
|
|
167
|
+
type: 'object',
|
|
168
|
+
properties: {
|
|
169
|
+
csv: { type: 'string', description: 'CSV content' },
|
|
170
|
+
instruction: { type: 'string', description: 'How to reorganize' }
|
|
171
|
+
},
|
|
172
|
+
required: ['csv', 'instruction']
|
|
173
|
+
},
|
|
174
|
+
cost: 0.08,
|
|
175
|
+
tier: 'pro'
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'csv_merge',
|
|
179
|
+
description: 'Merge multiple CSVs intelligently. $0.05',
|
|
180
|
+
inputSchema: {
|
|
181
|
+
type: 'object',
|
|
182
|
+
properties: {
|
|
183
|
+
files: { type: 'array', items: { type: 'string' }, description: 'Array of CSV contents' },
|
|
184
|
+
deduplicate: { type: 'boolean', default: false },
|
|
185
|
+
key_column: { type: 'string', description: 'Column to use as key for dedup' }
|
|
186
|
+
},
|
|
187
|
+
required: ['files']
|
|
188
|
+
},
|
|
189
|
+
cost: 0.05,
|
|
190
|
+
tier: 'pro'
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: 'csv_split',
|
|
194
|
+
description: 'Split CSV by column value or row count. $0.03',
|
|
195
|
+
inputSchema: {
|
|
196
|
+
type: 'object',
|
|
197
|
+
properties: {
|
|
198
|
+
csv: { type: 'string', description: 'CSV content' },
|
|
199
|
+
split_by: { type: 'string', description: 'Column name to split by' },
|
|
200
|
+
max_rows: { type: 'number', description: 'Max rows per file' }
|
|
201
|
+
},
|
|
202
|
+
required: ['csv']
|
|
203
|
+
},
|
|
204
|
+
cost: 0.03,
|
|
205
|
+
tier: 'pro'
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: 'folder_analyze',
|
|
209
|
+
description: 'Analyze folder structure: size, types, duplicates. $0.03',
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: 'object',
|
|
212
|
+
properties: {
|
|
213
|
+
files: { type: 'array', items: { type: 'object' }, description: 'Array of {path, size, modified}' }
|
|
214
|
+
},
|
|
215
|
+
required: ['files']
|
|
216
|
+
},
|
|
217
|
+
cost: 0.03,
|
|
218
|
+
tier: 'pro'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: 'folder_organize',
|
|
222
|
+
description: 'Generate folder reorganization plan via LLM. $0.10',
|
|
223
|
+
inputSchema: {
|
|
224
|
+
type: 'object',
|
|
225
|
+
properties: {
|
|
226
|
+
files: { type: 'array', items: { type: 'object' }, description: 'File list' },
|
|
227
|
+
instruction: { type: 'string', description: 'Organization goal' }
|
|
228
|
+
},
|
|
229
|
+
required: ['files', 'instruction']
|
|
230
|
+
},
|
|
231
|
+
cost: 0.10,
|
|
232
|
+
tier: 'pro'
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: 'folder_dedup',
|
|
236
|
+
description: 'Find duplicate files by name/hash/size. $0.05',
|
|
237
|
+
inputSchema: {
|
|
238
|
+
type: 'object',
|
|
239
|
+
properties: {
|
|
240
|
+
files: { type: 'array', items: { type: 'object' }, description: 'Array of {path, size, hash?}' },
|
|
241
|
+
by_name: { type: 'boolean', default: false },
|
|
242
|
+
by_hash: { type: 'boolean', default: false },
|
|
243
|
+
by_size: { type: 'boolean', default: true }
|
|
244
|
+
},
|
|
245
|
+
required: ['files']
|
|
246
|
+
},
|
|
247
|
+
cost: 0.05,
|
|
248
|
+
tier: 'pro'
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: 'folder_plan',
|
|
252
|
+
description: 'Create actionable file organization plan. $0.08',
|
|
253
|
+
inputSchema: {
|
|
254
|
+
type: 'object',
|
|
255
|
+
properties: {
|
|
256
|
+
files: { type: 'array', items: { type: 'object' }, description: 'File list' },
|
|
257
|
+
instruction: { type: 'string', description: 'What to achieve' }
|
|
258
|
+
},
|
|
259
|
+
required: ['files', 'instruction']
|
|
260
|
+
},
|
|
261
|
+
cost: 0.08,
|
|
262
|
+
tier: 'pro'
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
// ═══════════════════════════════════════════════════════════════
|
|
266
|
+
// DOMAIN MANAGEMENT
|
|
267
|
+
// ═══════════════════════════════════════════════════════════════
|
|
268
|
+
{
|
|
269
|
+
name: 'domains_add',
|
|
270
|
+
description: 'Add domain to your portfolio with registrar data. FREE.',
|
|
271
|
+
inputSchema: {
|
|
272
|
+
type: 'object',
|
|
273
|
+
properties: {
|
|
274
|
+
domain: { type: 'string', description: 'Domain name' },
|
|
275
|
+
registrar: { type: 'string', description: 'Registrar name' },
|
|
276
|
+
expiry: { type: 'string', description: 'Expiry date (YYYY-MM-DD)' },
|
|
277
|
+
auto_renew: { type: 'boolean', default: true },
|
|
278
|
+
purchase_price: { type: 'number' },
|
|
279
|
+
category: { type: 'string', description: 'Category/project' },
|
|
280
|
+
notes: { type: 'string' }
|
|
281
|
+
},
|
|
282
|
+
required: ['domain']
|
|
283
|
+
},
|
|
284
|
+
cost: 0,
|
|
285
|
+
tier: 'pro'
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
name: 'domains_list',
|
|
289
|
+
description: 'List your domain portfolio. FREE.',
|
|
290
|
+
inputSchema: {
|
|
291
|
+
type: 'object',
|
|
292
|
+
properties: {
|
|
293
|
+
expiring_within: { type: 'number', description: 'Days until expiry filter' },
|
|
294
|
+
category: { type: 'string' },
|
|
295
|
+
registrar: { type: 'string' }
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
cost: 0,
|
|
299
|
+
tier: 'pro'
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: 'domains_expiring',
|
|
303
|
+
description: 'Get domains expiring soon. FREE.',
|
|
304
|
+
inputSchema: {
|
|
305
|
+
type: 'object',
|
|
306
|
+
properties: {
|
|
307
|
+
days: { type: 'number', default: 30, description: 'Within X days' }
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
cost: 0,
|
|
311
|
+
tier: 'pro'
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: 'domains_suggest',
|
|
315
|
+
description: 'Suggest domains matching your portfolio style. $0.05',
|
|
316
|
+
inputSchema: {
|
|
317
|
+
type: 'object',
|
|
318
|
+
properties: {
|
|
319
|
+
concept: { type: 'string', description: 'What the domain is for' },
|
|
320
|
+
style: { type: 'string', enum: ['short', 'brandable', 'keyword', 'match_portfolio'], default: 'match_portfolio' }
|
|
321
|
+
},
|
|
322
|
+
required: ['concept']
|
|
323
|
+
},
|
|
324
|
+
cost: 0.05,
|
|
325
|
+
tier: 'pro'
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
name: 'domains_value',
|
|
329
|
+
description: 'Estimate domain portfolio value. $0.05',
|
|
330
|
+
inputSchema: {
|
|
331
|
+
type: 'object',
|
|
332
|
+
properties: {
|
|
333
|
+
domains: { type: 'array', items: { type: 'string' }, description: 'Domains to value (or omit for all)' }
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
cost: 0.05,
|
|
337
|
+
tier: 'pro'
|
|
338
|
+
},
|
|
339
|
+
|
|
340
|
+
// ═══════════════════════════════════════════════════════════════
|
|
341
|
+
// EMAIL/WRITING STYLE
|
|
342
|
+
// ═══════════════════════════════════════════════════════════════
|
|
343
|
+
{
|
|
344
|
+
name: 'writing_sample_add',
|
|
345
|
+
description: 'Store a writing sample to learn your style. FREE.',
|
|
346
|
+
inputSchema: {
|
|
347
|
+
type: 'object',
|
|
348
|
+
properties: {
|
|
349
|
+
content: { type: 'string', description: 'Your writing sample' },
|
|
350
|
+
type: { type: 'string', enum: ['email', 'blog', 'social', 'formal', 'casual', 'sales'], default: 'email' },
|
|
351
|
+
audience: { type: 'string', description: 'Who this was written for' },
|
|
352
|
+
context: { type: 'string', description: 'What was the situation' }
|
|
353
|
+
},
|
|
354
|
+
required: ['content']
|
|
355
|
+
},
|
|
356
|
+
cost: 0,
|
|
357
|
+
tier: 'pro'
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: 'writing_style_analyze',
|
|
361
|
+
description: 'Analyze your writing style from samples. $0.08',
|
|
362
|
+
inputSchema: {
|
|
363
|
+
type: 'object',
|
|
364
|
+
properties: {
|
|
365
|
+
type: { type: 'string', description: 'Filter by type' }
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
cost: 0.08,
|
|
369
|
+
tier: 'pro'
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: 'writing_draft',
|
|
373
|
+
description: 'Draft content in YOUR style. $0.10',
|
|
374
|
+
inputSchema: {
|
|
375
|
+
type: 'object',
|
|
376
|
+
properties: {
|
|
377
|
+
purpose: { type: 'string', description: 'What to write' },
|
|
378
|
+
audience: { type: 'string', description: 'Who is this for' },
|
|
379
|
+
type: { type: 'string', enum: ['email', 'blog', 'social', 'formal', 'casual', 'sales'], default: 'email' },
|
|
380
|
+
tone: { type: 'string', description: 'Override detected tone' }
|
|
381
|
+
},
|
|
382
|
+
required: ['purpose']
|
|
383
|
+
},
|
|
384
|
+
cost: 0.10,
|
|
385
|
+
tier: 'pro'
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
name: 'writing_improve',
|
|
389
|
+
description: 'Improve text while keeping your voice. $0.08',
|
|
390
|
+
inputSchema: {
|
|
391
|
+
type: 'object',
|
|
392
|
+
properties: {
|
|
393
|
+
content: { type: 'string', description: 'Text to improve' },
|
|
394
|
+
goal: { type: 'string', enum: ['clarity', 'persuasion', 'brevity', 'warmth', 'authority'], default: 'clarity' }
|
|
395
|
+
},
|
|
396
|
+
required: ['content']
|
|
397
|
+
},
|
|
398
|
+
cost: 0.08,
|
|
399
|
+
tier: 'pro'
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
name: 'writing_roast',
|
|
403
|
+
description: 'Brutally honest feedback on your writing. $0.05',
|
|
404
|
+
inputSchema: {
|
|
405
|
+
type: 'object',
|
|
406
|
+
properties: {
|
|
407
|
+
content: { type: 'string', description: 'Text to roast' },
|
|
408
|
+
context: { type: 'string', description: 'What this is for' }
|
|
409
|
+
},
|
|
410
|
+
required: ['content']
|
|
411
|
+
},
|
|
412
|
+
cost: 0.05,
|
|
413
|
+
tier: 'pro'
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
name: 'email_templates_list',
|
|
417
|
+
description: 'List your email templates by situation. FREE.',
|
|
418
|
+
inputSchema: {
|
|
419
|
+
type: 'object',
|
|
420
|
+
properties: {
|
|
421
|
+
situation: { type: 'string', description: 'Filter by situation type' }
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
cost: 0,
|
|
425
|
+
tier: 'pro'
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
name: 'email_generate',
|
|
429
|
+
description: 'Generate email from your templates + style. $0.08',
|
|
430
|
+
inputSchema: {
|
|
431
|
+
type: 'object',
|
|
432
|
+
properties: {
|
|
433
|
+
to: { type: 'string', description: 'Recipient description' },
|
|
434
|
+
purpose: { type: 'string', description: 'What the email should accomplish' },
|
|
435
|
+
context: { type: 'string', description: 'Background info' },
|
|
436
|
+
tone: { type: 'string', enum: ['professional', 'friendly', 'urgent', 'apologetic', 'persuasive'] }
|
|
437
|
+
},
|
|
438
|
+
required: ['purpose']
|
|
439
|
+
},
|
|
440
|
+
cost: 0.08,
|
|
441
|
+
tier: 'pro'
|
|
442
|
+
}
|
|
443
|
+
];
|
|
444
|
+
|
|
445
|
+
// ═══════════════════════════════════════════════════════════════
|
|
446
|
+
// TOOL HANDLER
|
|
447
|
+
// ═══════════════════════════════════════════════════════════════
|
|
448
|
+
|
|
449
|
+
async function handleTool(name, args) {
|
|
450
|
+
try {
|
|
451
|
+
switch (name) {
|
|
452
|
+
case 'bookmarks_prune':
|
|
453
|
+
return await bookmarksPrune(args.bookmarks, args);
|
|
454
|
+
case 'bookmarks_organize':
|
|
455
|
+
return await bookmarksOrganize(args.bookmarks, args.instruction);
|
|
456
|
+
case 'bookmarks_parse':
|
|
457
|
+
return await bookmarksParse(args.file_path);
|
|
458
|
+
case 'csv_clean':
|
|
459
|
+
return await csvClean(args.csv, args);
|
|
460
|
+
case 'csv_organize':
|
|
461
|
+
return await csvOrganize(args.csv, args.instruction);
|
|
462
|
+
case 'csv_merge':
|
|
463
|
+
return await csvMerge(args.files, args);
|
|
464
|
+
case 'csv_split':
|
|
465
|
+
return await csvSplit(args.csv, args);
|
|
466
|
+
case 'folder_analyze':
|
|
467
|
+
return await folderAnalyze(args.files);
|
|
468
|
+
case 'folder_organize':
|
|
469
|
+
return await folderOrganize(args.files, args.instruction);
|
|
470
|
+
case 'folder_dedup':
|
|
471
|
+
return await folderDedup(args.files, args);
|
|
472
|
+
case 'folder_plan':
|
|
473
|
+
return await folderPlan(args.files, args.instruction);
|
|
474
|
+
default:
|
|
475
|
+
return { error: `Unknown librarian tool: ${name}` };
|
|
476
|
+
}
|
|
477
|
+
} catch (e) {
|
|
478
|
+
return { error: e.message };
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
module.exports = {
|
|
483
|
+
LIBRARIAN_TOOLS,
|
|
484
|
+
handleTool,
|
|
485
|
+
bookmarksPrune,
|
|
486
|
+
bookmarksOrganize,
|
|
487
|
+
bookmarksParse,
|
|
488
|
+
csvClean,
|
|
489
|
+
csvOrganize,
|
|
490
|
+
csvMerge,
|
|
491
|
+
csvSplit,
|
|
492
|
+
folderAnalyze,
|
|
493
|
+
folderOrganize,
|
|
494
|
+
folderDedup,
|
|
495
|
+
folderPlan
|
|
496
|
+
};
|
package/lib/packs.js
CHANGED
|
@@ -26,11 +26,11 @@ const PACKS = {
|
|
|
26
26
|
},
|
|
27
27
|
vault: {
|
|
28
28
|
name: 'vault',
|
|
29
|
-
description: 'Secure
|
|
30
|
-
tools:
|
|
29
|
+
description: 'Secure storage + Dewey index card catalog',
|
|
30
|
+
tools: 19,
|
|
31
31
|
tier: 'free',
|
|
32
32
|
always_on: true,
|
|
33
|
-
highlights: ['
|
|
33
|
+
highlights: ['vault_yolo', 'dewey_add', 'dewey_search', 'dewey_stats']
|
|
34
34
|
},
|
|
35
35
|
|
|
36
36
|
// === STARTER TIER ($29/mo) ===
|
|
@@ -73,6 +73,13 @@ const PACKS = {
|
|
|
73
73
|
tier: 'pro',
|
|
74
74
|
highlights: ['ux_list_blocks', 'ux_ab_compare', 'ux_a11y_check']
|
|
75
75
|
},
|
|
76
|
+
librarian: {
|
|
77
|
+
name: 'librarian',
|
|
78
|
+
description: 'Digital asset manager - domains, bookmarks, CSV, folders, writing style',
|
|
79
|
+
tools: 24,
|
|
80
|
+
tier: 'pro',
|
|
81
|
+
highlights: ['domains_expiring', 'writing_draft', 'bookmarks_prune', 'csv_clean']
|
|
82
|
+
},
|
|
76
83
|
|
|
77
84
|
// === ENTERPRISE TIER ($499/mo) ===
|
|
78
85
|
labs_plus: {
|
|
@@ -110,6 +117,17 @@ const TOOL_TIERS = {
|
|
|
110
117
|
vault_get: 'free',
|
|
111
118
|
vault_list: 'free',
|
|
112
119
|
vault_delete: 'free',
|
|
120
|
+
vault_add_proxies: 'free',
|
|
121
|
+
vault_get_proxy: 'free',
|
|
122
|
+
vault_list_proxies: 'free',
|
|
123
|
+
// Dewey Index (free - local vault)
|
|
124
|
+
dewey_add: 'free',
|
|
125
|
+
dewey_get: 'free',
|
|
126
|
+
dewey_search: 'free',
|
|
127
|
+
dewey_list: 'free',
|
|
128
|
+
dewey_update: 'free',
|
|
129
|
+
dewey_delete: 'free',
|
|
130
|
+
dewey_stats: 'free',
|
|
113
131
|
|
|
114
132
|
// STARTER ($29/mo)
|
|
115
133
|
hints: 'starter',
|
|
@@ -166,6 +184,37 @@ const TOOL_TIERS = {
|
|
|
166
184
|
ux_a11y_check: 'pro',
|
|
167
185
|
ux_color_palette: 'pro',
|
|
168
186
|
ux_contrast_check: 'pro',
|
|
187
|
+
// Librarian tools
|
|
188
|
+
bookmarks_prune: 'pro',
|
|
189
|
+
bookmarks_organize: 'pro',
|
|
190
|
+
bookmarks_parse: 'pro',
|
|
191
|
+
csv_clean: 'pro',
|
|
192
|
+
csv_organize: 'pro',
|
|
193
|
+
csv_merge: 'pro',
|
|
194
|
+
csv_split: 'pro',
|
|
195
|
+
folder_analyze: 'pro',
|
|
196
|
+
folder_organize: 'pro',
|
|
197
|
+
folder_dedup: 'pro',
|
|
198
|
+
folder_plan: 'pro',
|
|
199
|
+
// Domain portfolio
|
|
200
|
+
domains_add: 'pro',
|
|
201
|
+
domains_list: 'pro',
|
|
202
|
+
domains_expiring: 'pro',
|
|
203
|
+
domains_suggest: 'pro',
|
|
204
|
+
domains_value: 'pro',
|
|
205
|
+
// Writing style
|
|
206
|
+
writing_sample_add: 'pro',
|
|
207
|
+
writing_style_analyze: 'pro',
|
|
208
|
+
writing_draft: 'pro',
|
|
209
|
+
writing_improve: 'pro',
|
|
210
|
+
writing_roast: 'pro',
|
|
211
|
+
email_templates_list: 'pro',
|
|
212
|
+
email_generate: 'pro',
|
|
213
|
+
// Roadmap (free - per user)
|
|
214
|
+
roadmap_add: 'free',
|
|
215
|
+
roadmap_list: 'free',
|
|
216
|
+
roadmap_update: 'free',
|
|
217
|
+
roadmap_delete: 'free',
|
|
169
218
|
|
|
170
219
|
// ENTERPRISE ($499/mo)
|
|
171
220
|
genius_plus: 'enterprise',
|
|
@@ -346,7 +395,7 @@ const PACK_TOOLS = [
|
|
|
346
395
|
inputSchema: {
|
|
347
396
|
type: 'object',
|
|
348
397
|
properties: {
|
|
349
|
-
pack: { type: 'string', description: 'Pack name', enum: ['beacon', 'labs', 'labs_plus', 'whm', 'cf', 'wp', 'ux'] }
|
|
398
|
+
pack: { type: 'string', description: 'Pack name', enum: ['beacon', 'labs', 'labs_plus', 'whm', 'cf', 'wp', 'ux', 'librarian'] }
|
|
350
399
|
},
|
|
351
400
|
required: ['pack']
|
|
352
401
|
}
|
package/lib/vault.js
CHANGED
|
@@ -304,6 +304,298 @@ async function yolo(passphrase) {
|
|
|
304
304
|
return unlock(passphrase, 365 * 24 * 3600);
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
// Proxy list management
|
|
308
|
+
async function addProxies(name, proxyList) {
|
|
309
|
+
const masterKey = await getMasterKey();
|
|
310
|
+
if (!masterKey) throw new Error('Vault locked. Unlock first.');
|
|
311
|
+
|
|
312
|
+
const proxies = typeof proxyList === 'string'
|
|
313
|
+
? proxyList.split('\n').map(p => p.trim()).filter(p => p && p.includes(':'))
|
|
314
|
+
: proxyList;
|
|
315
|
+
|
|
316
|
+
const data = JSON.stringify({ proxies, added_at: Date.now(), count: proxies.length });
|
|
317
|
+
const encrypted = encrypt(data, masterKey);
|
|
318
|
+
|
|
319
|
+
if (MODE === 'local') {
|
|
320
|
+
const db = loadLocalDB();
|
|
321
|
+
db.credentials[`proxies/${name}`] = { encrypted, updated_at: Date.now() };
|
|
322
|
+
saveLocalDB(db);
|
|
323
|
+
} else {
|
|
324
|
+
await apiRequest('POST', '/vault/credentials', { name: `proxies/${name}`, encrypted });
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return { success: true, name, count: proxies.length };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async function getProxies(name, random = false) {
|
|
331
|
+
const masterKey = await getMasterKey();
|
|
332
|
+
if (!masterKey) throw new Error('Vault locked. Unlock first.');
|
|
333
|
+
|
|
334
|
+
let encrypted;
|
|
335
|
+
const fullName = `proxies/${name}`;
|
|
336
|
+
|
|
337
|
+
if (MODE === 'local') {
|
|
338
|
+
const db = loadLocalDB();
|
|
339
|
+
if (!db.credentials[fullName]) throw new Error(`Proxy list not found: ${name}`);
|
|
340
|
+
encrypted = db.credentials[fullName].encrypted;
|
|
341
|
+
} else {
|
|
342
|
+
const response = await apiRequest('GET', `/vault/credentials/${encodeURIComponent(fullName)}`);
|
|
343
|
+
if (!response?.encrypted) throw new Error(`Proxy list not found: ${name}`);
|
|
344
|
+
encrypted = response.encrypted;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const data = JSON.parse(decrypt(encrypted, masterKey));
|
|
348
|
+
|
|
349
|
+
if (random) {
|
|
350
|
+
const idx = Math.floor(Math.random() * data.proxies.length);
|
|
351
|
+
return { proxy: data.proxies[idx], total: data.count };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return data;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async function listProxies() {
|
|
358
|
+
const all = await list('proxies');
|
|
359
|
+
return all.map(n => n.replace('proxies/', ''));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ═══════════════════════════════════════════════════════════════
|
|
363
|
+
// DEWEY INDEX - Digital Card Catalog
|
|
364
|
+
// Index cards for files, articles, books, anything
|
|
365
|
+
// ═══════════════════════════════════════════════════════════════
|
|
366
|
+
|
|
367
|
+
async function deweyAdd(card) {
|
|
368
|
+
const masterKey = await getMasterKey();
|
|
369
|
+
if (!masterKey) throw new Error('Vault locked. Unlock first.');
|
|
370
|
+
|
|
371
|
+
const id = card.id || `dew_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
372
|
+
const now = Date.now();
|
|
373
|
+
|
|
374
|
+
const indexCard = {
|
|
375
|
+
id,
|
|
376
|
+
type: card.type || 'file', // file, article, book, idea, snippet, email, url
|
|
377
|
+
title: card.title || 'Untitled',
|
|
378
|
+
location: card.location || null, // file path, URL, or null
|
|
379
|
+
category: card.category || 'uncategorized',
|
|
380
|
+
tags: card.tags || [],
|
|
381
|
+
summary: card.summary || null, // LLM-generated or manual
|
|
382
|
+
content_preview: card.content_preview || null, // first 500 chars
|
|
383
|
+
metadata: {
|
|
384
|
+
author: card.author || null,
|
|
385
|
+
date_created: card.date_created || null,
|
|
386
|
+
date_read: card.date_read || null,
|
|
387
|
+
rating: card.rating || null, // 1-5
|
|
388
|
+
notes: card.notes || null, // personal notes
|
|
389
|
+
source: card.source || null, // goodreads, audible, local, web
|
|
390
|
+
...card.metadata
|
|
391
|
+
},
|
|
392
|
+
indexed_at: now,
|
|
393
|
+
updated_at: now
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const encrypted = encrypt(JSON.stringify(indexCard), masterKey);
|
|
397
|
+
|
|
398
|
+
if (MODE === 'local') {
|
|
399
|
+
const db = loadLocalDB();
|
|
400
|
+
db.credentials[`dewey/${id}`] = { encrypted, updated_at: now };
|
|
401
|
+
saveLocalDB(db);
|
|
402
|
+
} else {
|
|
403
|
+
await apiRequest('POST', '/vault/credentials', { name: `dewey/${id}`, encrypted });
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return { id, title: indexCard.title, type: indexCard.type, indexed: true };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function deweyGet(id) {
|
|
410
|
+
const masterKey = await getMasterKey();
|
|
411
|
+
if (!masterKey) throw new Error('Vault locked. Unlock first.');
|
|
412
|
+
|
|
413
|
+
let encrypted;
|
|
414
|
+
const fullName = `dewey/${id}`;
|
|
415
|
+
|
|
416
|
+
if (MODE === 'local') {
|
|
417
|
+
const db = loadLocalDB();
|
|
418
|
+
if (!db.credentials[fullName]) throw new Error(`Card not found: ${id}`);
|
|
419
|
+
encrypted = db.credentials[fullName].encrypted;
|
|
420
|
+
} else {
|
|
421
|
+
const response = await apiRequest('GET', `/vault/credentials/${encodeURIComponent(fullName)}`);
|
|
422
|
+
if (!response?.encrypted) throw new Error(`Card not found: ${id}`);
|
|
423
|
+
encrypted = response.encrypted;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return JSON.parse(decrypt(encrypted, masterKey));
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async function deweySearch(query) {
|
|
430
|
+
const masterKey = await getMasterKey();
|
|
431
|
+
if (!masterKey) throw new Error('Vault locked. Unlock first.');
|
|
432
|
+
|
|
433
|
+
const allCards = await list('dewey');
|
|
434
|
+
const results = [];
|
|
435
|
+
const q = query.toLowerCase();
|
|
436
|
+
|
|
437
|
+
for (const name of allCards) {
|
|
438
|
+
try {
|
|
439
|
+
let encrypted;
|
|
440
|
+
if (MODE === 'local') {
|
|
441
|
+
const db = loadLocalDB();
|
|
442
|
+
encrypted = db.credentials[name]?.encrypted;
|
|
443
|
+
} else {
|
|
444
|
+
const response = await apiRequest('GET', `/vault/credentials/${encodeURIComponent(name)}`);
|
|
445
|
+
encrypted = response?.encrypted;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (encrypted) {
|
|
449
|
+
const card = JSON.parse(decrypt(encrypted, masterKey));
|
|
450
|
+
// Search in title, tags, category, summary, notes
|
|
451
|
+
const searchable = [
|
|
452
|
+
card.title,
|
|
453
|
+
card.category,
|
|
454
|
+
card.summary,
|
|
455
|
+
card.metadata?.notes,
|
|
456
|
+
card.metadata?.author,
|
|
457
|
+
...(card.tags || [])
|
|
458
|
+
].filter(Boolean).join(' ').toLowerCase();
|
|
459
|
+
|
|
460
|
+
if (searchable.includes(q)) {
|
|
461
|
+
results.push({
|
|
462
|
+
id: card.id,
|
|
463
|
+
type: card.type,
|
|
464
|
+
title: card.title,
|
|
465
|
+
category: card.category,
|
|
466
|
+
tags: card.tags,
|
|
467
|
+
rating: card.metadata?.rating,
|
|
468
|
+
date_read: card.metadata?.date_read
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
} catch {}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return { query, results, count: results.length };
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async function deweyList(options = {}) {
|
|
479
|
+
const masterKey = await getMasterKey();
|
|
480
|
+
if (!masterKey) throw new Error('Vault locked. Unlock first.');
|
|
481
|
+
|
|
482
|
+
const allCards = await list('dewey');
|
|
483
|
+
const results = [];
|
|
484
|
+
|
|
485
|
+
for (const name of allCards) {
|
|
486
|
+
try {
|
|
487
|
+
let encrypted;
|
|
488
|
+
if (MODE === 'local') {
|
|
489
|
+
const db = loadLocalDB();
|
|
490
|
+
encrypted = db.credentials[name]?.encrypted;
|
|
491
|
+
} else {
|
|
492
|
+
const response = await apiRequest('GET', `/vault/credentials/${encodeURIComponent(name)}`);
|
|
493
|
+
encrypted = response?.encrypted;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (encrypted) {
|
|
497
|
+
const card = JSON.parse(decrypt(encrypted, masterKey));
|
|
498
|
+
|
|
499
|
+
// Filter by type
|
|
500
|
+
if (options.type && card.type !== options.type) continue;
|
|
501
|
+
// Filter by category
|
|
502
|
+
if (options.category && card.category !== options.category) continue;
|
|
503
|
+
// Filter by tag
|
|
504
|
+
if (options.tag && !card.tags?.includes(options.tag)) continue;
|
|
505
|
+
// Filter by rating
|
|
506
|
+
if (options.min_rating && (card.metadata?.rating || 0) < options.min_rating) continue;
|
|
507
|
+
|
|
508
|
+
results.push({
|
|
509
|
+
id: card.id,
|
|
510
|
+
type: card.type,
|
|
511
|
+
title: card.title,
|
|
512
|
+
category: card.category,
|
|
513
|
+
tags: card.tags,
|
|
514
|
+
rating: card.metadata?.rating,
|
|
515
|
+
location: card.location,
|
|
516
|
+
indexed_at: card.indexed_at
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
} catch {}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Sort by indexed_at desc
|
|
523
|
+
results.sort((a, b) => (b.indexed_at || 0) - (a.indexed_at || 0));
|
|
524
|
+
|
|
525
|
+
return {
|
|
526
|
+
cards: results.slice(0, options.limit || 50),
|
|
527
|
+
total: results.length,
|
|
528
|
+
filters: { type: options.type, category: options.category, tag: options.tag }
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
async function deweyUpdate(id, updates) {
|
|
533
|
+
const masterKey = await getMasterKey();
|
|
534
|
+
if (!masterKey) throw new Error('Vault locked. Unlock first.');
|
|
535
|
+
|
|
536
|
+
const card = await deweyGet(id);
|
|
537
|
+
|
|
538
|
+
// Merge updates
|
|
539
|
+
if (updates.title) card.title = updates.title;
|
|
540
|
+
if (updates.category) card.category = updates.category;
|
|
541
|
+
if (updates.tags) card.tags = updates.tags;
|
|
542
|
+
if (updates.summary) card.summary = updates.summary;
|
|
543
|
+
if (updates.rating !== undefined) card.metadata.rating = updates.rating;
|
|
544
|
+
if (updates.notes) card.metadata.notes = updates.notes;
|
|
545
|
+
if (updates.date_read) card.metadata.date_read = updates.date_read;
|
|
546
|
+
card.updated_at = Date.now();
|
|
547
|
+
|
|
548
|
+
const encrypted = encrypt(JSON.stringify(card), masterKey);
|
|
549
|
+
|
|
550
|
+
if (MODE === 'local') {
|
|
551
|
+
const db = loadLocalDB();
|
|
552
|
+
db.credentials[`dewey/${id}`] = { encrypted, updated_at: card.updated_at };
|
|
553
|
+
saveLocalDB(db);
|
|
554
|
+
} else {
|
|
555
|
+
await apiRequest('POST', '/vault/credentials', { name: `dewey/${id}`, encrypted });
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
return { id, updated: true };
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async function deweyDelete(id) {
|
|
562
|
+
return remove(`dewey/${id}`);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
async function deweyStats() {
|
|
566
|
+
const masterKey = await getMasterKey();
|
|
567
|
+
if (!masterKey) throw new Error('Vault locked. Unlock first.');
|
|
568
|
+
|
|
569
|
+
const allCards = await list('dewey');
|
|
570
|
+
const stats = {
|
|
571
|
+
total: allCards.length,
|
|
572
|
+
by_type: {},
|
|
573
|
+
by_category: {},
|
|
574
|
+
rated: 0,
|
|
575
|
+
unread: 0
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
for (const name of allCards) {
|
|
579
|
+
try {
|
|
580
|
+
let encrypted;
|
|
581
|
+
if (MODE === 'local') {
|
|
582
|
+
const db = loadLocalDB();
|
|
583
|
+
encrypted = db.credentials[name]?.encrypted;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (encrypted) {
|
|
587
|
+
const card = JSON.parse(decrypt(encrypted, masterKey));
|
|
588
|
+
stats.by_type[card.type] = (stats.by_type[card.type] || 0) + 1;
|
|
589
|
+
stats.by_category[card.category] = (stats.by_category[card.category] || 0) + 1;
|
|
590
|
+
if (card.metadata?.rating) stats.rated++;
|
|
591
|
+
if (card.type === 'book' && !card.metadata?.date_read) stats.unread++;
|
|
592
|
+
}
|
|
593
|
+
} catch {}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return stats;
|
|
597
|
+
}
|
|
598
|
+
|
|
307
599
|
// Tool definitions for MCP
|
|
308
600
|
const VAULT_TOOLS = [
|
|
309
601
|
{ name: 'vault_status', description: 'Check vault status. FREE.', inputSchema: { type: 'object', properties: {} } },
|
|
@@ -314,7 +606,44 @@ const VAULT_TOOLS = [
|
|
|
314
606
|
{ name: 'vault_add', description: 'Add credential to vault. FREE.', inputSchema: { type: 'object', properties: { name: { type: 'string' }, value: { type: 'string' } }, required: ['name', 'value'] } },
|
|
315
607
|
{ name: 'vault_get', description: 'Get credential from vault. FREE.', inputSchema: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] } },
|
|
316
608
|
{ name: 'vault_list', description: 'List credentials in vault. FREE.', inputSchema: { type: 'object', properties: { namespace: { type: 'string' } } } },
|
|
317
|
-
{ name: 'vault_delete', description: 'Delete credential from vault. FREE.', inputSchema: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] } }
|
|
609
|
+
{ name: 'vault_delete', description: 'Delete credential from vault. FREE.', inputSchema: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] } },
|
|
610
|
+
{ name: 'vault_add_proxies', description: 'Store proxy list in vault. FREE.', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'List name (e.g., residential, datacenter)' }, proxy_list: { type: 'string', description: 'Newline-separated IP:PORT:USER:PASS' } }, required: ['name', 'proxy_list'] } },
|
|
611
|
+
{ name: 'vault_get_proxy', description: 'Get proxy from stored list. FREE.', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'List name' }, random: { type: 'boolean', default: true, description: 'Return random proxy' } }, required: ['name'] } },
|
|
612
|
+
{ name: 'vault_list_proxies', description: 'List stored proxy lists. FREE.', inputSchema: { type: 'object', properties: {} } },
|
|
613
|
+
// Dewey Index - Digital Card Catalog
|
|
614
|
+
{ name: 'dewey_add', description: 'Add item to Dewey index (file, article, book, idea). FREE.', inputSchema: { type: 'object', properties: {
|
|
615
|
+
type: { type: 'string', enum: ['file', 'article', 'book', 'idea', 'snippet', 'email', 'url'], default: 'file' },
|
|
616
|
+
title: { type: 'string', description: 'Title of the item' },
|
|
617
|
+
location: { type: 'string', description: 'File path or URL' },
|
|
618
|
+
category: { type: 'string', description: 'Category/topic' },
|
|
619
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
620
|
+
summary: { type: 'string', description: 'Brief summary' },
|
|
621
|
+
author: { type: 'string' },
|
|
622
|
+
rating: { type: 'number', minimum: 1, maximum: 5 },
|
|
623
|
+
notes: { type: 'string', description: 'Personal notes' },
|
|
624
|
+
source: { type: 'string', description: 'Where from (goodreads, audible, local, web)' }
|
|
625
|
+
}, required: ['title'] } },
|
|
626
|
+
{ name: 'dewey_get', description: 'Get full details of indexed item. FREE.', inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] } },
|
|
627
|
+
{ name: 'dewey_search', description: 'Search your Dewey index. FREE.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search in titles, tags, notes, authors' } }, required: ['query'] } },
|
|
628
|
+
{ name: 'dewey_list', description: 'List items in Dewey index with filters. FREE.', inputSchema: { type: 'object', properties: {
|
|
629
|
+
type: { type: 'string', description: 'Filter by type' },
|
|
630
|
+
category: { type: 'string', description: 'Filter by category' },
|
|
631
|
+
tag: { type: 'string', description: 'Filter by tag' },
|
|
632
|
+
min_rating: { type: 'number', description: 'Minimum rating filter' },
|
|
633
|
+
limit: { type: 'number', default: 50 }
|
|
634
|
+
} } },
|
|
635
|
+
{ name: 'dewey_update', description: 'Update indexed item. FREE.', inputSchema: { type: 'object', properties: {
|
|
636
|
+
id: { type: 'string' },
|
|
637
|
+
title: { type: 'string' },
|
|
638
|
+
category: { type: 'string' },
|
|
639
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
640
|
+
summary: { type: 'string' },
|
|
641
|
+
rating: { type: 'number' },
|
|
642
|
+
notes: { type: 'string' },
|
|
643
|
+
date_read: { type: 'string', description: 'When you read/consumed it' }
|
|
644
|
+
}, required: ['id'] } },
|
|
645
|
+
{ name: 'dewey_delete', description: 'Remove item from Dewey index. FREE.', inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] } },
|
|
646
|
+
{ name: 'dewey_stats', description: 'Get stats on your Dewey index. FREE.', inputSchema: { type: 'object', properties: {} } }
|
|
318
647
|
];
|
|
319
648
|
|
|
320
649
|
// Handle vault tool calls
|
|
@@ -330,6 +659,17 @@ async function handleTool(name, args) {
|
|
|
330
659
|
case 'vault_get': return { value: await get(args.name) };
|
|
331
660
|
case 'vault_list': return { credentials: await list(args.namespace) };
|
|
332
661
|
case 'vault_delete': return await remove(args.name);
|
|
662
|
+
case 'vault_add_proxies': return await addProxies(args.name, args.proxy_list);
|
|
663
|
+
case 'vault_get_proxy': return await getProxies(args.name, args.random !== false);
|
|
664
|
+
case 'vault_list_proxies': return { lists: await listProxies() };
|
|
665
|
+
// Dewey Index
|
|
666
|
+
case 'dewey_add': return await deweyAdd(args);
|
|
667
|
+
case 'dewey_get': return await deweyGet(args.id);
|
|
668
|
+
case 'dewey_search': return await deweySearch(args.query);
|
|
669
|
+
case 'dewey_list': return await deweyList(args);
|
|
670
|
+
case 'dewey_update': return await deweyUpdate(args.id, args);
|
|
671
|
+
case 'dewey_delete': return await deweyDelete(args.id);
|
|
672
|
+
case 'dewey_stats': return await deweyStats();
|
|
333
673
|
default: return { error: `Unknown vault tool: ${name}` };
|
|
334
674
|
}
|
|
335
675
|
} catch (e) {
|
|
@@ -350,5 +690,15 @@ module.exports = {
|
|
|
350
690
|
list,
|
|
351
691
|
remove,
|
|
352
692
|
isUnlocked,
|
|
353
|
-
getMasterKey
|
|
693
|
+
getMasterKey,
|
|
694
|
+
addProxies,
|
|
695
|
+
getProxies,
|
|
696
|
+
listProxies,
|
|
697
|
+
deweyAdd,
|
|
698
|
+
deweyGet,
|
|
699
|
+
deweySearch,
|
|
700
|
+
deweyList,
|
|
701
|
+
deweyUpdate,
|
|
702
|
+
deweyDelete,
|
|
703
|
+
deweyStats
|
|
354
704
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "50c",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "AI toolkit. One install, 100+ tools.
|
|
3
|
+
"version": "2.5.1",
|
|
4
|
+
"description": "AI toolkit with Dewey index. One install, 100+ tools.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"50c": "./bin/50c.js"
|
|
@@ -19,7 +19,10 @@
|
|
|
19
19
|
"cloudflare",
|
|
20
20
|
"whm",
|
|
21
21
|
"cpanel",
|
|
22
|
-
"wordpress"
|
|
22
|
+
"wordpress",
|
|
23
|
+
"librarian",
|
|
24
|
+
"bookmarks",
|
|
25
|
+
"csv"
|
|
23
26
|
],
|
|
24
27
|
"author": "genxis.com",
|
|
25
28
|
"license": "MIT",
|