apexbot 1.0.2 → 1.0.5

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.
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ /**
3
+ * ApexBot Skills System
4
+ *
5
+ * Skills are collections of tools for specific integrations (Obsidian, Spotify, etc.)
6
+ * Similar to Clawdbot's skills/integrations architecture.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.SkillManager = void 0;
43
+ exports.getSkillManager = getSkillManager;
44
+ exports.initSkillManager = initSkillManager;
45
+ const events_1 = require("events");
46
+ const fs = __importStar(require("fs/promises"));
47
+ const path = __importStar(require("path"));
48
+ const tools_1 = require("../tools");
49
+ class SkillManager extends events_1.EventEmitter {
50
+ skills = new Map();
51
+ enabledSkills = new Set();
52
+ skillConfigs = new Map();
53
+ configDir;
54
+ constructor(configDir) {
55
+ super();
56
+ this.configDir = configDir;
57
+ }
58
+ async loadConfig() {
59
+ try {
60
+ const configPath = path.join(this.configDir, 'skills.json');
61
+ const data = await fs.readFile(configPath, 'utf-8');
62
+ const config = JSON.parse(data);
63
+ this.enabledSkills = new Set(config.enabled || []);
64
+ this.skillConfigs = new Map(Object.entries(config.configs || {}));
65
+ console.log('[Skills] Loaded config, enabled:', Array.from(this.enabledSkills));
66
+ }
67
+ catch (error) {
68
+ // Config doesn't exist yet, use defaults
69
+ console.log('[Skills] No config found, using defaults');
70
+ }
71
+ }
72
+ async saveConfig() {
73
+ const configPath = path.join(this.configDir, 'skills.json');
74
+ const config = {
75
+ enabled: Array.from(this.enabledSkills),
76
+ configs: Object.fromEntries(this.skillConfigs),
77
+ };
78
+ await fs.mkdir(this.configDir, { recursive: true });
79
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
80
+ console.log('[Skills] Saved config');
81
+ }
82
+ register(skill) {
83
+ const name = skill.manifest.name;
84
+ this.skills.set(name, skill);
85
+ console.log(`[Skills] Registered: ${name} v${skill.manifest.version}`);
86
+ this.emit('skill:registered', { name, manifest: skill.manifest });
87
+ }
88
+ async enable(name, config) {
89
+ const skill = this.skills.get(name);
90
+ if (!skill) {
91
+ console.error(`[Skills] Skill not found: ${name}`);
92
+ return false;
93
+ }
94
+ try {
95
+ // Store config
96
+ if (config) {
97
+ this.skillConfigs.set(name, config);
98
+ }
99
+ // Initialize skill
100
+ const skillConfig = this.skillConfigs.get(name) || {};
101
+ if (skill.initialize) {
102
+ await skill.initialize(skillConfig);
103
+ }
104
+ // Register tools
105
+ for (const tool of skill.tools) {
106
+ tools_1.toolRegistry.register(tool);
107
+ }
108
+ this.enabledSkills.add(name);
109
+ await this.saveConfig();
110
+ console.log(`[Skills] Enabled: ${name}`);
111
+ this.emit('skill:enabled', { name });
112
+ return true;
113
+ }
114
+ catch (error) {
115
+ console.error(`[Skills] Failed to enable ${name}:`, error.message);
116
+ return false;
117
+ }
118
+ }
119
+ async disable(name) {
120
+ const skill = this.skills.get(name);
121
+ if (!skill) {
122
+ return false;
123
+ }
124
+ try {
125
+ // Shutdown skill
126
+ if (skill.shutdown) {
127
+ await skill.shutdown();
128
+ }
129
+ // Unregister tools
130
+ for (const tool of skill.tools) {
131
+ tools_1.toolRegistry.unregister(tool.definition.name);
132
+ }
133
+ this.enabledSkills.delete(name);
134
+ await this.saveConfig();
135
+ console.log(`[Skills] Disabled: ${name}`);
136
+ this.emit('skill:disabled', { name });
137
+ return true;
138
+ }
139
+ catch (error) {
140
+ console.error(`[Skills] Failed to disable ${name}:`, error.message);
141
+ return false;
142
+ }
143
+ }
144
+ isEnabled(name) {
145
+ return this.enabledSkills.has(name);
146
+ }
147
+ get(name) {
148
+ return this.skills.get(name);
149
+ }
150
+ list() {
151
+ return Array.from(this.skills.values()).map(skill => ({
152
+ name: skill.manifest.name,
153
+ version: skill.manifest.version,
154
+ description: skill.manifest.description,
155
+ category: skill.manifest.category,
156
+ enabled: this.enabledSkills.has(skill.manifest.name),
157
+ tools: skill.tools.map(t => t.definition.name),
158
+ }));
159
+ }
160
+ listEnabled() {
161
+ return this.list().filter(s => s.enabled);
162
+ }
163
+ listByCategory(category) {
164
+ return this.list().filter(s => s.category === category);
165
+ }
166
+ getConfig(name) {
167
+ return this.skillConfigs.get(name);
168
+ }
169
+ async setConfig(name, config) {
170
+ this.skillConfigs.set(name, config);
171
+ await this.saveConfig();
172
+ // Re-initialize if enabled
173
+ if (this.enabledSkills.has(name)) {
174
+ const skill = this.skills.get(name);
175
+ if (skill?.initialize) {
176
+ await skill.initialize(config);
177
+ }
178
+ }
179
+ }
180
+ // Initialize all enabled skills on startup
181
+ async initializeEnabled() {
182
+ for (const name of this.enabledSkills) {
183
+ const skill = this.skills.get(name);
184
+ if (skill) {
185
+ try {
186
+ const config = this.skillConfigs.get(name) || {};
187
+ if (skill.initialize) {
188
+ await skill.initialize(config);
189
+ }
190
+ for (const tool of skill.tools) {
191
+ tools_1.toolRegistry.register(tool);
192
+ }
193
+ console.log(`[Skills] Initialized: ${name}`);
194
+ }
195
+ catch (error) {
196
+ console.error(`[Skills] Failed to initialize ${name}:`, error.message);
197
+ }
198
+ }
199
+ }
200
+ }
201
+ }
202
+ exports.SkillManager = SkillManager;
203
+ // Global skill manager instance (initialized in gateway)
204
+ let skillManager = null;
205
+ function getSkillManager() {
206
+ return skillManager;
207
+ }
208
+ function initSkillManager(configDir) {
209
+ skillManager = new SkillManager(configDir);
210
+ return skillManager;
211
+ }
212
+ exports.default = { SkillManager, getSkillManager, initSkillManager };
@@ -0,0 +1,440 @@
1
+ "use strict";
2
+ /**
3
+ * Obsidian Skill
4
+ *
5
+ * Integration with Obsidian note-taking app.
6
+ * Supports reading, writing, searching notes, and managing daily notes.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ const fs = __importStar(require("fs/promises"));
43
+ const path = __importStar(require("path"));
44
+ let vaultPath = '';
45
+ const searchNotesTool = {
46
+ definition: {
47
+ name: 'obsidian_search',
48
+ description: 'Search for notes in Obsidian vault by content or filename.',
49
+ category: 'productivity',
50
+ parameters: [
51
+ {
52
+ name: 'query',
53
+ type: 'string',
54
+ description: 'Search query',
55
+ required: true,
56
+ },
57
+ {
58
+ name: 'type',
59
+ type: 'string',
60
+ description: 'Search type: content, filename, tag',
61
+ required: false,
62
+ default: 'content',
63
+ enum: ['content', 'filename', 'tag'],
64
+ },
65
+ {
66
+ name: 'limit',
67
+ type: 'number',
68
+ description: 'Maximum results',
69
+ required: false,
70
+ default: 10,
71
+ },
72
+ ],
73
+ returns: 'List of matching notes',
74
+ },
75
+ async execute(params, context) {
76
+ const { query, type = 'content', limit = 10 } = params;
77
+ if (!vaultPath) {
78
+ return { success: false, error: 'Obsidian vault not configured' };
79
+ }
80
+ try {
81
+ const results = [];
82
+ async function searchDir(dir) {
83
+ const entries = await fs.readdir(dir, { withFileTypes: true });
84
+ for (const entry of entries) {
85
+ if (results.length >= limit)
86
+ return;
87
+ const fullPath = path.join(dir, entry.name);
88
+ const relativePath = path.relative(vaultPath, fullPath);
89
+ // Skip hidden files/folders
90
+ if (entry.name.startsWith('.'))
91
+ continue;
92
+ if (entry.isDirectory()) {
93
+ await searchDir(fullPath);
94
+ }
95
+ else if (entry.name.endsWith('.md')) {
96
+ const noteName = entry.name.replace('.md', '');
97
+ if (type === 'filename') {
98
+ if (noteName.toLowerCase().includes(query.toLowerCase())) {
99
+ results.push({
100
+ name: noteName,
101
+ path: relativePath,
102
+ });
103
+ }
104
+ }
105
+ else if (type === 'content' || type === 'tag') {
106
+ const content = await fs.readFile(fullPath, 'utf-8');
107
+ if (type === 'tag') {
108
+ const tagPattern = new RegExp(`#${query}\\b`, 'i');
109
+ if (tagPattern.test(content)) {
110
+ results.push({
111
+ name: noteName,
112
+ path: relativePath,
113
+ preview: content.slice(0, 200),
114
+ });
115
+ }
116
+ }
117
+ else {
118
+ if (content.toLowerCase().includes(query.toLowerCase())) {
119
+ const idx = content.toLowerCase().indexOf(query.toLowerCase());
120
+ const start = Math.max(0, idx - 50);
121
+ const end = Math.min(content.length, idx + query.length + 50);
122
+ results.push({
123
+ name: noteName,
124
+ path: relativePath,
125
+ preview: content.slice(start, end),
126
+ });
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ await searchDir(vaultPath);
134
+ return {
135
+ success: true,
136
+ data: {
137
+ query,
138
+ type,
139
+ results,
140
+ count: results.length,
141
+ },
142
+ };
143
+ }
144
+ catch (error) {
145
+ return { success: false, error: error.message };
146
+ }
147
+ },
148
+ };
149
+ const readNoteTool = {
150
+ definition: {
151
+ name: 'obsidian_read',
152
+ description: 'Read the content of an Obsidian note.',
153
+ category: 'productivity',
154
+ parameters: [
155
+ {
156
+ name: 'name',
157
+ type: 'string',
158
+ description: 'Note name or path (without .md)',
159
+ required: true,
160
+ },
161
+ ],
162
+ returns: 'Note content',
163
+ },
164
+ async execute(params, context) {
165
+ const { name } = params;
166
+ if (!vaultPath) {
167
+ return { success: false, error: 'Obsidian vault not configured' };
168
+ }
169
+ try {
170
+ let notePath = name;
171
+ if (!notePath.endsWith('.md')) {
172
+ notePath += '.md';
173
+ }
174
+ const fullPath = path.join(vaultPath, notePath);
175
+ const content = await fs.readFile(fullPath, 'utf-8');
176
+ // Parse frontmatter if present
177
+ let frontmatter = {};
178
+ let body = content;
179
+ if (content.startsWith('---')) {
180
+ const endIdx = content.indexOf('---', 3);
181
+ if (endIdx !== -1) {
182
+ const yamlStr = content.slice(3, endIdx).trim();
183
+ body = content.slice(endIdx + 3).trim();
184
+ // Simple YAML parsing
185
+ yamlStr.split('\n').forEach(line => {
186
+ const [key, ...valueParts] = line.split(':');
187
+ if (key && valueParts.length) {
188
+ frontmatter[key.trim()] = valueParts.join(':').trim();
189
+ }
190
+ });
191
+ }
192
+ }
193
+ return {
194
+ success: true,
195
+ data: {
196
+ name,
197
+ path: notePath,
198
+ frontmatter,
199
+ content: body,
200
+ },
201
+ };
202
+ }
203
+ catch (error) {
204
+ return { success: false, error: error.message };
205
+ }
206
+ },
207
+ };
208
+ const writeNoteTool = {
209
+ definition: {
210
+ name: 'obsidian_write',
211
+ description: 'Create or update an Obsidian note.',
212
+ category: 'productivity',
213
+ parameters: [
214
+ {
215
+ name: 'name',
216
+ type: 'string',
217
+ description: 'Note name or path (without .md)',
218
+ required: true,
219
+ },
220
+ {
221
+ name: 'content',
222
+ type: 'string',
223
+ description: 'Note content (markdown)',
224
+ required: true,
225
+ },
226
+ {
227
+ name: 'append',
228
+ type: 'boolean',
229
+ description: 'Append to existing note',
230
+ required: false,
231
+ default: false,
232
+ },
233
+ ],
234
+ returns: 'Success confirmation',
235
+ },
236
+ async execute(params, context) {
237
+ const { name, content, append = false } = params;
238
+ if (!vaultPath) {
239
+ return { success: false, error: 'Obsidian vault not configured' };
240
+ }
241
+ try {
242
+ let notePath = name;
243
+ if (!notePath.endsWith('.md')) {
244
+ notePath += '.md';
245
+ }
246
+ const fullPath = path.join(vaultPath, notePath);
247
+ // Ensure directory exists
248
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
249
+ if (append) {
250
+ const existing = await fs.readFile(fullPath, 'utf-8').catch(() => '');
251
+ await fs.writeFile(fullPath, existing + '\n' + content);
252
+ }
253
+ else {
254
+ await fs.writeFile(fullPath, content);
255
+ }
256
+ return {
257
+ success: true,
258
+ data: {
259
+ name,
260
+ path: notePath,
261
+ action: append ? 'appended' : 'created',
262
+ },
263
+ };
264
+ }
265
+ catch (error) {
266
+ return { success: false, error: error.message };
267
+ }
268
+ },
269
+ };
270
+ const dailyNoteTool = {
271
+ definition: {
272
+ name: 'obsidian_daily',
273
+ description: 'Get or create today\'s daily note.',
274
+ category: 'productivity',
275
+ parameters: [
276
+ {
277
+ name: 'date',
278
+ type: 'string',
279
+ description: 'Date (YYYY-MM-DD), defaults to today',
280
+ required: false,
281
+ },
282
+ {
283
+ name: 'append',
284
+ type: 'string',
285
+ description: 'Content to append to daily note',
286
+ required: false,
287
+ },
288
+ ],
289
+ returns: 'Daily note content',
290
+ },
291
+ async execute(params, context) {
292
+ const { date, append } = params;
293
+ if (!vaultPath) {
294
+ return { success: false, error: 'Obsidian vault not configured' };
295
+ }
296
+ try {
297
+ const targetDate = date ? new Date(date) : new Date();
298
+ const dateStr = targetDate.toISOString().split('T')[0];
299
+ const notePath = `Daily Notes/${dateStr}.md`;
300
+ const fullPath = path.join(vaultPath, notePath);
301
+ // Ensure directory exists
302
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
303
+ let content;
304
+ try {
305
+ content = await fs.readFile(fullPath, 'utf-8');
306
+ }
307
+ catch {
308
+ // Create new daily note
309
+ content = `# ${targetDate.toLocaleDateString('en-US', {
310
+ weekday: 'long',
311
+ year: 'numeric',
312
+ month: 'long',
313
+ day: 'numeric'
314
+ })}\n\n`;
315
+ await fs.writeFile(fullPath, content);
316
+ }
317
+ if (append) {
318
+ content += '\n' + append;
319
+ await fs.writeFile(fullPath, content);
320
+ }
321
+ return {
322
+ success: true,
323
+ data: {
324
+ date: dateStr,
325
+ path: notePath,
326
+ content,
327
+ },
328
+ };
329
+ }
330
+ catch (error) {
331
+ return { success: false, error: error.message };
332
+ }
333
+ },
334
+ };
335
+ const listNotesTool = {
336
+ definition: {
337
+ name: 'obsidian_list',
338
+ description: 'List notes in Obsidian vault.',
339
+ category: 'productivity',
340
+ parameters: [
341
+ {
342
+ name: 'folder',
343
+ type: 'string',
344
+ description: 'Folder to list (default: root)',
345
+ required: false,
346
+ default: '',
347
+ },
348
+ {
349
+ name: 'recursive',
350
+ type: 'boolean',
351
+ description: 'List recursively',
352
+ required: false,
353
+ default: false,
354
+ },
355
+ ],
356
+ returns: 'List of notes',
357
+ },
358
+ async execute(params, context) {
359
+ const { folder = '', recursive = false } = params;
360
+ if (!vaultPath) {
361
+ return { success: false, error: 'Obsidian vault not configured' };
362
+ }
363
+ try {
364
+ const targetPath = path.join(vaultPath, folder);
365
+ const notes = [];
366
+ async function listDir(dir) {
367
+ const entries = await fs.readdir(dir, { withFileTypes: true });
368
+ for (const entry of entries) {
369
+ if (entry.name.startsWith('.'))
370
+ continue;
371
+ const fullPath = path.join(dir, entry.name);
372
+ const relativePath = path.relative(vaultPath, fullPath);
373
+ if (entry.isDirectory()) {
374
+ if (recursive) {
375
+ await listDir(fullPath);
376
+ }
377
+ else {
378
+ notes.push({
379
+ name: entry.name,
380
+ path: relativePath,
381
+ isFolder: true,
382
+ });
383
+ }
384
+ }
385
+ else if (entry.name.endsWith('.md')) {
386
+ const stat = await fs.stat(fullPath);
387
+ notes.push({
388
+ name: entry.name.replace('.md', ''),
389
+ path: relativePath,
390
+ isFolder: false,
391
+ modified: stat.mtime,
392
+ size: stat.size,
393
+ });
394
+ }
395
+ }
396
+ }
397
+ await listDir(targetPath);
398
+ return {
399
+ success: true,
400
+ data: {
401
+ folder,
402
+ notes,
403
+ count: notes.length,
404
+ },
405
+ };
406
+ }
407
+ catch (error) {
408
+ return { success: false, error: error.message };
409
+ }
410
+ },
411
+ };
412
+ const manifest = {
413
+ name: 'obsidian',
414
+ version: '1.0.0',
415
+ description: 'Integration with Obsidian note-taking app',
416
+ category: 'productivity',
417
+ icon: 'notes',
418
+ tools: ['obsidian_search', 'obsidian_read', 'obsidian_write', 'obsidian_daily', 'obsidian_list'],
419
+ config: [
420
+ {
421
+ name: 'vaultPath',
422
+ type: 'string',
423
+ description: 'Path to your Obsidian vault folder',
424
+ required: true,
425
+ },
426
+ ],
427
+ };
428
+ const obsidianSkill = {
429
+ manifest,
430
+ tools: [searchNotesTool, readNoteTool, writeNoteTool, dailyNoteTool, listNotesTool],
431
+ async initialize(config) {
432
+ vaultPath = config.vaultPath || '';
433
+ console.log(`[Obsidian] Initialized with vault: ${vaultPath}`);
434
+ },
435
+ async shutdown() {
436
+ vaultPath = '';
437
+ console.log('[Obsidian] Shutdown');
438
+ },
439
+ };
440
+ exports.default = obsidianSkill;