lsh-framework 3.2.5 → 3.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.
Files changed (54) 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/self.js +22 -16
  5. package/dist/commands/sync.js +49 -38
  6. package/dist/constants/config.js +3 -0
  7. package/dist/lib/floating-point-arithmetic.js +2 -2
  8. package/dist/lib/ipfs-client-manager.js +51 -13
  9. package/dist/lib/ipfs-secrets-storage.js +21 -16
  10. package/dist/lib/ipfs-sync.js +88 -14
  11. package/dist/lib/secrets-manager.js +117 -47
  12. package/dist/lib/sync-key-store.js +87 -0
  13. package/dist/services/secrets/secrets.js +77 -39
  14. package/package.json +16 -16
  15. package/dist/__tests__/fixtures/job-fixtures.js +0 -204
  16. package/dist/__tests__/fixtures/supabase-mocks.js +0 -252
  17. package/dist/daemon/job-registry.js +0 -556
  18. package/dist/daemon/lshd.js +0 -968
  19. package/dist/daemon/saas-api-routes.js +0 -599
  20. package/dist/daemon/saas-api-server.js +0 -231
  21. package/dist/examples/supabase-integration.js +0 -106
  22. package/dist/lib/api-response.js +0 -226
  23. package/dist/lib/base-command-registrar.js +0 -287
  24. package/dist/lib/base-job-manager.js +0 -295
  25. package/dist/lib/cloud-config-manager.js +0 -348
  26. package/dist/lib/cron-job-manager.js +0 -368
  27. package/dist/lib/daemon-client-helper.js +0 -145
  28. package/dist/lib/daemon-client.js +0 -513
  29. package/dist/lib/database-persistence.js +0 -727
  30. package/dist/lib/database-schema.js +0 -259
  31. package/dist/lib/database-types.js +0 -90
  32. package/dist/lib/enhanced-history-system.js +0 -247
  33. package/dist/lib/history-system.js +0 -246
  34. package/dist/lib/job-manager.js +0 -436
  35. package/dist/lib/job-storage-database.js +0 -164
  36. package/dist/lib/job-storage-memory.js +0 -73
  37. package/dist/lib/local-storage-adapter.js +0 -507
  38. package/dist/lib/optimized-job-scheduler.js +0 -356
  39. package/dist/lib/saas-audit.js +0 -215
  40. package/dist/lib/saas-auth.js +0 -465
  41. package/dist/lib/saas-billing.js +0 -503
  42. package/dist/lib/saas-email.js +0 -403
  43. package/dist/lib/saas-encryption.js +0 -221
  44. package/dist/lib/saas-organizations.js +0 -662
  45. package/dist/lib/saas-secrets.js +0 -408
  46. package/dist/lib/saas-types.js +0 -165
  47. package/dist/lib/supabase-client.js +0 -125
  48. package/dist/lib/supabase-utils.js +0 -396
  49. package/dist/services/cron/cron-registrar.js +0 -240
  50. package/dist/services/cron/cron.js +0 -9
  51. package/dist/services/daemon/daemon-registrar.js +0 -585
  52. package/dist/services/daemon/daemon.js +0 -9
  53. package/dist/services/supabase/supabase-registrar.js +0 -375
  54. 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;