@open-skills-hub/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@open-skills-hub/core",
3
+ "version": "1.0.0",
4
+ "description": "Core library for Open Skills Hub",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./config": {
14
+ "import": "./dist/config/index.js",
15
+ "types": "./dist/config/index.d.ts"
16
+ },
17
+ "./storage": {
18
+ "import": "./dist/storage/index.js",
19
+ "types": "./dist/storage/index.d.ts"
20
+ },
21
+ "./models": {
22
+ "import": "./dist/models/index.js",
23
+ "types": "./dist/models/index.d.ts"
24
+ },
25
+ "./scanner": {
26
+ "import": "./dist/scanner/index.js",
27
+ "types": "./dist/scanner/index.d.ts"
28
+ },
29
+ "./search": {
30
+ "import": "./dist/search/index.js",
31
+ "types": "./dist/search/index.d.ts"
32
+ },
33
+ "./utils": {
34
+ "import": "./dist/utils/index.js",
35
+ "types": "./dist/utils/index.d.ts"
36
+ }
37
+ },
38
+ "scripts": {
39
+ "build": "tsc",
40
+ "dev": "tsc --watch",
41
+ "clean": "rm -rf dist"
42
+ },
43
+ "dependencies": {
44
+ "@skills-guard/core": "^0.2.0",
45
+ "date-fns": "^3.0.6",
46
+ "nanoid": "^5.0.4",
47
+ "pino": "^8.17.2",
48
+ "pino-pretty": "^13.1.3",
49
+ "sql.js": "^1.10.0",
50
+ "yaml": "^2.3.4",
51
+ "zod": "^3.22.4"
52
+ },
53
+ "devDependencies": {
54
+ "@types/sql.js": "^1.4.9"
55
+ },
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "https://github.com/OpenSkillsHub/open-skills-hub.git",
59
+ "directory": "packages/core"
60
+ },
61
+ "bugs": {
62
+ "url": "https://github.com/OpenSkillsHub/open-skills-hub/issues"
63
+ },
64
+ "homepage": "https://github.com/OpenSkillsHub/open-skills-hub#readme",
65
+ "keywords": [
66
+ "skills",
67
+ "ai",
68
+ "agent",
69
+ "mcp",
70
+ "registry",
71
+ "core"
72
+ ],
73
+ "author": "Open Skills Hub Team",
74
+ "license": "MIT"
75
+ }
@@ -0,0 +1,420 @@
1
+ /**
2
+ * Open Skills Hub - Configuration Management
3
+ */
4
+
5
+ import { z } from 'zod';
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import * as os from 'os';
9
+ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
10
+
11
+ // ============================================================================
12
+ // Configuration Schema
13
+ // ============================================================================
14
+
15
+ export const StorageMode = z.enum(['local', 'docker', 'cloud']);
16
+ export type StorageMode = z.infer<typeof StorageMode>;
17
+
18
+ export const AuthMode = z.enum(['local', 'token', 'oauth']);
19
+ export type AuthMode = z.infer<typeof AuthMode>;
20
+
21
+ export const LogLevel = z.enum(['debug', 'info', 'warn', 'error']);
22
+ export type LogLevel = z.infer<typeof LogLevel>;
23
+
24
+ export const CacheConfigSchema = z.object({
25
+ ttl: z.number().default(604800), // 7 days in seconds
26
+ maxSize: z.number().default(524288000), // 500MB
27
+ strategy: z.enum(['lru', 'fifo']).default('lru'),
28
+ autoRefresh: z.boolean().default(true),
29
+ });
30
+ export type CacheConfig = z.infer<typeof CacheConfigSchema>;
31
+
32
+ export const AuthConfigSchema = z.object({
33
+ mode: AuthMode.default('local'),
34
+ requireAuth: z.boolean().default(false),
35
+ jwtSecret: z.string().optional(),
36
+ tokenExpiry: z.number().default(86400), // 24 hours
37
+ });
38
+ export type AuthConfig = z.infer<typeof AuthConfigSchema>;
39
+
40
+ export const ScannerConfigSchema = z.object({
41
+ enabled: z.boolean().default(true),
42
+ timeout: z.number().default(30000), // 30 seconds
43
+ maxFileSize: z.number().default(10485760), // 10MB
44
+ rulesPath: z.string().optional(),
45
+ });
46
+ export type ScannerConfig = z.infer<typeof ScannerConfigSchema>;
47
+
48
+ export const RetryConfigSchema = z.object({
49
+ maxAttempts: z.number().default(5),
50
+ baseDelay: z.number().default(1000),
51
+ maxDelay: z.number().default(3600000), // 1 hour
52
+ backoffMultiplier: z.number().default(2),
53
+ jitter: z.boolean().default(true),
54
+ jitterFactor: z.number().default(0.1),
55
+ });
56
+ export type RetryConfig = z.infer<typeof RetryConfigSchema>;
57
+
58
+ export const ServerConfigSchema = z.object({
59
+ host: z.string().default('localhost'),
60
+ port: z.number().default(3000),
61
+ cors: z.boolean().default(true),
62
+ trustProxy: z.boolean().default(false),
63
+ });
64
+ export type ServerConfig = z.infer<typeof ServerConfigSchema>;
65
+
66
+ export const RateLimitConfigSchema = z.object({
67
+ enabled: z.boolean().default(true),
68
+ windowMs: z.number().default(60000), // 1 minute
69
+ max: z.number().default(100),
70
+ });
71
+ export type RateLimitConfig = z.infer<typeof RateLimitConfigSchema>;
72
+
73
+ export const DatabaseConfigSchema = z.object({
74
+ url: z.string().optional(),
75
+ filename: z.string().default('hub.db'),
76
+ poolSize: z.number().default(10),
77
+ timeout: z.number().default(5000),
78
+ });
79
+ export type DatabaseConfig = z.infer<typeof DatabaseConfigSchema>;
80
+
81
+ export const RegistryConfigSchema = z.object({
82
+ // Storage mode
83
+ mode: StorageMode.default('local'),
84
+ storagePath: z.string().default('~/.open-skills-hub'),
85
+
86
+ // API endpoint (for remote mode)
87
+ apiUrl: z.string().default('https://api.open-skills-hub.io'),
88
+ apiToken: z.string().optional(),
89
+
90
+ // Sub-configurations
91
+ server: ServerConfigSchema.default({}),
92
+ database: DatabaseConfigSchema.default({}),
93
+ cache: CacheConfigSchema.default({}),
94
+ auth: AuthConfigSchema.default({}),
95
+ scanner: ScannerConfigSchema.default({}),
96
+ retry: RetryConfigSchema.default({}),
97
+ rateLimit: RateLimitConfigSchema.default({}),
98
+
99
+ // Logging
100
+ logLevel: LogLevel.default('info'),
101
+ logFormat: z.enum(['json', 'pretty']).default('json'),
102
+
103
+ // Telemetry
104
+ telemetryEnabled: z.boolean().default(true),
105
+ });
106
+ export type RegistryConfig = z.infer<typeof RegistryConfigSchema>;
107
+
108
+ // ============================================================================
109
+ // Configuration Manager
110
+ // ============================================================================
111
+
112
+ const DEFAULT_CONFIG_FILENAME = 'config.yaml';
113
+
114
+ export class ConfigManager {
115
+ private config: RegistryConfig;
116
+ private configPath: string;
117
+ private loaded: boolean = false;
118
+
119
+ constructor(configPath?: string) {
120
+ this.configPath = configPath || this.getDefaultConfigPath();
121
+ this.config = RegistryConfigSchema.parse({});
122
+ }
123
+
124
+ /**
125
+ * Get the default configuration file path
126
+ */
127
+ private getDefaultConfigPath(): string {
128
+ const storagePath = this.expandPath('~/.open-skills-hub');
129
+ return path.join(storagePath, DEFAULT_CONFIG_FILENAME);
130
+ }
131
+
132
+ /**
133
+ * Expand ~ to home directory
134
+ */
135
+ private expandPath(p: string): string {
136
+ if (p.startsWith('~')) {
137
+ return path.join(os.homedir(), p.slice(1));
138
+ }
139
+ return p;
140
+ }
141
+
142
+ /**
143
+ * Get the resolved storage path
144
+ */
145
+ getStoragePath(): string {
146
+ return this.expandPath(this.config.storagePath);
147
+ }
148
+
149
+ /**
150
+ * Get database file path
151
+ */
152
+ getDatabasePath(): string {
153
+ return path.join(
154
+ this.getStoragePath(),
155
+ 'data',
156
+ this.config.database.filename
157
+ );
158
+ }
159
+
160
+ /**
161
+ * Get cache directory path
162
+ */
163
+ getCachePath(): string {
164
+ return path.join(this.getStoragePath(), 'cache');
165
+ }
166
+
167
+ /**
168
+ * Get local skills directory path
169
+ */
170
+ getLocalSkillsPath(): string {
171
+ return path.join(this.getStoragePath(), 'local');
172
+ }
173
+
174
+ /**
175
+ * Get logs directory path
176
+ */
177
+ getLogsPath(): string {
178
+ return path.join(this.getStoragePath(), 'logs');
179
+ }
180
+
181
+ /**
182
+ * Get queue directory path
183
+ */
184
+ getQueuePath(): string {
185
+ return path.join(this.getStoragePath(), 'queue');
186
+ }
187
+
188
+ /**
189
+ * Load configuration from file
190
+ */
191
+ async load(): Promise<RegistryConfig> {
192
+ if (this.loaded) {
193
+ return this.config;
194
+ }
195
+
196
+ try {
197
+ // Check if config file exists
198
+ if (fs.existsSync(this.configPath)) {
199
+ const content = fs.readFileSync(this.configPath, 'utf-8');
200
+ const parsed = parseYaml(content) as Record<string, unknown>;
201
+ this.config = RegistryConfigSchema.parse(parsed);
202
+ }
203
+
204
+ // Override with environment variables
205
+ this.applyEnvironmentOverrides();
206
+
207
+ // Ensure directories exist
208
+ await this.ensureDirectories();
209
+
210
+ this.loaded = true;
211
+ return this.config;
212
+ } catch (error) {
213
+ throw new Error(`Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`);
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Save configuration to file
219
+ */
220
+ async save(): Promise<void> {
221
+ try {
222
+ const dir = path.dirname(this.configPath);
223
+ if (!fs.existsSync(dir)) {
224
+ fs.mkdirSync(dir, { recursive: true });
225
+ }
226
+
227
+ const content = stringifyYaml(this.config, { indent: 2 });
228
+ fs.writeFileSync(this.configPath, content, 'utf-8');
229
+ } catch (error) {
230
+ throw new Error(`Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`);
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Apply environment variable overrides
236
+ */
237
+ private applyEnvironmentOverrides(): void {
238
+ const env = process.env;
239
+
240
+ // Storage mode
241
+ if (env['STORAGE_MODE']) {
242
+ const mode = StorageMode.safeParse(env['STORAGE_MODE']);
243
+ if (mode.success) {
244
+ this.config.mode = mode.data;
245
+ }
246
+ }
247
+
248
+ // Storage path
249
+ if (env['STORAGE_PATH']) {
250
+ this.config.storagePath = env['STORAGE_PATH'];
251
+ }
252
+
253
+ // API configuration
254
+ if (env['SKILLS_REGISTRY_URL']) {
255
+ this.config.apiUrl = env['SKILLS_REGISTRY_URL'];
256
+ }
257
+ if (env['SKILLS_API_TOKEN']) {
258
+ this.config.apiToken = env['SKILLS_API_TOKEN'];
259
+ }
260
+
261
+ // Server configuration
262
+ if (env['PORT']) {
263
+ this.config.server.port = parseInt(env['PORT'], 10);
264
+ }
265
+ if (env['HOST']) {
266
+ this.config.server.host = env['HOST'];
267
+ }
268
+
269
+ // Database URL
270
+ if (env['DATABASE_URL']) {
271
+ this.config.database.url = env['DATABASE_URL'];
272
+ }
273
+
274
+ // Cache configuration
275
+ if (env['CACHE_TTL']) {
276
+ this.config.cache.ttl = parseInt(env['CACHE_TTL'], 10);
277
+ }
278
+ if (env['CACHE_MAX_SIZE']) {
279
+ this.config.cache.maxSize = parseInt(env['CACHE_MAX_SIZE'], 10);
280
+ }
281
+
282
+ // Auth configuration
283
+ if (env['AUTH_ENABLED']) {
284
+ this.config.auth.requireAuth = env['AUTH_ENABLED'] === 'true';
285
+ }
286
+ if (env['JWT_SECRET']) {
287
+ this.config.auth.jwtSecret = env['JWT_SECRET'];
288
+ }
289
+
290
+ // Scanner configuration
291
+ if (env['SCANNER_ENABLED']) {
292
+ this.config.scanner.enabled = env['SCANNER_ENABLED'] === 'true';
293
+ }
294
+ if (env['SCANNER_TIMEOUT']) {
295
+ this.config.scanner.timeout = parseInt(env['SCANNER_TIMEOUT'], 10);
296
+ }
297
+
298
+ // Retry configuration
299
+ if (env['FEEDBACK_QUEUE_MAX_ATTEMPTS']) {
300
+ this.config.retry.maxAttempts = parseInt(env['FEEDBACK_QUEUE_MAX_ATTEMPTS'], 10);
301
+ }
302
+ if (env['FEEDBACK_QUEUE_BASE_DELAY']) {
303
+ this.config.retry.baseDelay = parseInt(env['FEEDBACK_QUEUE_BASE_DELAY'], 10);
304
+ }
305
+ if (env['FEEDBACK_QUEUE_MAX_DELAY']) {
306
+ this.config.retry.maxDelay = parseInt(env['FEEDBACK_QUEUE_MAX_DELAY'], 10);
307
+ }
308
+
309
+ // Logging
310
+ if (env['LOG_LEVEL']) {
311
+ const level = LogLevel.safeParse(env['LOG_LEVEL']);
312
+ if (level.success) {
313
+ this.config.logLevel = level.data;
314
+ }
315
+ }
316
+ if (env['LOG_FORMAT']) {
317
+ if (env['LOG_FORMAT'] === 'json' || env['LOG_FORMAT'] === 'pretty') {
318
+ this.config.logFormat = env['LOG_FORMAT'];
319
+ }
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Ensure required directories exist
325
+ */
326
+ private async ensureDirectories(): Promise<void> {
327
+ const dirs = [
328
+ this.getStoragePath(),
329
+ path.join(this.getStoragePath(), 'data'),
330
+ this.getCachePath(),
331
+ this.getLocalSkillsPath(),
332
+ this.getLogsPath(),
333
+ this.getQueuePath(),
334
+ ];
335
+
336
+ for (const dir of dirs) {
337
+ if (!fs.existsSync(dir)) {
338
+ fs.mkdirSync(dir, { recursive: true });
339
+ }
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Get the current configuration
345
+ */
346
+ get(): RegistryConfig {
347
+ return this.config;
348
+ }
349
+
350
+ /**
351
+ * Update configuration
352
+ */
353
+ set(updates: Partial<RegistryConfig>): void {
354
+ this.config = RegistryConfigSchema.parse({
355
+ ...this.config,
356
+ ...updates,
357
+ });
358
+ }
359
+
360
+ /**
361
+ * Get a specific configuration value
362
+ */
363
+ getValue<K extends keyof RegistryConfig>(key: K): RegistryConfig[K] {
364
+ return this.config[key];
365
+ }
366
+
367
+ /**
368
+ * Set a specific configuration value
369
+ */
370
+ setValue<K extends keyof RegistryConfig>(key: K, value: RegistryConfig[K]): void {
371
+ this.config[key] = value;
372
+ }
373
+
374
+ /**
375
+ * Check if running in local mode
376
+ */
377
+ isLocalMode(): boolean {
378
+ return this.config.mode === 'local';
379
+ }
380
+
381
+ /**
382
+ * Check if running in Docker mode
383
+ */
384
+ isDockerMode(): boolean {
385
+ return this.config.mode === 'docker';
386
+ }
387
+
388
+ /**
389
+ * Check if running in cloud mode
390
+ */
391
+ isCloudMode(): boolean {
392
+ return this.config.mode === 'cloud';
393
+ }
394
+
395
+ /**
396
+ * Reset to default configuration
397
+ */
398
+ reset(): void {
399
+ this.config = RegistryConfigSchema.parse({});
400
+ this.loaded = false;
401
+ }
402
+ }
403
+
404
+ // ============================================================================
405
+ // Singleton Instance
406
+ // ============================================================================
407
+
408
+ let configInstance: ConfigManager | null = null;
409
+
410
+ export function getConfig(): ConfigManager {
411
+ if (!configInstance) {
412
+ configInstance = new ConfigManager();
413
+ }
414
+ return configInstance;
415
+ }
416
+
417
+ export function initConfig(configPath?: string): ConfigManager {
418
+ configInstance = new ConfigManager(configPath);
419
+ return configInstance;
420
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Open Skills Hub - Configuration Exports
3
+ */
4
+
5
+ export * from './config.js';
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Open Skills Hub - Core Package Entry
3
+ */
4
+
5
+ // Config
6
+ export * from './config/index.js';
7
+
8
+ // Models
9
+ export * from './models/index.js';
10
+
11
+ // Storage
12
+ export * from './storage/index.js';
13
+
14
+ // Scanner
15
+ export * from './scanner/index.js';
16
+
17
+ // Utils
18
+ export * from './utils/index.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Open Skills Hub - Model Exports
3
+ */
4
+
5
+ export * from './types.js';