lsh-framework 0.5.4

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 (90) hide show
  1. package/.env.example +51 -0
  2. package/README.md +399 -0
  3. package/dist/app.js +33 -0
  4. package/dist/cicd/analytics.js +261 -0
  5. package/dist/cicd/auth.js +269 -0
  6. package/dist/cicd/cache-manager.js +172 -0
  7. package/dist/cicd/data-retention.js +305 -0
  8. package/dist/cicd/performance-monitor.js +224 -0
  9. package/dist/cicd/webhook-receiver.js +634 -0
  10. package/dist/cli.js +500 -0
  11. package/dist/commands/api.js +343 -0
  12. package/dist/commands/self.js +318 -0
  13. package/dist/commands/theme.js +257 -0
  14. package/dist/commands/zsh-import.js +240 -0
  15. package/dist/components/App.js +1 -0
  16. package/dist/components/Divider.js +29 -0
  17. package/dist/components/REPL.js +43 -0
  18. package/dist/components/Terminal.js +232 -0
  19. package/dist/components/UserInput.js +30 -0
  20. package/dist/daemon/api-server.js +315 -0
  21. package/dist/daemon/job-registry.js +554 -0
  22. package/dist/daemon/lshd.js +822 -0
  23. package/dist/daemon/monitoring-api.js +220 -0
  24. package/dist/examples/supabase-integration.js +106 -0
  25. package/dist/lib/api-error-handler.js +183 -0
  26. package/dist/lib/associative-arrays.js +285 -0
  27. package/dist/lib/base-api-server.js +290 -0
  28. package/dist/lib/base-command-registrar.js +286 -0
  29. package/dist/lib/base-job-manager.js +293 -0
  30. package/dist/lib/brace-expansion.js +160 -0
  31. package/dist/lib/builtin-commands.js +439 -0
  32. package/dist/lib/cloud-config-manager.js +347 -0
  33. package/dist/lib/command-validator.js +190 -0
  34. package/dist/lib/completion-system.js +344 -0
  35. package/dist/lib/cron-job-manager.js +364 -0
  36. package/dist/lib/daemon-client-helper.js +141 -0
  37. package/dist/lib/daemon-client.js +501 -0
  38. package/dist/lib/database-persistence.js +638 -0
  39. package/dist/lib/database-schema.js +259 -0
  40. package/dist/lib/enhanced-history-system.js +246 -0
  41. package/dist/lib/env-validator.js +265 -0
  42. package/dist/lib/executors/builtin-executor.js +52 -0
  43. package/dist/lib/extended-globbing.js +411 -0
  44. package/dist/lib/extended-parameter-expansion.js +227 -0
  45. package/dist/lib/floating-point-arithmetic.js +256 -0
  46. package/dist/lib/history-system.js +245 -0
  47. package/dist/lib/interactive-shell.js +460 -0
  48. package/dist/lib/job-builtins.js +580 -0
  49. package/dist/lib/job-manager.js +386 -0
  50. package/dist/lib/job-storage-database.js +156 -0
  51. package/dist/lib/job-storage-memory.js +73 -0
  52. package/dist/lib/logger.js +274 -0
  53. package/dist/lib/lshrc-init.js +177 -0
  54. package/dist/lib/pathname-expansion.js +216 -0
  55. package/dist/lib/prompt-system.js +328 -0
  56. package/dist/lib/script-runner.js +226 -0
  57. package/dist/lib/secrets-manager.js +193 -0
  58. package/dist/lib/shell-executor.js +2504 -0
  59. package/dist/lib/shell-parser.js +958 -0
  60. package/dist/lib/shell-types.js +6 -0
  61. package/dist/lib/shell.lib.js +40 -0
  62. package/dist/lib/supabase-client.js +58 -0
  63. package/dist/lib/theme-manager.js +476 -0
  64. package/dist/lib/variable-expansion.js +385 -0
  65. package/dist/lib/zsh-compatibility.js +658 -0
  66. package/dist/lib/zsh-import-manager.js +699 -0
  67. package/dist/lib/zsh-options.js +328 -0
  68. package/dist/pipeline/job-tracker.js +491 -0
  69. package/dist/pipeline/mcli-bridge.js +302 -0
  70. package/dist/pipeline/pipeline-service.js +1116 -0
  71. package/dist/pipeline/workflow-engine.js +867 -0
  72. package/dist/services/api/api.js +58 -0
  73. package/dist/services/api/auth.js +35 -0
  74. package/dist/services/api/config.js +7 -0
  75. package/dist/services/api/file.js +22 -0
  76. package/dist/services/cron/cron-registrar.js +235 -0
  77. package/dist/services/cron/cron.js +9 -0
  78. package/dist/services/daemon/daemon-registrar.js +565 -0
  79. package/dist/services/daemon/daemon.js +9 -0
  80. package/dist/services/lib/lib.js +86 -0
  81. package/dist/services/log-file-extractor.js +170 -0
  82. package/dist/services/secrets/secrets.js +94 -0
  83. package/dist/services/shell/shell.js +28 -0
  84. package/dist/services/supabase/supabase-registrar.js +367 -0
  85. package/dist/services/supabase/supabase.js +9 -0
  86. package/dist/services/zapier.js +16 -0
  87. package/dist/simple-api-server.js +148 -0
  88. package/dist/store/store.js +31 -0
  89. package/dist/util/lib.util.js +11 -0
  90. package/package.json +144 -0
@@ -0,0 +1,259 @@
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
+ };
@@ -0,0 +1,246 @@
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
+ export class EnhancedHistorySystem extends HistorySystem {
10
+ databasePersistence;
11
+ enhancedConfig;
12
+ syncTimer;
13
+ pendingSync = false;
14
+ constructor(config = {}) {
15
+ const defaultConfig = {
16
+ maxSize: 10000,
17
+ filePath: path.join(os.homedir(), '.lsh_history'),
18
+ shareHistory: true,
19
+ ignoreDups: true,
20
+ ignoreSpace: true,
21
+ expireDuplicatesFirst: true,
22
+ enableCloudSync: true,
23
+ userId: undefined,
24
+ syncInterval: 30000, // 30 seconds
25
+ ...config,
26
+ };
27
+ super(defaultConfig);
28
+ this.enhancedConfig = defaultConfig;
29
+ this.databasePersistence = new DatabasePersistence(defaultConfig.userId);
30
+ if (this.enhancedConfig.enableCloudSync) {
31
+ this.initializeCloudSync();
32
+ }
33
+ }
34
+ /**
35
+ * Initialize cloud synchronization
36
+ */
37
+ async initializeCloudSync() {
38
+ try {
39
+ const isConnected = await this.databasePersistence.testConnection();
40
+ if (isConnected) {
41
+ console.log('✅ Cloud history sync enabled');
42
+ await this.loadCloudHistory();
43
+ this.startSyncTimer();
44
+ }
45
+ else {
46
+ console.log('⚠️ Cloud history sync disabled - database not available');
47
+ }
48
+ }
49
+ catch (error) {
50
+ console.error('Failed to initialize cloud sync:', error);
51
+ }
52
+ }
53
+ /**
54
+ * Load history from cloud database
55
+ */
56
+ async loadCloudHistory() {
57
+ try {
58
+ const cloudEntries = await this.databasePersistence.getHistoryEntries(1000);
59
+ // Convert cloud entries to local format
60
+ const localEntries = cloudEntries.map((entry, index) => ({
61
+ lineNumber: index + 1,
62
+ command: entry.command,
63
+ timestamp: new Date(entry.timestamp).getTime(),
64
+ exitCode: entry.exit_code,
65
+ }));
66
+ // Merge with local history (avoid duplicates)
67
+ this.mergeHistoryEntries(localEntries);
68
+ }
69
+ catch (error) {
70
+ console.error('Failed to load cloud history:', error);
71
+ }
72
+ }
73
+ /**
74
+ * Merge cloud history entries with local history
75
+ */
76
+ mergeHistoryEntries(cloudEntries) {
77
+ const localEntries = this.getAllEntries();
78
+ const mergedEntries = [];
79
+ // Create a map of local entries by command and timestamp
80
+ const localMap = new Map();
81
+ localEntries.forEach(entry => {
82
+ const key = `${entry.command}_${entry.timestamp}`;
83
+ localMap.set(key, entry);
84
+ });
85
+ // Add cloud entries that don't exist locally
86
+ cloudEntries.forEach(entry => {
87
+ const key = `${entry.command}_${entry.timestamp}`;
88
+ if (!localMap.has(key)) {
89
+ mergedEntries.push(entry);
90
+ }
91
+ });
92
+ // Add all local entries
93
+ mergedEntries.push(...localEntries);
94
+ // Sort by timestamp and update line numbers
95
+ mergedEntries.sort((a, b) => a.timestamp - b.timestamp);
96
+ mergedEntries.forEach((entry, index) => {
97
+ entry.lineNumber = index + 1;
98
+ });
99
+ // Update internal entries
100
+ this.entries = mergedEntries;
101
+ }
102
+ /**
103
+ * Start periodic synchronization timer
104
+ */
105
+ startSyncTimer() {
106
+ this.syncTimer = setInterval(() => {
107
+ this.syncToCloud();
108
+ }, this.enhancedConfig.syncInterval);
109
+ }
110
+ /**
111
+ * Stop synchronization timer
112
+ */
113
+ stopSyncTimer() {
114
+ if (this.syncTimer) {
115
+ clearInterval(this.syncTimer);
116
+ this.syncTimer = undefined;
117
+ }
118
+ }
119
+ /**
120
+ * Synchronize local history to cloud
121
+ */
122
+ async syncToCloud() {
123
+ if (this.pendingSync || !this.enhancedConfig.enableCloudSync) {
124
+ return;
125
+ }
126
+ this.pendingSync = true;
127
+ try {
128
+ const localEntries = this.getAllEntries();
129
+ const recentEntries = localEntries.slice(-50); // Sync last 50 entries
130
+ for (const entry of recentEntries) {
131
+ await this.databasePersistence.saveHistoryEntry({
132
+ session_id: this.databasePersistence.getSessionId(),
133
+ command: entry.command,
134
+ working_directory: process.cwd(),
135
+ exit_code: entry.exitCode,
136
+ timestamp: new Date(entry.timestamp).toISOString(),
137
+ hostname: os.hostname(),
138
+ });
139
+ }
140
+ }
141
+ catch (error) {
142
+ console.error('Failed to sync history to cloud:', error);
143
+ }
144
+ finally {
145
+ this.pendingSync = false;
146
+ }
147
+ }
148
+ /**
149
+ * Override addCommand to include cloud sync
150
+ */
151
+ addCommand(command, exitCode) {
152
+ super.addCommand(command, exitCode);
153
+ // Sync to cloud if enabled
154
+ if (this.enhancedConfig.enableCloudSync) {
155
+ this.syncToCloud().catch(error => {
156
+ console.error('Failed to sync command to cloud:', error);
157
+ });
158
+ }
159
+ }
160
+ /**
161
+ * Search history across local and cloud data
162
+ */
163
+ async searchHistoryCloud(query, limit = 20) {
164
+ const localResults = this.searchHistory(query).slice(0, limit);
165
+ if (!this.enhancedConfig.enableCloudSync) {
166
+ return localResults;
167
+ }
168
+ try {
169
+ // Search cloud history
170
+ const cloudEntries = await this.databasePersistence.getHistoryEntries(1000);
171
+ const cloudResults = cloudEntries
172
+ .filter(entry => entry.command.toLowerCase().includes(query.toLowerCase()))
173
+ .slice(0, limit)
174
+ .map((entry, index) => ({
175
+ lineNumber: index + 1,
176
+ command: entry.command,
177
+ timestamp: new Date(entry.timestamp).getTime(),
178
+ exitCode: entry.exit_code,
179
+ }));
180
+ // Merge and deduplicate results
181
+ const allResults = [...localResults, ...cloudResults];
182
+ const uniqueResults = allResults.filter((entry, index, arr) => arr.findIndex(e => e.command === entry.command && e.timestamp === entry.timestamp) === index);
183
+ return uniqueResults.slice(0, limit);
184
+ }
185
+ catch (error) {
186
+ console.error('Failed to search cloud history:', error);
187
+ return localResults;
188
+ }
189
+ }
190
+ /**
191
+ * Get history statistics
192
+ */
193
+ async getHistoryStats() {
194
+ const localEntries = this.getAllEntries();
195
+ let cloudEntries = 0;
196
+ if (this.enhancedConfig.enableCloudSync) {
197
+ try {
198
+ const cloudData = await this.databasePersistence.getHistoryEntries(10000);
199
+ cloudEntries = cloudData.length;
200
+ }
201
+ catch (error) {
202
+ console.error('Failed to get cloud history stats:', error);
203
+ }
204
+ }
205
+ // Calculate statistics
206
+ const commandCounts = new Map();
207
+ let totalLength = 0;
208
+ localEntries.forEach(entry => {
209
+ commandCounts.set(entry.command, (commandCounts.get(entry.command) || 0) + 1);
210
+ totalLength += entry.command.length;
211
+ });
212
+ const mostUsedCommand = Array.from(commandCounts.entries())
213
+ .sort(([, a], [, b]) => b - a)[0]?.[0] || '';
214
+ return {
215
+ localEntries: localEntries.length,
216
+ cloudEntries,
217
+ totalCommands: localEntries.length + cloudEntries,
218
+ mostUsedCommand,
219
+ averageCommandLength: localEntries.length > 0 ? totalLength / localEntries.length : 0,
220
+ };
221
+ }
222
+ /**
223
+ * Enable or disable cloud sync
224
+ */
225
+ setCloudSyncEnabled(enabled) {
226
+ this.enhancedConfig.enableCloudSync = enabled;
227
+ if (enabled) {
228
+ this.initializeCloudSync();
229
+ }
230
+ else {
231
+ this.stopSyncTimer();
232
+ }
233
+ }
234
+ /**
235
+ * Cleanup resources
236
+ */
237
+ destroy() {
238
+ this.stopSyncTimer();
239
+ if (this.enhancedConfig.enableCloudSync) {
240
+ this.syncToCloud().catch(error => {
241
+ console.error('Failed to final sync on destroy:', error);
242
+ });
243
+ }
244
+ }
245
+ }
246
+ export default EnhancedHistorySystem;