lsh-framework 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +40 -3
  2. package/dist/cli.js +104 -486
  3. package/dist/commands/doctor.js +427 -0
  4. package/dist/commands/init.js +371 -0
  5. package/dist/constants/api.js +94 -0
  6. package/dist/constants/commands.js +64 -0
  7. package/dist/constants/config.js +56 -0
  8. package/dist/constants/database.js +21 -0
  9. package/dist/constants/errors.js +79 -0
  10. package/dist/constants/index.js +28 -0
  11. package/dist/constants/paths.js +28 -0
  12. package/dist/constants/ui.js +73 -0
  13. package/dist/constants/validation.js +124 -0
  14. package/dist/daemon/lshd.js +11 -32
  15. package/dist/lib/daemon-client-helper.js +7 -4
  16. package/dist/lib/daemon-client.js +9 -2
  17. package/dist/lib/format-utils.js +163 -0
  18. package/dist/lib/fuzzy-match.js +123 -0
  19. package/dist/lib/job-manager.js +2 -1
  20. package/dist/lib/platform-utils.js +211 -0
  21. package/dist/lib/secrets-manager.js +11 -1
  22. package/dist/lib/string-utils.js +128 -0
  23. package/dist/services/daemon/daemon-registrar.js +3 -2
  24. package/dist/services/secrets/secrets.js +119 -59
  25. package/package.json +10 -74
  26. package/dist/app.js +0 -33
  27. package/dist/cicd/analytics.js +0 -261
  28. package/dist/cicd/auth.js +0 -269
  29. package/dist/cicd/cache-manager.js +0 -172
  30. package/dist/cicd/data-retention.js +0 -305
  31. package/dist/cicd/performance-monitor.js +0 -224
  32. package/dist/cicd/webhook-receiver.js +0 -640
  33. package/dist/commands/api.js +0 -346
  34. package/dist/commands/theme.js +0 -261
  35. package/dist/commands/zsh-import.js +0 -240
  36. package/dist/components/App.js +0 -1
  37. package/dist/components/Divider.js +0 -29
  38. package/dist/components/REPL.js +0 -43
  39. package/dist/components/Terminal.js +0 -232
  40. package/dist/components/UserInput.js +0 -30
  41. package/dist/daemon/api-server.js +0 -316
  42. package/dist/daemon/monitoring-api.js +0 -220
  43. package/dist/lib/api-error-handler.js +0 -185
  44. package/dist/lib/associative-arrays.js +0 -285
  45. package/dist/lib/base-api-server.js +0 -290
  46. package/dist/lib/brace-expansion.js +0 -160
  47. package/dist/lib/builtin-commands.js +0 -439
  48. package/dist/lib/executors/builtin-executor.js +0 -52
  49. package/dist/lib/extended-globbing.js +0 -411
  50. package/dist/lib/extended-parameter-expansion.js +0 -227
  51. package/dist/lib/interactive-shell.js +0 -460
  52. package/dist/lib/job-builtins.js +0 -582
  53. package/dist/lib/pathname-expansion.js +0 -216
  54. package/dist/lib/script-runner.js +0 -226
  55. package/dist/lib/shell-executor.js +0 -2504
  56. package/dist/lib/shell-parser.js +0 -958
  57. package/dist/lib/shell-types.js +0 -6
  58. package/dist/lib/shell.lib.js +0 -40
  59. package/dist/lib/theme-manager.js +0 -476
  60. package/dist/lib/variable-expansion.js +0 -385
  61. package/dist/lib/zsh-compatibility.js +0 -659
  62. package/dist/lib/zsh-import-manager.js +0 -707
  63. package/dist/lib/zsh-options.js +0 -328
  64. package/dist/pipeline/job-tracker.js +0 -491
  65. package/dist/pipeline/mcli-bridge.js +0 -309
  66. package/dist/pipeline/pipeline-service.js +0 -1119
  67. package/dist/pipeline/workflow-engine.js +0 -870
  68. package/dist/services/api/api.js +0 -58
  69. package/dist/services/api/auth.js +0 -35
  70. package/dist/services/api/config.js +0 -7
  71. package/dist/services/api/file.js +0 -22
  72. package/dist/services/shell/shell.js +0 -28
  73. package/dist/services/zapier.js +0 -16
  74. package/dist/simple-api-server.js +0 -148
package/dist/cicd/auth.js DELETED
@@ -1,269 +0,0 @@
1
- import jwt from 'jsonwebtoken';
2
- import bcrypt from 'bcrypt';
3
- const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
4
- const TOKEN_EXPIRY = '24h';
5
- export class AuthService {
6
- pool;
7
- constructor(pool) {
8
- this.pool = pool;
9
- this.initializeSchema();
10
- }
11
- async initializeSchema() {
12
- const query = `
13
- CREATE TABLE IF NOT EXISTS users (
14
- id SERIAL PRIMARY KEY,
15
- email VARCHAR(255) UNIQUE NOT NULL,
16
- name VARCHAR(255) NOT NULL,
17
- password_hash VARCHAR(255) NOT NULL,
18
- role VARCHAR(50) NOT NULL DEFAULT 'viewer',
19
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
20
- last_login TIMESTAMP,
21
- is_active BOOLEAN DEFAULT true
22
- );
23
-
24
- CREATE TABLE IF NOT EXISTS api_keys (
25
- id SERIAL PRIMARY KEY,
26
- user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
27
- key_hash VARCHAR(255) UNIQUE NOT NULL,
28
- name VARCHAR(255),
29
- permissions JSONB,
30
- last_used TIMESTAMP,
31
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
32
- expires_at TIMESTAMP
33
- );
34
-
35
- CREATE TABLE IF NOT EXISTS audit_logs (
36
- id SERIAL PRIMARY KEY,
37
- user_id INTEGER REFERENCES users(id),
38
- action VARCHAR(255) NOT NULL,
39
- resource VARCHAR(255),
40
- details JSONB,
41
- ip_address VARCHAR(45),
42
- user_agent TEXT,
43
- timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
44
- );
45
-
46
- CREATE INDEX IF NOT EXISTS idx_audit_logs_user ON audit_logs(user_id);
47
- CREATE INDEX IF NOT EXISTS idx_audit_logs_timestamp ON audit_logs(timestamp);
48
- `;
49
- try {
50
- await this.pool.query(query);
51
- }
52
- catch (error) {
53
- console.error('Error initializing auth schema:', error);
54
- }
55
- }
56
- async register(email, password, name, role = 'viewer') {
57
- const passwordHash = await bcrypt.hash(password, 10);
58
- const query = `
59
- INSERT INTO users (email, name, password_hash, role)
60
- VALUES ($1, $2, $3, $4)
61
- RETURNING id, email, name, role, created_at
62
- `;
63
- try {
64
- const result = await this.pool.query(query, [email, name, passwordHash, role]);
65
- return result.rows[0];
66
- }
67
- catch (error) {
68
- if (error.code === '23505') { // Unique constraint violation
69
- throw new Error('User with this email already exists');
70
- }
71
- throw error;
72
- }
73
- }
74
- async login(email, password) {
75
- const query = `
76
- SELECT id, email, name, role, password_hash, created_at
77
- FROM users
78
- WHERE email = $1 AND is_active = true
79
- `;
80
- const result = await this.pool.query(query, [email]);
81
- if (result.rows.length === 0) {
82
- throw new Error('Invalid credentials');
83
- }
84
- const user = result.rows[0];
85
- const isValid = await bcrypt.compare(password, user.password_hash);
86
- if (!isValid) {
87
- throw new Error('Invalid credentials');
88
- }
89
- // Update last login
90
- await this.pool.query('UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = $1', [user.id]);
91
- const token = this.generateToken(user);
92
- delete user.password_hash;
93
- return { user, token };
94
- }
95
- generateToken(user) {
96
- const payload = {
97
- userId: user.id,
98
- email: user.email,
99
- role: user.role
100
- };
101
- return jwt.sign(payload, JWT_SECRET, { expiresIn: TOKEN_EXPIRY });
102
- }
103
- verifyToken(token) {
104
- try {
105
- return jwt.verify(token, JWT_SECRET);
106
- }
107
- catch (_error) {
108
- throw new Error('Invalid or expired token');
109
- }
110
- }
111
- async generateApiKey(userId, name, permissions) {
112
- const apiKey = this.generateRandomKey();
113
- const keyHash = await bcrypt.hash(apiKey, 10);
114
- const query = `
115
- INSERT INTO api_keys (user_id, key_hash, name, permissions)
116
- VALUES ($1, $2, $3, $4)
117
- RETURNING id
118
- `;
119
- await this.pool.query(query, [userId, keyHash, name, permissions || {}]);
120
- return apiKey;
121
- }
122
- generateRandomKey() {
123
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
124
- let key = 'cicd_';
125
- for (let i = 0; i < 32; i++) {
126
- key += chars.charAt(Math.floor(Math.random() * chars.length));
127
- }
128
- return key;
129
- }
130
- async verifyApiKey(apiKey) {
131
- const query = `
132
- SELECT ak.*, u.email, u.role
133
- FROM api_keys ak
134
- JOIN users u ON ak.user_id = u.id
135
- WHERE u.is_active = true
136
- AND (ak.expires_at IS NULL OR ak.expires_at > CURRENT_TIMESTAMP)
137
- `;
138
- const result = await this.pool.query(query);
139
- for (const row of result.rows) {
140
- const isValid = await bcrypt.compare(apiKey, row.key_hash);
141
- if (isValid) {
142
- // Update last used
143
- await this.pool.query('UPDATE api_keys SET last_used = CURRENT_TIMESTAMP WHERE id = $1', [row.id]);
144
- return {
145
- userId: row.user_id,
146
- email: row.email,
147
- role: row.role
148
- };
149
- }
150
- }
151
- return null;
152
- }
153
- async logAudit(userId, action, resource, details, req) {
154
- const query = `
155
- INSERT INTO audit_logs (user_id, action, resource, details, ip_address, user_agent)
156
- VALUES ($1, $2, $3, $4, $5, $6)
157
- `;
158
- const ipAddress = req?.ip || req?.socket?.remoteAddress;
159
- const userAgent = req?.headers['user-agent'];
160
- await this.pool.query(query, [
161
- userId,
162
- action,
163
- resource,
164
- details || {},
165
- ipAddress,
166
- userAgent
167
- ]);
168
- }
169
- }
170
- // Middleware for authentication
171
- export function authenticate(authService) {
172
- return async (req, res, next) => {
173
- try {
174
- const authHeader = req.headers.authorization;
175
- if (!authHeader) {
176
- return res.status(401).json({ error: 'No authorization header' });
177
- }
178
- let user = null;
179
- if (authHeader.startsWith('Bearer ')) {
180
- // JWT token authentication
181
- const token = authHeader.substring(7);
182
- user = authService.verifyToken(token);
183
- }
184
- else if (authHeader.startsWith('ApiKey ')) {
185
- // API key authentication
186
- const apiKey = authHeader.substring(7);
187
- user = await authService.verifyApiKey(apiKey);
188
- }
189
- if (!user) {
190
- return res.status(401).json({ error: 'Invalid authentication credentials' });
191
- }
192
- req.user = user;
193
- next();
194
- }
195
- catch (error) {
196
- const message = error instanceof Error ? error.message : 'Authentication failed';
197
- return res.status(401).json({ error: message });
198
- }
199
- };
200
- }
201
- // Role-based access control middleware
202
- export function authorize(...allowedRoles) {
203
- return (req, res, next) => {
204
- if (!req.user) {
205
- return res.status(401).json({ error: 'Authentication required' });
206
- }
207
- if (!allowedRoles.includes(req.user.role)) {
208
- return res.status(403).json({ error: 'Insufficient permissions' });
209
- }
210
- next();
211
- };
212
- }
213
- // Permission-based middleware
214
- export function requirePermission(permission) {
215
- return (req, res, next) => {
216
- if (!req.user) {
217
- return res.status(401).json({ error: 'Authentication required' });
218
- }
219
- // Admin has all permissions
220
- if (req.user.role === 'admin') {
221
- return next();
222
- }
223
- // Check specific permissions based on role
224
- const rolePermissions = {
225
- developer: [
226
- 'pipelines.view',
227
- 'pipelines.trigger',
228
- 'metrics.view',
229
- 'analytics.view',
230
- 'alerts.view'
231
- ],
232
- viewer: [
233
- 'pipelines.view',
234
- 'metrics.view',
235
- 'analytics.view'
236
- ]
237
- };
238
- const userPermissions = rolePermissions[req.user.role] || [];
239
- if (!userPermissions.includes(permission)) {
240
- return res.status(403).json({ error: `Permission '${permission}' required` });
241
- }
242
- next();
243
- };
244
- }
245
- export function rateLimit(options) {
246
- const requests = new Map();
247
- return (req, res, next) => {
248
- const key = req.user?.userId?.toString() || req.ip || 'anonymous';
249
- const now = Date.now();
250
- const record = requests.get(key);
251
- if (!record || now > record.resetTime) {
252
- requests.set(key, {
253
- count: 1,
254
- resetTime: now + options.windowMs
255
- });
256
- return next();
257
- }
258
- if (record.count >= options.max) {
259
- const retryAfter = Math.ceil((record.resetTime - now) / 1000);
260
- res.setHeader('Retry-After', retryAfter);
261
- return res.status(429).json({
262
- error: 'Too many requests',
263
- retryAfter
264
- });
265
- }
266
- record.count++;
267
- next();
268
- };
269
- }
@@ -1,172 +0,0 @@
1
- import Redis from 'ioredis';
2
- import { createHash } from 'crypto';
3
- export class CacheManager {
4
- redis;
5
- stats;
6
- defaultTTL;
7
- keyPrefix;
8
- constructor(redisUrl = 'redis://localhost:6379', options = {}) {
9
- this.redis = new Redis(redisUrl);
10
- this.defaultTTL = options.ttl || 3600; // 1 hour default
11
- this.keyPrefix = options.prefix || 'cicd:cache:';
12
- this.stats = {
13
- hits: 0,
14
- misses: 0,
15
- sets: 0,
16
- deletes: 0,
17
- size: 0,
18
- hitRate: 0
19
- };
20
- // Set up error handling
21
- this.redis.on('error', (err) => {
22
- console.error('Redis cache error:', err);
23
- });
24
- }
25
- generateKey(namespace, identifier) {
26
- const hash = createHash('md5').update(identifier).digest('hex');
27
- return `${this.keyPrefix}${namespace}:${hash}`;
28
- }
29
- async get(namespace, identifier) {
30
- const key = this.generateKey(namespace, identifier);
31
- try {
32
- const cached = await this.redis.get(key);
33
- if (cached) {
34
- this.stats.hits++;
35
- this.updateHitRate();
36
- return JSON.parse(cached);
37
- }
38
- this.stats.misses++;
39
- this.updateHitRate();
40
- return null;
41
- }
42
- catch (error) {
43
- console.error(`Cache get error for ${key}:`, error);
44
- return null;
45
- }
46
- }
47
- async set(namespace, identifier, value, ttl) {
48
- const key = this.generateKey(namespace, identifier);
49
- const ttlSeconds = ttl || this.defaultTTL;
50
- try {
51
- const serialized = JSON.stringify(value);
52
- await this.redis.setex(key, ttlSeconds, serialized);
53
- this.stats.sets++;
54
- this.stats.size = await this.getSize();
55
- }
56
- catch (error) {
57
- console.error(`Cache set error for ${key}:`, error);
58
- }
59
- }
60
- async invalidate(namespace, identifier) {
61
- try {
62
- if (identifier) {
63
- // Invalidate specific item
64
- const key = this.generateKey(namespace, identifier);
65
- await this.redis.del(key);
66
- this.stats.deletes++;
67
- }
68
- else {
69
- // Invalidate entire namespace
70
- const pattern = `${this.keyPrefix}${namespace}:*`;
71
- const keys = await this.redis.keys(pattern);
72
- if (keys.length > 0) {
73
- await this.redis.del(...keys);
74
- this.stats.deletes += keys.length;
75
- }
76
- }
77
- this.stats.size = await this.getSize();
78
- }
79
- catch (error) {
80
- console.error(`Cache invalidate error:`, error);
81
- }
82
- }
83
- async getOrSet(namespace, identifier, factory, ttl) {
84
- // Try to get from cache first
85
- const cached = await this.get(namespace, identifier);
86
- if (cached !== null) {
87
- return cached;
88
- }
89
- // Generate fresh value
90
- const value = await factory();
91
- await this.set(namespace, identifier, value, ttl);
92
- return value;
93
- }
94
- async warmup(namespace, items) {
95
- const promises = items.map(async (item) => {
96
- await this.getOrSet(namespace, item.id, item.factory, item.ttl);
97
- });
98
- await Promise.all(promises);
99
- }
100
- updateHitRate() {
101
- const total = this.stats.hits + this.stats.misses;
102
- this.stats.hitRate = total > 0 ? (this.stats.hits / total) * 100 : 0;
103
- }
104
- async getSize() {
105
- try {
106
- const info = await this.redis.info('memory');
107
- const match = info.match(/used_memory_human:(.+)/);
108
- if (match) {
109
- const size = match[1].trim();
110
- // Convert to bytes
111
- if (size.endsWith('K'))
112
- return parseFloat(size) * 1024;
113
- if (size.endsWith('M'))
114
- return parseFloat(size) * 1024 * 1024;
115
- if (size.endsWith('G'))
116
- return parseFloat(size) * 1024 * 1024 * 1024;
117
- return parseFloat(size);
118
- }
119
- return 0;
120
- }
121
- catch (_error) {
122
- return 0;
123
- }
124
- }
125
- async getStats() {
126
- this.stats.size = await this.getSize();
127
- return { ...this.stats };
128
- }
129
- async clear() {
130
- try {
131
- const pattern = `${this.keyPrefix}*`;
132
- const keys = await this.redis.keys(pattern);
133
- if (keys.length > 0) {
134
- await this.redis.del(...keys);
135
- }
136
- this.resetStats();
137
- }
138
- catch (error) {
139
- console.error('Cache clear error:', error);
140
- }
141
- }
142
- resetStats() {
143
- this.stats = {
144
- hits: 0,
145
- misses: 0,
146
- sets: 0,
147
- deletes: 0,
148
- size: 0,
149
- hitRate: 0
150
- };
151
- }
152
- async disconnect() {
153
- await this.redis.quit();
154
- }
155
- }
156
- // Decorator for method-level caching
157
- export function Cacheable(namespace, ttl) {
158
- return function (target, propertyKey, descriptor) {
159
- const originalMethod = descriptor.value;
160
- descriptor.value = async function (...args) {
161
- const cacheManager = this.cacheManager;
162
- if (!cacheManager) {
163
- return originalMethod.apply(this, args);
164
- }
165
- const identifier = `${propertyKey}:${JSON.stringify(args)}`;
166
- return cacheManager.getOrSet(namespace, identifier, () => originalMethod.apply(this, args), ttl);
167
- };
168
- return descriptor;
169
- };
170
- }
171
- // Export singleton instance
172
- export const cacheManager = new CacheManager();