lsh-framework 3.2.4 → 3.5.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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +72 -34
  3. package/dist/commands/ipfs.js +7 -12
  4. package/dist/commands/sync.js +51 -39
  5. package/dist/constants/config.js +3 -0
  6. package/dist/lib/floating-point-arithmetic.js +2 -2
  7. package/dist/lib/ipfs-client-manager.js +51 -13
  8. package/dist/lib/ipfs-secrets-storage.js +21 -16
  9. package/dist/lib/ipfs-sync.js +88 -14
  10. package/dist/lib/secrets-manager.js +117 -47
  11. package/dist/lib/sync-key-store.js +87 -0
  12. package/dist/services/secrets/secrets.js +77 -39
  13. package/package.json +16 -16
  14. package/dist/__tests__/fixtures/job-fixtures.js +0 -204
  15. package/dist/__tests__/fixtures/supabase-mocks.js +0 -252
  16. package/dist/daemon/job-registry.js +0 -556
  17. package/dist/daemon/lshd.js +0 -968
  18. package/dist/daemon/saas-api-routes.js +0 -599
  19. package/dist/daemon/saas-api-server.js +0 -231
  20. package/dist/examples/supabase-integration.js +0 -106
  21. package/dist/lib/api-response.js +0 -226
  22. package/dist/lib/base-command-registrar.js +0 -287
  23. package/dist/lib/base-job-manager.js +0 -295
  24. package/dist/lib/cloud-config-manager.js +0 -348
  25. package/dist/lib/cron-job-manager.js +0 -368
  26. package/dist/lib/daemon-client-helper.js +0 -145
  27. package/dist/lib/daemon-client.js +0 -513
  28. package/dist/lib/database-persistence.js +0 -727
  29. package/dist/lib/database-schema.js +0 -259
  30. package/dist/lib/database-types.js +0 -90
  31. package/dist/lib/enhanced-history-system.js +0 -247
  32. package/dist/lib/history-system.js +0 -246
  33. package/dist/lib/job-manager.js +0 -436
  34. package/dist/lib/job-storage-database.js +0 -164
  35. package/dist/lib/job-storage-memory.js +0 -73
  36. package/dist/lib/local-storage-adapter.js +0 -507
  37. package/dist/lib/optimized-job-scheduler.js +0 -356
  38. package/dist/lib/saas-audit.js +0 -215
  39. package/dist/lib/saas-auth.js +0 -465
  40. package/dist/lib/saas-billing.js +0 -503
  41. package/dist/lib/saas-email.js +0 -403
  42. package/dist/lib/saas-encryption.js +0 -221
  43. package/dist/lib/saas-organizations.js +0 -662
  44. package/dist/lib/saas-secrets.js +0 -408
  45. package/dist/lib/saas-types.js +0 -165
  46. package/dist/lib/supabase-client.js +0 -125
  47. package/dist/lib/supabase-utils.js +0 -396
  48. package/dist/services/cron/cron-registrar.js +0 -240
  49. package/dist/services/cron/cron.js +0 -9
  50. package/dist/services/daemon/daemon-registrar.js +0 -585
  51. package/dist/services/daemon/daemon.js +0 -9
  52. package/dist/services/supabase/supabase-registrar.js +0 -375
  53. package/dist/services/supabase/supabase.js +0 -9
@@ -1,507 +0,0 @@
1
- /**
2
- * Local File-Based Storage Adapter
3
- * Provides persistence when Supabase/PostgreSQL is not available
4
- * Uses JSON files for storage - suitable for development and single-user deployments
5
- */
6
- import * as fs from 'fs/promises';
7
- import * as path from 'path';
8
- import * as os from 'os';
9
- /**
10
- * Local file-based storage adapter
11
- * Implements same interface as DatabasePersistence but uses local JSON files
12
- */
13
- export class LocalStorageAdapter {
14
- dataDir;
15
- dataFile;
16
- data;
17
- userId;
18
- sessionId;
19
- autoFlush;
20
- flushInterval;
21
- isDirty = false;
22
- constructor(userId, config = {}) {
23
- this.userId = userId;
24
- this.sessionId = this.generateSessionId();
25
- this.dataDir = config.dataDir || path.join(os.homedir(), '.lsh', 'data');
26
- this.dataFile = path.join(this.dataDir, 'storage.json');
27
- this.autoFlush = config.autoFlush !== false; // default true
28
- // Initialize empty data structure
29
- this.data = {
30
- shell_history: [],
31
- shell_jobs: [],
32
- shell_configuration: [],
33
- shell_sessions: [],
34
- shell_aliases: [],
35
- shell_functions: [],
36
- shell_completions: [],
37
- };
38
- // Start auto-flush if enabled
39
- if (this.autoFlush) {
40
- const interval = config.flushInterval || 5000; // default 5s
41
- this.flushInterval = setInterval(() => this.flush(), interval);
42
- }
43
- }
44
- /**
45
- * Initialize storage directory and load existing data
46
- */
47
- async initialize() {
48
- try {
49
- // Create data directory if it doesn't exist
50
- await fs.mkdir(this.dataDir, { recursive: true });
51
- // Load existing data if file exists
52
- try {
53
- const content = await fs.readFile(this.dataFile, 'utf-8');
54
- this.data = JSON.parse(content);
55
- }
56
- catch (_error) {
57
- // File doesn't exist yet, use empty data
58
- await this.flush();
59
- }
60
- }
61
- catch (error) {
62
- console.error('Failed to initialize local storage:', error);
63
- throw error;
64
- }
65
- }
66
- /**
67
- * Flush in-memory data to disk
68
- */
69
- async flush() {
70
- if (!this.isDirty) {
71
- return;
72
- }
73
- try {
74
- await fs.writeFile(this.dataFile, JSON.stringify(this.data, null, 2), 'utf-8');
75
- this.isDirty = false;
76
- }
77
- catch (error) {
78
- console.error('Failed to flush data to disk:', error);
79
- throw error;
80
- }
81
- }
82
- /**
83
- * Mark data as dirty (needs flush)
84
- */
85
- markDirty() {
86
- this.isDirty = true;
87
- }
88
- /**
89
- * Reload data from disk (useful to get latest data from other processes)
90
- */
91
- async reload() {
92
- try {
93
- const content = await fs.readFile(this.dataFile, 'utf-8');
94
- this.data = JSON.parse(content);
95
- this.isDirty = false;
96
- }
97
- catch (_error) {
98
- // File doesn't exist or can't be read - use current in-memory data
99
- // Don't throw here, as this is expected on first run
100
- }
101
- }
102
- /**
103
- * Cleanup and flush on exit
104
- */
105
- async cleanup() {
106
- if (this.flushInterval) {
107
- clearInterval(this.flushInterval);
108
- }
109
- await this.flush();
110
- }
111
- /**
112
- * Generate a unique session ID
113
- */
114
- generateSessionId() {
115
- return `lsh_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
116
- }
117
- /**
118
- * Generate a unique ID
119
- */
120
- generateId() {
121
- return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
122
- }
123
- /**
124
- * Save shell history entry
125
- */
126
- async saveHistoryEntry(entry) {
127
- try {
128
- const newEntry = {
129
- ...entry,
130
- id: this.generateId(),
131
- user_id: this.userId,
132
- session_id: this.sessionId,
133
- created_at: new Date().toISOString(),
134
- updated_at: new Date().toISOString(),
135
- };
136
- this.data.shell_history.push(newEntry);
137
- this.markDirty();
138
- return true;
139
- }
140
- catch (error) {
141
- console.error('Error saving history entry:', error);
142
- return false;
143
- }
144
- }
145
- /**
146
- * Get shell history entries
147
- */
148
- async getHistoryEntries(limit = 100, offset = 0) {
149
- try {
150
- const filtered = this.data.shell_history.filter(entry => this.userId ? entry.user_id === this.userId : entry.user_id === undefined || entry.user_id === null);
151
- // Sort by timestamp descending
152
- filtered.sort((a, b) => {
153
- const timeA = new Date(a.timestamp).getTime();
154
- const timeB = new Date(b.timestamp).getTime();
155
- return timeB - timeA;
156
- });
157
- return filtered.slice(offset, offset + limit);
158
- }
159
- catch (error) {
160
- console.error('Error getting history entries:', error);
161
- return [];
162
- }
163
- }
164
- /**
165
- * Save shell job
166
- */
167
- async saveJob(job) {
168
- try {
169
- const newJob = {
170
- ...job,
171
- id: this.generateId(),
172
- user_id: this.userId,
173
- session_id: this.sessionId,
174
- created_at: new Date().toISOString(),
175
- updated_at: new Date().toISOString(),
176
- };
177
- this.data.shell_jobs.push(newJob);
178
- this.markDirty();
179
- return true;
180
- }
181
- catch (error) {
182
- console.error('Error saving job:', error);
183
- return false;
184
- }
185
- }
186
- /**
187
- * Update shell job status
188
- */
189
- async updateJobStatus(jobId, status, exitCode) {
190
- try {
191
- const job = this.data.shell_jobs.find(j => j.job_id === jobId &&
192
- (this.userId ? j.user_id === this.userId : j.user_id === undefined || j.user_id === null));
193
- if (!job) {
194
- return false;
195
- }
196
- job.status = status;
197
- job.updated_at = new Date().toISOString();
198
- if (status === 'completed' || status === 'failed') {
199
- job.completed_at = new Date().toISOString();
200
- if (exitCode !== undefined) {
201
- job.exit_code = exitCode;
202
- }
203
- }
204
- this.markDirty();
205
- return true;
206
- }
207
- catch (error) {
208
- console.error('Error updating job status:', error);
209
- return false;
210
- }
211
- }
212
- /**
213
- * Get active jobs
214
- */
215
- async getActiveJobs() {
216
- try {
217
- return this.data.shell_jobs
218
- .filter(job => ['running', 'stopped', 'completed', 'failed'].includes(job.status) &&
219
- (this.userId ? job.user_id === this.userId : job.user_id === undefined || job.user_id === null))
220
- .sort((a, b) => {
221
- const timeA = new Date(a.created_at || 0).getTime();
222
- const timeB = new Date(b.created_at || 0).getTime();
223
- return timeB - timeA;
224
- });
225
- }
226
- catch (error) {
227
- console.error('Error getting active jobs:', error);
228
- return [];
229
- }
230
- }
231
- /**
232
- * Save shell configuration
233
- */
234
- async saveConfiguration(config) {
235
- try {
236
- // Find existing config
237
- const existingIndex = this.data.shell_configuration.findIndex(c => c.user_id === (this.userId || null) &&
238
- c.config_key === config.config_key);
239
- const newConfig = {
240
- ...config,
241
- id: existingIndex >= 0 ? this.data.shell_configuration[existingIndex].id : this.generateId(),
242
- user_id: this.userId,
243
- created_at: existingIndex >= 0 ? this.data.shell_configuration[existingIndex].created_at : new Date().toISOString(),
244
- updated_at: new Date().toISOString(),
245
- };
246
- if (existingIndex >= 0) {
247
- this.data.shell_configuration[existingIndex] = newConfig;
248
- }
249
- else {
250
- this.data.shell_configuration.push(newConfig);
251
- }
252
- this.markDirty();
253
- return true;
254
- }
255
- catch (error) {
256
- console.error('Error saving configuration:', error);
257
- return false;
258
- }
259
- }
260
- /**
261
- * Get shell configuration
262
- */
263
- async getConfiguration(key) {
264
- try {
265
- let filtered = this.data.shell_configuration.filter(config => this.userId ? config.user_id === this.userId : config.user_id === undefined || config.user_id === null);
266
- if (key) {
267
- filtered = filtered.filter(config => config.config_key === key);
268
- }
269
- return filtered;
270
- }
271
- catch (error) {
272
- console.error('Error getting configuration:', error);
273
- return [];
274
- }
275
- }
276
- /**
277
- * Save shell alias
278
- */
279
- async saveAlias(alias) {
280
- try {
281
- const existingIndex = this.data.shell_aliases.findIndex(a => a.user_id === (this.userId || null) &&
282
- a.alias_name === alias.alias_name);
283
- const newAlias = {
284
- ...alias,
285
- id: existingIndex >= 0 ? this.data.shell_aliases[existingIndex].id : this.generateId(),
286
- user_id: this.userId,
287
- created_at: existingIndex >= 0 ? this.data.shell_aliases[existingIndex].created_at : new Date().toISOString(),
288
- updated_at: new Date().toISOString(),
289
- };
290
- if (existingIndex >= 0) {
291
- this.data.shell_aliases[existingIndex] = newAlias;
292
- }
293
- else {
294
- this.data.shell_aliases.push(newAlias);
295
- }
296
- this.markDirty();
297
- return true;
298
- }
299
- catch (error) {
300
- console.error('Error saving alias:', error);
301
- return false;
302
- }
303
- }
304
- /**
305
- * Get shell aliases
306
- */
307
- async getAliases() {
308
- try {
309
- return this.data.shell_aliases.filter(alias => alias.is_active &&
310
- (this.userId ? alias.user_id === this.userId : alias.user_id === undefined || alias.user_id === null));
311
- }
312
- catch (error) {
313
- console.error('Error getting aliases:', error);
314
- return [];
315
- }
316
- }
317
- /**
318
- * Save shell function
319
- */
320
- async saveFunction(func) {
321
- try {
322
- const existingIndex = this.data.shell_functions.findIndex(f => f.user_id === (this.userId || null) &&
323
- f.function_name === func.function_name);
324
- const newFunc = {
325
- ...func,
326
- id: existingIndex >= 0 ? this.data.shell_functions[existingIndex].id : this.generateId(),
327
- user_id: this.userId,
328
- created_at: existingIndex >= 0 ? this.data.shell_functions[existingIndex].created_at : new Date().toISOString(),
329
- updated_at: new Date().toISOString(),
330
- };
331
- if (existingIndex >= 0) {
332
- this.data.shell_functions[existingIndex] = newFunc;
333
- }
334
- else {
335
- this.data.shell_functions.push(newFunc);
336
- }
337
- this.markDirty();
338
- return true;
339
- }
340
- catch (error) {
341
- console.error('Error saving function:', error);
342
- return false;
343
- }
344
- }
345
- /**
346
- * Get shell functions
347
- */
348
- async getFunctions() {
349
- try {
350
- return this.data.shell_functions.filter(func => func.is_active &&
351
- (this.userId ? func.user_id === this.userId : func.user_id === undefined || func.user_id === null));
352
- }
353
- catch (error) {
354
- console.error('Error getting functions:', error);
355
- return [];
356
- }
357
- }
358
- /**
359
- * Start a new shell session
360
- */
361
- async startSession(workingDirectory, environmentVariables) {
362
- try {
363
- const newSession = {
364
- id: this.generateId(),
365
- user_id: this.userId,
366
- session_id: this.sessionId,
367
- hostname: os.hostname(),
368
- working_directory: workingDirectory,
369
- environment_variables: environmentVariables,
370
- started_at: new Date().toISOString(),
371
- is_active: true,
372
- created_at: new Date().toISOString(),
373
- updated_at: new Date().toISOString(),
374
- };
375
- this.data.shell_sessions.push(newSession);
376
- this.markDirty();
377
- return true;
378
- }
379
- catch (error) {
380
- console.error('Error starting session:', error);
381
- return false;
382
- }
383
- }
384
- /**
385
- * End the current shell session
386
- */
387
- async endSession() {
388
- try {
389
- const session = this.data.shell_sessions.find(s => s.session_id === this.sessionId &&
390
- (this.userId ? s.user_id === this.userId : s.user_id === undefined || s.user_id === null));
391
- if (!session) {
392
- return false;
393
- }
394
- session.ended_at = new Date().toISOString();
395
- session.is_active = false;
396
- session.updated_at = new Date().toISOString();
397
- this.markDirty();
398
- return true;
399
- }
400
- catch (error) {
401
- console.error('Error ending session:', error);
402
- return false;
403
- }
404
- }
405
- /**
406
- * Test storage connectivity (always succeeds for local storage)
407
- */
408
- async testConnection() {
409
- try {
410
- await fs.access(this.dataDir);
411
- return true;
412
- }
413
- catch (_error) {
414
- return false;
415
- }
416
- }
417
- /**
418
- * Get session ID
419
- */
420
- getSessionId() {
421
- return this.sessionId;
422
- }
423
- /**
424
- * Get latest rows from all tables
425
- */
426
- async getLatestRows(limit = 5) {
427
- const result = {};
428
- try {
429
- // Get latest shell history entries
430
- const history = this.data.shell_history
431
- .filter(entry => this.userId ? entry.user_id === this.userId : entry.user_id === undefined || entry.user_id === null)
432
- .sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime())
433
- .slice(0, limit);
434
- result.shell_history = history;
435
- // Get latest shell jobs
436
- const jobs = this.data.shell_jobs
437
- .filter(job => this.userId ? job.user_id === this.userId : job.user_id === undefined || job.user_id === null)
438
- .sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime())
439
- .slice(0, limit);
440
- result.shell_jobs = jobs;
441
- // Get latest shell configuration
442
- const config = this.data.shell_configuration
443
- .filter(cfg => this.userId ? cfg.user_id === this.userId : cfg.user_id === undefined || cfg.user_id === null)
444
- .sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime())
445
- .slice(0, limit);
446
- result.shell_configuration = config;
447
- // Get latest shell sessions
448
- const sessions = this.data.shell_sessions
449
- .filter(session => this.userId ? session.user_id === this.userId : session.user_id === undefined || session.user_id === null)
450
- .sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime())
451
- .slice(0, limit);
452
- result.shell_sessions = sessions;
453
- // Get latest shell aliases
454
- const aliases = this.data.shell_aliases
455
- .filter(alias => this.userId ? alias.user_id === this.userId : alias.user_id === undefined || alias.user_id === null)
456
- .sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime())
457
- .slice(0, limit);
458
- result.shell_aliases = aliases;
459
- // Get latest shell functions
460
- const functions = this.data.shell_functions
461
- .filter(func => this.userId ? func.user_id === this.userId : func.user_id === undefined || func.user_id === null)
462
- .sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime())
463
- .slice(0, limit);
464
- result.shell_functions = functions;
465
- // Get latest shell completions
466
- const completions = this.data.shell_completions
467
- .filter(comp => this.userId ? comp.user_id === this.userId : comp.user_id === undefined || comp.user_id === null)
468
- .sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime())
469
- .slice(0, limit);
470
- result.shell_completions = completions;
471
- return result;
472
- }
473
- catch (error) {
474
- console.error('Error getting latest rows:', error);
475
- return {};
476
- }
477
- }
478
- /**
479
- * Get latest rows from a specific table
480
- */
481
- async getLatestRowsFromTable(tableName, limit = 5) {
482
- try {
483
- const validTables = [
484
- 'shell_history',
485
- 'shell_jobs',
486
- 'shell_configuration',
487
- 'shell_sessions',
488
- 'shell_aliases',
489
- 'shell_functions',
490
- 'shell_completions',
491
- ];
492
- if (!validTables.includes(tableName)) {
493
- throw new Error(`Invalid table name: ${tableName}`);
494
- }
495
- const table = this.data[tableName];
496
- return table
497
- .filter(row => this.userId ? row.user_id === this.userId : row.user_id === undefined || row.user_id === null)
498
- .sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime())
499
- .slice(0, limit);
500
- }
501
- catch (error) {
502
- console.error(`Error getting latest rows from ${tableName}:`, error);
503
- return [];
504
- }
505
- }
506
- }
507
- export default LocalStorageAdapter;