lsh-framework 3.2.5 → 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 +49 -38
  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,259 +0,0 @@
1
- /**
2
- * Database Schema Definitions for LSH
3
- * Defines tables and types for shell data persistence
4
- */
5
- // SQL schema for creating tables
6
- export const CREATE_TABLES_SQL = `
7
- -- Shell History Table
8
- CREATE TABLE IF NOT EXISTS shell_history (
9
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
10
- user_id UUID,
11
- session_id TEXT NOT NULL,
12
- command TEXT NOT NULL,
13
- working_directory TEXT NOT NULL,
14
- exit_code INTEGER,
15
- timestamp TIMESTAMPTZ NOT NULL,
16
- duration_ms INTEGER,
17
- hostname TEXT NOT NULL,
18
- created_at TIMESTAMPTZ DEFAULT NOW(),
19
- updated_at TIMESTAMPTZ DEFAULT NOW()
20
- );
21
-
22
- -- Shell Jobs Table
23
- CREATE TABLE IF NOT EXISTS shell_jobs (
24
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
25
- user_id UUID,
26
- session_id TEXT NOT NULL,
27
- job_id TEXT NOT NULL,
28
- command TEXT NOT NULL,
29
- status TEXT NOT NULL CHECK (status IN ('running', 'stopped', 'completed', 'failed')),
30
- pid INTEGER,
31
- working_directory TEXT NOT NULL,
32
- started_at TIMESTAMPTZ NOT NULL,
33
- completed_at TIMESTAMPTZ,
34
- exit_code INTEGER,
35
- output TEXT,
36
- error TEXT,
37
- created_at TIMESTAMPTZ DEFAULT NOW(),
38
- updated_at TIMESTAMPTZ DEFAULT NOW()
39
- );
40
-
41
- -- Shell Configuration Table
42
- CREATE TABLE IF NOT EXISTS shell_configuration (
43
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
44
- user_id UUID,
45
- config_key TEXT NOT NULL,
46
- config_value TEXT NOT NULL,
47
- config_type TEXT NOT NULL CHECK (config_type IN ('string', 'number', 'boolean', 'array', 'object')),
48
- description TEXT,
49
- is_default BOOLEAN DEFAULT FALSE,
50
- created_at TIMESTAMPTZ DEFAULT NOW(),
51
- updated_at TIMESTAMPTZ DEFAULT NOW(),
52
- UNIQUE(user_id, config_key)
53
- );
54
-
55
- -- Shell Sessions Table
56
- CREATE TABLE IF NOT EXISTS shell_sessions (
57
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
58
- user_id UUID,
59
- session_id TEXT NOT NULL UNIQUE,
60
- hostname TEXT NOT NULL,
61
- working_directory TEXT NOT NULL,
62
- environment_variables JSONB,
63
- started_at TIMESTAMPTZ NOT NULL,
64
- ended_at TIMESTAMPTZ,
65
- is_active BOOLEAN DEFAULT TRUE,
66
- created_at TIMESTAMPTZ DEFAULT NOW(),
67
- updated_at TIMESTAMPTZ DEFAULT NOW()
68
- );
69
-
70
- -- Shell Aliases Table
71
- CREATE TABLE IF NOT EXISTS shell_aliases (
72
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
73
- user_id UUID,
74
- alias_name TEXT NOT NULL,
75
- alias_value TEXT NOT NULL,
76
- description TEXT,
77
- is_active BOOLEAN DEFAULT TRUE,
78
- created_at TIMESTAMPTZ DEFAULT NOW(),
79
- updated_at TIMESTAMPTZ DEFAULT NOW(),
80
- UNIQUE(user_id, alias_name)
81
- );
82
-
83
- -- Shell Functions Table
84
- CREATE TABLE IF NOT EXISTS shell_functions (
85
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
86
- user_id UUID,
87
- function_name TEXT NOT NULL,
88
- function_body TEXT NOT NULL,
89
- description TEXT,
90
- is_active BOOLEAN DEFAULT TRUE,
91
- created_at TIMESTAMPTZ DEFAULT NOW(),
92
- updated_at TIMESTAMPTZ DEFAULT NOW(),
93
- UNIQUE(user_id, function_name)
94
- );
95
-
96
- -- Shell Completions Table
97
- CREATE TABLE IF NOT EXISTS shell_completions (
98
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
99
- user_id UUID,
100
- command TEXT NOT NULL,
101
- completion_type TEXT NOT NULL CHECK (completion_type IN ('file', 'directory', 'command', 'variable', 'function', 'option')),
102
- completion_pattern TEXT NOT NULL,
103
- description TEXT,
104
- is_active BOOLEAN DEFAULT TRUE,
105
- created_at TIMESTAMPTZ DEFAULT NOW(),
106
- updated_at TIMESTAMPTZ DEFAULT NOW(),
107
- UNIQUE(user_id, command, completion_pattern)
108
- );
109
-
110
- -- Indexes for performance
111
- CREATE INDEX IF NOT EXISTS idx_shell_history_session_id ON shell_history(session_id);
112
- CREATE INDEX IF NOT EXISTS idx_shell_history_timestamp ON shell_history(timestamp);
113
- CREATE INDEX IF NOT EXISTS idx_shell_history_user_id ON shell_history(user_id);
114
-
115
- CREATE INDEX IF NOT EXISTS idx_shell_jobs_session_id ON shell_jobs(session_id);
116
- CREATE INDEX IF NOT EXISTS idx_shell_jobs_status ON shell_jobs(status);
117
- CREATE INDEX IF NOT EXISTS idx_shell_jobs_user_id ON shell_jobs(user_id);
118
-
119
- CREATE INDEX IF NOT EXISTS idx_shell_configuration_user_id ON shell_configuration(user_id);
120
- CREATE INDEX IF NOT EXISTS idx_shell_configuration_key ON shell_configuration(config_key);
121
-
122
- CREATE INDEX IF NOT EXISTS idx_shell_sessions_user_id ON shell_sessions(user_id);
123
- CREATE INDEX IF NOT EXISTS idx_shell_sessions_active ON shell_sessions(is_active);
124
-
125
- CREATE INDEX IF NOT EXISTS idx_shell_aliases_user_id ON shell_aliases(user_id);
126
- CREATE INDEX IF NOT EXISTS idx_shell_aliases_name ON shell_aliases(alias_name);
127
-
128
- CREATE INDEX IF NOT EXISTS idx_shell_functions_user_id ON shell_functions(user_id);
129
- CREATE INDEX IF NOT EXISTS idx_shell_functions_name ON shell_functions(function_name);
130
-
131
- CREATE INDEX IF NOT EXISTS idx_shell_completions_user_id ON shell_completions(user_id);
132
- CREATE INDEX IF NOT EXISTS idx_shell_completions_command ON shell_completions(command);
133
-
134
- -- ML Training Jobs Table
135
- CREATE TABLE IF NOT EXISTS ml_training_jobs (
136
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
137
- user_id UUID,
138
- job_name TEXT NOT NULL,
139
- model_type TEXT NOT NULL,
140
- dataset_name TEXT NOT NULL,
141
- status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'completed', 'failed', 'cancelled')),
142
- hyperparameters JSONB NOT NULL,
143
- feature_names TEXT[] NOT NULL,
144
- target_variable TEXT NOT NULL,
145
- train_rmse FLOAT,
146
- train_mae FLOAT,
147
- train_r2 FLOAT,
148
- test_rmse FLOAT,
149
- test_mae FLOAT,
150
- test_r2 FLOAT,
151
- mape FLOAT,
152
- cv_scores FLOAT[],
153
- cv_mean FLOAT,
154
- cv_std FLOAT,
155
- model_path TEXT,
156
- scaler_path TEXT,
157
- started_at TIMESTAMPTZ,
158
- completed_at TIMESTAMPTZ,
159
- duration_ms INTEGER,
160
- error_message TEXT,
161
- notes TEXT,
162
- metadata_json JSONB,
163
- created_at TIMESTAMPTZ DEFAULT NOW(),
164
- updated_at TIMESTAMPTZ DEFAULT NOW()
165
- );
166
-
167
- -- ML Model Versions Table
168
- CREATE TABLE IF NOT EXISTS ml_model_versions (
169
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
170
- user_id UUID,
171
- training_job_id UUID REFERENCES ml_training_jobs(id) ON DELETE CASCADE,
172
- model_name TEXT NOT NULL,
173
- version TEXT NOT NULL,
174
- model_type TEXT NOT NULL,
175
- test_rmse FLOAT NOT NULL,
176
- test_mae FLOAT NOT NULL,
177
- test_r2 FLOAT NOT NULL,
178
- test_mape FLOAT,
179
- feature_importance JSONB,
180
- rank_by_rmse INTEGER,
181
- rank_by_r2 INTEGER,
182
- is_deployed BOOLEAN DEFAULT FALSE,
183
- deployed_at TIMESTAMPTZ,
184
- model_path TEXT NOT NULL,
185
- predictions_file TEXT,
186
- residuals_file TEXT,
187
- created_at TIMESTAMPTZ DEFAULT NOW(),
188
- updated_at TIMESTAMPTZ DEFAULT NOW(),
189
- UNIQUE(model_name, version)
190
- );
191
-
192
- -- ML Feature Sets Table
193
- CREATE TABLE IF NOT EXISTS ml_feature_sets (
194
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
195
- user_id UUID,
196
- feature_set_name TEXT NOT NULL,
197
- version TEXT NOT NULL,
198
- features JSONB NOT NULL,
199
- feature_count INTEGER NOT NULL,
200
- correlation_with_target JSONB,
201
- used_in_models TEXT[],
202
- description TEXT,
203
- created_at TIMESTAMPTZ DEFAULT NOW(),
204
- updated_at TIMESTAMPTZ DEFAULT NOW(),
205
- UNIQUE(feature_set_name, version)
206
- );
207
-
208
- -- ML Predictions Table
209
- CREATE TABLE IF NOT EXISTS ml_predictions (
210
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
211
- user_id UUID,
212
- model_version_id UUID REFERENCES ml_model_versions(id) ON DELETE CASCADE,
213
- prediction_date TIMESTAMPTZ NOT NULL,
214
- target_date TIMESTAMPTZ,
215
- predicted_value FLOAT NOT NULL,
216
- actual_value FLOAT,
217
- confidence_score FLOAT,
218
- features_json JSONB NOT NULL,
219
- residual FLOAT,
220
- absolute_error FLOAT,
221
- created_at TIMESTAMPTZ DEFAULT NOW()
222
- );
223
-
224
- -- ML Model Comparisons Table
225
- CREATE TABLE IF NOT EXISTS ml_model_comparisons (
226
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
227
- user_id UUID,
228
- comparison_name TEXT NOT NULL,
229
- model_ids UUID[] NOT NULL,
230
- comparison_results JSONB NOT NULL,
231
- best_model_id UUID NOT NULL,
232
- best_model_metric TEXT NOT NULL CHECK (best_model_metric IN ('rmse', 'mae', 'r2')),
233
- notes TEXT,
234
- created_at TIMESTAMPTZ DEFAULT NOW()
235
- );
236
-
237
- -- Indexes for ML tables
238
- CREATE INDEX IF NOT EXISTS idx_ml_training_jobs_user_id ON ml_training_jobs(user_id);
239
- CREATE INDEX IF NOT EXISTS idx_ml_training_jobs_status ON ml_training_jobs(status);
240
- CREATE INDEX IF NOT EXISTS idx_ml_training_jobs_created ON ml_training_jobs(created_at);
241
-
242
- CREATE INDEX IF NOT EXISTS idx_ml_model_versions_user_id ON ml_model_versions(user_id);
243
- CREATE INDEX IF NOT EXISTS idx_ml_model_versions_training_job ON ml_model_versions(training_job_id);
244
- CREATE INDEX IF NOT EXISTS idx_ml_model_versions_deployed ON ml_model_versions(is_deployed);
245
- CREATE INDEX IF NOT EXISTS idx_ml_model_versions_performance ON ml_model_versions(test_r2 DESC, test_rmse ASC);
246
-
247
- CREATE INDEX IF NOT EXISTS idx_ml_feature_sets_user_id ON ml_feature_sets(user_id);
248
- CREATE INDEX IF NOT EXISTS idx_ml_feature_sets_name ON ml_feature_sets(feature_set_name);
249
-
250
- CREATE INDEX IF NOT EXISTS idx_ml_predictions_user_id ON ml_predictions(user_id);
251
- CREATE INDEX IF NOT EXISTS idx_ml_predictions_model ON ml_predictions(model_version_id);
252
- CREATE INDEX IF NOT EXISTS idx_ml_predictions_date ON ml_predictions(prediction_date);
253
-
254
- CREATE INDEX IF NOT EXISTS idx_ml_model_comparisons_user_id ON ml_model_comparisons(user_id);
255
- CREATE INDEX IF NOT EXISTS idx_ml_model_comparisons_created ON ml_model_comparisons(created_at);
256
- `;
257
- export default {
258
- CREATE_TABLES_SQL,
259
- };
@@ -1,90 +0,0 @@
1
- /**
2
- * LSH Database Record Types
3
- *
4
- * These interfaces represent the exact shape of data returned from Supabase/PostgreSQL.
5
- * They use snake_case to match database column names, distinguishing them from
6
- * the domain model types in saas-types.ts which use camelCase.
7
- *
8
- * Use these types for:
9
- * - Typing Supabase query results
10
- * - Mapping functions that convert DB rows to domain objects
11
- * - Understanding the database schema without checking migrations
12
- *
13
- * @see saas-types.ts for domain model types (camelCase)
14
- * @see database-schema.ts for schema definitions
15
- */
16
- // ============================================================================
17
- // TYPE GUARDS & UTILITIES
18
- // ============================================================================
19
- /**
20
- * Type guard to check if a value is a valid DbOrganizationRecord.
21
- * Useful for runtime validation of Supabase responses.
22
- */
23
- export function isDbOrganizationRecord(value) {
24
- if (typeof value !== 'object' || value === null)
25
- return false;
26
- const record = value;
27
- return (typeof record.id === 'string' &&
28
- typeof record.name === 'string' &&
29
- typeof record.slug === 'string' &&
30
- typeof record.created_at === 'string');
31
- }
32
- /**
33
- * Type guard to check if a value is a valid DbUserRecord.
34
- */
35
- export function isDbUserRecord(value) {
36
- if (typeof value !== 'object' || value === null)
37
- return false;
38
- const record = value;
39
- return (typeof record.id === 'string' &&
40
- typeof record.email === 'string' &&
41
- typeof record.created_at === 'string');
42
- }
43
- /**
44
- * Type guard to check if a value is a valid DbSecretRecord.
45
- */
46
- export function isDbSecretRecord(value) {
47
- if (typeof value !== 'object' || value === null)
48
- return false;
49
- const record = value;
50
- return (typeof record.id === 'string' &&
51
- typeof record.team_id === 'string' &&
52
- typeof record.key === 'string' &&
53
- typeof record.encrypted_value === 'string');
54
- }
55
- /**
56
- * Parse tags from database - handles both string JSON and array formats.
57
- */
58
- export function parseDbTags(tags) {
59
- if (!tags)
60
- return [];
61
- if (Array.isArray(tags))
62
- return tags;
63
- if (typeof tags === 'string') {
64
- try {
65
- return JSON.parse(tags);
66
- }
67
- catch {
68
- return [];
69
- }
70
- }
71
- return [];
72
- }
73
- /**
74
- * Safely parse an ISO timestamp string to Date.
75
- * Returns null for null/undefined input.
76
- */
77
- export function parseDbTimestamp(timestamp) {
78
- if (!timestamp)
79
- return null;
80
- return new Date(timestamp);
81
- }
82
- /**
83
- * Safely parse an ISO timestamp string to Date.
84
- * Returns current date for null/undefined input.
85
- */
86
- export function parseDbTimestampRequired(timestamp) {
87
- if (!timestamp)
88
- return new Date();
89
- return new Date(timestamp);
90
- }
@@ -1,247 +0,0 @@
1
- /**
2
- * Enhanced History System with Supabase Integration
3
- * Extends the base history system with cloud persistence
4
- */
5
- import HistorySystem from './history-system.js';
6
- import DatabasePersistence from './database-persistence.js';
7
- import * as os from 'os';
8
- import * as path from 'path';
9
- import { DEFAULTS } from '../constants/index.js';
10
- export class EnhancedHistorySystem extends HistorySystem {
11
- databasePersistence;
12
- enhancedConfig;
13
- syncTimer;
14
- pendingSync = false;
15
- constructor(config = {}) {
16
- const defaultConfig = {
17
- maxSize: DEFAULTS.MAX_HISTORY_SIZE,
18
- filePath: path.join(os.homedir(), '.lsh_history'),
19
- shareHistory: true,
20
- ignoreDups: true,
21
- ignoreSpace: true,
22
- expireDuplicatesFirst: true,
23
- enableCloudSync: true,
24
- userId: undefined,
25
- syncInterval: DEFAULTS.HISTORY_SYNC_INTERVAL_MS,
26
- ...config,
27
- };
28
- super(defaultConfig);
29
- this.enhancedConfig = defaultConfig;
30
- this.databasePersistence = new DatabasePersistence(defaultConfig.userId);
31
- if (this.enhancedConfig.enableCloudSync) {
32
- this.initializeCloudSync();
33
- }
34
- }
35
- /**
36
- * Initialize cloud synchronization
37
- */
38
- async initializeCloudSync() {
39
- try {
40
- const isConnected = await this.databasePersistence.testConnection();
41
- if (isConnected) {
42
- console.log('✅ Cloud history sync enabled');
43
- await this.loadCloudHistory();
44
- this.startSyncTimer();
45
- }
46
- else {
47
- console.log('⚠️ Cloud history sync disabled - database not available');
48
- }
49
- }
50
- catch (error) {
51
- console.error('Failed to initialize cloud sync:', error);
52
- }
53
- }
54
- /**
55
- * Load history from cloud database
56
- */
57
- async loadCloudHistory() {
58
- try {
59
- const cloudEntries = await this.databasePersistence.getHistoryEntries(1000);
60
- // Convert cloud entries to local format
61
- const localEntries = cloudEntries.map((entry, index) => ({
62
- lineNumber: index + 1,
63
- command: entry.command,
64
- timestamp: new Date(entry.timestamp).getTime(),
65
- exitCode: entry.exit_code,
66
- }));
67
- // Merge with local history (avoid duplicates)
68
- this.mergeHistoryEntries(localEntries);
69
- }
70
- catch (error) {
71
- console.error('Failed to load cloud history:', error);
72
- }
73
- }
74
- /**
75
- * Merge cloud history entries with local history
76
- */
77
- mergeHistoryEntries(cloudEntries) {
78
- const localEntries = this.getAllEntries();
79
- const mergedEntries = [];
80
- // Create a map of local entries by command and timestamp
81
- const localMap = new Map();
82
- localEntries.forEach(entry => {
83
- const key = `${entry.command}_${entry.timestamp}`;
84
- localMap.set(key, entry);
85
- });
86
- // Add cloud entries that don't exist locally
87
- cloudEntries.forEach(entry => {
88
- const key = `${entry.command}_${entry.timestamp}`;
89
- if (!localMap.has(key)) {
90
- mergedEntries.push(entry);
91
- }
92
- });
93
- // Add all local entries
94
- mergedEntries.push(...localEntries);
95
- // Sort by timestamp and update line numbers
96
- mergedEntries.sort((a, b) => a.timestamp - b.timestamp);
97
- mergedEntries.forEach((entry, index) => {
98
- entry.lineNumber = index + 1;
99
- });
100
- // Update internal entries
101
- this.entries = mergedEntries;
102
- }
103
- /**
104
- * Start periodic synchronization timer
105
- */
106
- startSyncTimer() {
107
- this.syncTimer = setInterval(() => {
108
- this.syncToCloud();
109
- }, this.enhancedConfig.syncInterval);
110
- }
111
- /**
112
- * Stop synchronization timer
113
- */
114
- stopSyncTimer() {
115
- if (this.syncTimer) {
116
- clearInterval(this.syncTimer);
117
- this.syncTimer = undefined;
118
- }
119
- }
120
- /**
121
- * Synchronize local history to cloud
122
- */
123
- async syncToCloud() {
124
- if (this.pendingSync || !this.enhancedConfig.enableCloudSync) {
125
- return;
126
- }
127
- this.pendingSync = true;
128
- try {
129
- const localEntries = this.getAllEntries();
130
- const recentEntries = localEntries.slice(-50); // Sync last 50 entries
131
- for (const entry of recentEntries) {
132
- await this.databasePersistence.saveHistoryEntry({
133
- session_id: this.databasePersistence.getSessionId(),
134
- command: entry.command,
135
- working_directory: process.cwd(),
136
- exit_code: entry.exitCode,
137
- timestamp: new Date(entry.timestamp).toISOString(),
138
- hostname: os.hostname(),
139
- });
140
- }
141
- }
142
- catch (error) {
143
- console.error('Failed to sync history to cloud:', error);
144
- }
145
- finally {
146
- this.pendingSync = false;
147
- }
148
- }
149
- /**
150
- * Override addCommand to include cloud sync
151
- */
152
- addCommand(command, exitCode) {
153
- super.addCommand(command, exitCode);
154
- // Sync to cloud if enabled
155
- if (this.enhancedConfig.enableCloudSync) {
156
- this.syncToCloud().catch(error => {
157
- console.error('Failed to sync command to cloud:', error);
158
- });
159
- }
160
- }
161
- /**
162
- * Search history across local and cloud data
163
- */
164
- async searchHistoryCloud(query, limit = 20) {
165
- const localResults = this.searchHistory(query).slice(0, limit);
166
- if (!this.enhancedConfig.enableCloudSync) {
167
- return localResults;
168
- }
169
- try {
170
- // Search cloud history
171
- const cloudEntries = await this.databasePersistence.getHistoryEntries(1000);
172
- const cloudResults = cloudEntries
173
- .filter(entry => entry.command.toLowerCase().includes(query.toLowerCase()))
174
- .slice(0, limit)
175
- .map((entry, index) => ({
176
- lineNumber: index + 1,
177
- command: entry.command,
178
- timestamp: new Date(entry.timestamp).getTime(),
179
- exitCode: entry.exit_code,
180
- }));
181
- // Merge and deduplicate results
182
- const allResults = [...localResults, ...cloudResults];
183
- const uniqueResults = allResults.filter((entry, index, arr) => arr.findIndex(e => e.command === entry.command && e.timestamp === entry.timestamp) === index);
184
- return uniqueResults.slice(0, limit);
185
- }
186
- catch (error) {
187
- console.error('Failed to search cloud history:', error);
188
- return localResults;
189
- }
190
- }
191
- /**
192
- * Get history statistics
193
- */
194
- async getHistoryStats() {
195
- const localEntries = this.getAllEntries();
196
- let cloudEntries = 0;
197
- if (this.enhancedConfig.enableCloudSync) {
198
- try {
199
- const cloudData = await this.databasePersistence.getHistoryEntries(10000);
200
- cloudEntries = cloudData.length;
201
- }
202
- catch (error) {
203
- console.error('Failed to get cloud history stats:', error);
204
- }
205
- }
206
- // Calculate statistics
207
- const commandCounts = new Map();
208
- let totalLength = 0;
209
- localEntries.forEach(entry => {
210
- commandCounts.set(entry.command, (commandCounts.get(entry.command) || 0) + 1);
211
- totalLength += entry.command.length;
212
- });
213
- const mostUsedCommand = Array.from(commandCounts.entries())
214
- .sort(([, a], [, b]) => b - a)[0]?.[0] || '';
215
- return {
216
- localEntries: localEntries.length,
217
- cloudEntries,
218
- totalCommands: localEntries.length + cloudEntries,
219
- mostUsedCommand,
220
- averageCommandLength: localEntries.length > 0 ? totalLength / localEntries.length : 0,
221
- };
222
- }
223
- /**
224
- * Enable or disable cloud sync
225
- */
226
- setCloudSyncEnabled(enabled) {
227
- this.enhancedConfig.enableCloudSync = enabled;
228
- if (enabled) {
229
- this.initializeCloudSync();
230
- }
231
- else {
232
- this.stopSyncTimer();
233
- }
234
- }
235
- /**
236
- * Cleanup resources
237
- */
238
- destroy() {
239
- this.stopSyncTimer();
240
- if (this.enhancedConfig.enableCloudSync) {
241
- this.syncToCloud().catch(error => {
242
- console.error('Failed to final sync on destroy:', error);
243
- });
244
- }
245
- }
246
- }
247
- export default EnhancedHistorySystem;