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.
- package/.env.example +51 -0
- package/README.md +399 -0
- package/dist/app.js +33 -0
- package/dist/cicd/analytics.js +261 -0
- package/dist/cicd/auth.js +269 -0
- package/dist/cicd/cache-manager.js +172 -0
- package/dist/cicd/data-retention.js +305 -0
- package/dist/cicd/performance-monitor.js +224 -0
- package/dist/cicd/webhook-receiver.js +634 -0
- package/dist/cli.js +500 -0
- package/dist/commands/api.js +343 -0
- package/dist/commands/self.js +318 -0
- package/dist/commands/theme.js +257 -0
- package/dist/commands/zsh-import.js +240 -0
- package/dist/components/App.js +1 -0
- package/dist/components/Divider.js +29 -0
- package/dist/components/REPL.js +43 -0
- package/dist/components/Terminal.js +232 -0
- package/dist/components/UserInput.js +30 -0
- package/dist/daemon/api-server.js +315 -0
- package/dist/daemon/job-registry.js +554 -0
- package/dist/daemon/lshd.js +822 -0
- package/dist/daemon/monitoring-api.js +220 -0
- package/dist/examples/supabase-integration.js +106 -0
- package/dist/lib/api-error-handler.js +183 -0
- package/dist/lib/associative-arrays.js +285 -0
- package/dist/lib/base-api-server.js +290 -0
- package/dist/lib/base-command-registrar.js +286 -0
- package/dist/lib/base-job-manager.js +293 -0
- package/dist/lib/brace-expansion.js +160 -0
- package/dist/lib/builtin-commands.js +439 -0
- package/dist/lib/cloud-config-manager.js +347 -0
- package/dist/lib/command-validator.js +190 -0
- package/dist/lib/completion-system.js +344 -0
- package/dist/lib/cron-job-manager.js +364 -0
- package/dist/lib/daemon-client-helper.js +141 -0
- package/dist/lib/daemon-client.js +501 -0
- package/dist/lib/database-persistence.js +638 -0
- package/dist/lib/database-schema.js +259 -0
- package/dist/lib/enhanced-history-system.js +246 -0
- package/dist/lib/env-validator.js +265 -0
- package/dist/lib/executors/builtin-executor.js +52 -0
- package/dist/lib/extended-globbing.js +411 -0
- package/dist/lib/extended-parameter-expansion.js +227 -0
- package/dist/lib/floating-point-arithmetic.js +256 -0
- package/dist/lib/history-system.js +245 -0
- package/dist/lib/interactive-shell.js +460 -0
- package/dist/lib/job-builtins.js +580 -0
- package/dist/lib/job-manager.js +386 -0
- package/dist/lib/job-storage-database.js +156 -0
- package/dist/lib/job-storage-memory.js +73 -0
- package/dist/lib/logger.js +274 -0
- package/dist/lib/lshrc-init.js +177 -0
- package/dist/lib/pathname-expansion.js +216 -0
- package/dist/lib/prompt-system.js +328 -0
- package/dist/lib/script-runner.js +226 -0
- package/dist/lib/secrets-manager.js +193 -0
- package/dist/lib/shell-executor.js +2504 -0
- package/dist/lib/shell-parser.js +958 -0
- package/dist/lib/shell-types.js +6 -0
- package/dist/lib/shell.lib.js +40 -0
- package/dist/lib/supabase-client.js +58 -0
- package/dist/lib/theme-manager.js +476 -0
- package/dist/lib/variable-expansion.js +385 -0
- package/dist/lib/zsh-compatibility.js +658 -0
- package/dist/lib/zsh-import-manager.js +699 -0
- package/dist/lib/zsh-options.js +328 -0
- package/dist/pipeline/job-tracker.js +491 -0
- package/dist/pipeline/mcli-bridge.js +302 -0
- package/dist/pipeline/pipeline-service.js +1116 -0
- package/dist/pipeline/workflow-engine.js +867 -0
- package/dist/services/api/api.js +58 -0
- package/dist/services/api/auth.js +35 -0
- package/dist/services/api/config.js +7 -0
- package/dist/services/api/file.js +22 -0
- package/dist/services/cron/cron-registrar.js +235 -0
- package/dist/services/cron/cron.js +9 -0
- package/dist/services/daemon/daemon-registrar.js +565 -0
- package/dist/services/daemon/daemon.js +9 -0
- package/dist/services/lib/lib.js +86 -0
- package/dist/services/log-file-extractor.js +170 -0
- package/dist/services/secrets/secrets.js +94 -0
- package/dist/services/shell/shell.js +28 -0
- package/dist/services/supabase/supabase-registrar.js +367 -0
- package/dist/services/supabase/supabase.js +9 -0
- package/dist/services/zapier.js +16 -0
- package/dist/simple-api-server.js +148 -0
- package/dist/store/store.js +31 -0
- package/dist/util/lib.util.js +11 -0
- package/package.json +144 -0
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Persistence Layer for LSH
|
|
3
|
+
* Handles data synchronization with Supabase PostgreSQL
|
|
4
|
+
*/
|
|
5
|
+
import { supabaseClient } from './supabase-client.js';
|
|
6
|
+
import * as os from 'os';
|
|
7
|
+
export class DatabasePersistence {
|
|
8
|
+
client;
|
|
9
|
+
userId;
|
|
10
|
+
sessionId;
|
|
11
|
+
constructor(userId) {
|
|
12
|
+
this.client = supabaseClient.getClient();
|
|
13
|
+
this.userId = userId ? this.generateUserUUID(userId) : undefined;
|
|
14
|
+
this.sessionId = this.generateSessionId();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Generate a deterministic UUID from username
|
|
18
|
+
*/
|
|
19
|
+
generateUserUUID(username) {
|
|
20
|
+
// Create a simple UUID v5-like deterministic UUID from username
|
|
21
|
+
// In production, you'd use a proper UUID library
|
|
22
|
+
const hash = username.split('').reduce((a, b) => {
|
|
23
|
+
a = ((a << 5) - a) + b.charCodeAt(0);
|
|
24
|
+
return a & a;
|
|
25
|
+
}, 0);
|
|
26
|
+
const hex = Math.abs(hash).toString(16).padStart(8, '0');
|
|
27
|
+
return `${hex.substr(0, 8)}-${hex.substr(0, 4)}-4${hex.substr(1, 3)}-8${hex.substr(0, 3)}-${hex}${hex.substr(0, 4)}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generate a unique session ID
|
|
31
|
+
*/
|
|
32
|
+
generateSessionId() {
|
|
33
|
+
return `lsh_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Initialize database schema
|
|
37
|
+
*/
|
|
38
|
+
async initializeSchema() {
|
|
39
|
+
try {
|
|
40
|
+
// Note: In a real implementation, you'd need to run the SQL schema
|
|
41
|
+
// This would typically be done through Supabase dashboard or migrations
|
|
42
|
+
console.log('Database schema initialization would be handled by Supabase migrations');
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('Failed to initialize database schema:', error);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Save shell history entry
|
|
52
|
+
*/
|
|
53
|
+
async saveHistoryEntry(entry) {
|
|
54
|
+
try {
|
|
55
|
+
const insertData = {
|
|
56
|
+
...entry,
|
|
57
|
+
session_id: this.sessionId,
|
|
58
|
+
created_at: new Date().toISOString(),
|
|
59
|
+
updated_at: new Date().toISOString(),
|
|
60
|
+
};
|
|
61
|
+
// Only include user_id if it's not undefined
|
|
62
|
+
if (this.userId !== undefined) {
|
|
63
|
+
insertData.user_id = this.userId;
|
|
64
|
+
}
|
|
65
|
+
const { _data, error } = await this.client
|
|
66
|
+
.from('shell_history')
|
|
67
|
+
.insert([insertData]);
|
|
68
|
+
if (error) {
|
|
69
|
+
console.error('Failed to save history entry:', error);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error('Error saving history entry:', error);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get shell history entries
|
|
81
|
+
*/
|
|
82
|
+
async getHistoryEntries(limit = 100, offset = 0) {
|
|
83
|
+
try {
|
|
84
|
+
let query = this.client
|
|
85
|
+
.from('shell_history')
|
|
86
|
+
.select('*')
|
|
87
|
+
.order('timestamp', { ascending: false })
|
|
88
|
+
.range(offset, offset + limit - 1);
|
|
89
|
+
// Only filter by user_id if it's not undefined
|
|
90
|
+
if (this.userId !== undefined) {
|
|
91
|
+
query = query.eq('user_id', this.userId);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
query = query.is('user_id', null);
|
|
95
|
+
}
|
|
96
|
+
const { data, error } = await query;
|
|
97
|
+
if (error) {
|
|
98
|
+
console.error('Failed to get history entries:', error);
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
return data || [];
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error('Error getting history entries:', error);
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Save shell job
|
|
110
|
+
*/
|
|
111
|
+
async saveJob(job) {
|
|
112
|
+
try {
|
|
113
|
+
const insertData = {
|
|
114
|
+
...job,
|
|
115
|
+
session_id: this.sessionId,
|
|
116
|
+
created_at: new Date().toISOString(),
|
|
117
|
+
updated_at: new Date().toISOString(),
|
|
118
|
+
};
|
|
119
|
+
// Only include user_id if it's not undefined
|
|
120
|
+
if (this.userId !== undefined) {
|
|
121
|
+
insertData.user_id = this.userId;
|
|
122
|
+
}
|
|
123
|
+
const { _data, error } = await this.client
|
|
124
|
+
.from('shell_jobs')
|
|
125
|
+
.insert([insertData]);
|
|
126
|
+
if (error) {
|
|
127
|
+
console.error('Failed to save job:', error);
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error('Error saving job:', error);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Update shell job status
|
|
139
|
+
*/
|
|
140
|
+
async updateJobStatus(jobId, status, exitCode) {
|
|
141
|
+
try {
|
|
142
|
+
const updateData = {
|
|
143
|
+
status,
|
|
144
|
+
updated_at: new Date().toISOString(),
|
|
145
|
+
};
|
|
146
|
+
if (status === 'completed' || status === 'failed') {
|
|
147
|
+
updateData.completed_at = new Date().toISOString();
|
|
148
|
+
if (exitCode !== undefined) {
|
|
149
|
+
updateData.exit_code = exitCode;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
let query = this.client
|
|
153
|
+
.from('shell_jobs')
|
|
154
|
+
.update(updateData)
|
|
155
|
+
.eq('job_id', jobId);
|
|
156
|
+
// Only filter by user_id if it's not undefined
|
|
157
|
+
if (this.userId !== undefined) {
|
|
158
|
+
query = query.eq('user_id', this.userId);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
query = query.is('user_id', null);
|
|
162
|
+
}
|
|
163
|
+
const { _data, error } = await query;
|
|
164
|
+
if (error) {
|
|
165
|
+
console.error('Failed to update job status:', error);
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error('Error updating job status:', error);
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get active jobs
|
|
177
|
+
*/
|
|
178
|
+
async getActiveJobs() {
|
|
179
|
+
try {
|
|
180
|
+
let query = this.client
|
|
181
|
+
.from('shell_jobs')
|
|
182
|
+
.select('*')
|
|
183
|
+
.in('status', ['running', 'stopped', 'completed', 'failed'])
|
|
184
|
+
.order('created_at', { ascending: false });
|
|
185
|
+
// Only filter by user_id if it's not undefined
|
|
186
|
+
if (this.userId !== undefined) {
|
|
187
|
+
query = query.eq('user_id', this.userId);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
query = query.is('user_id', null);
|
|
191
|
+
}
|
|
192
|
+
const { data, error } = await query;
|
|
193
|
+
if (error) {
|
|
194
|
+
console.error('Failed to get active jobs:', error);
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
return data || [];
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
console.error('Error getting active jobs:', error);
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Save shell configuration
|
|
206
|
+
*/
|
|
207
|
+
async saveConfiguration(config) {
|
|
208
|
+
try {
|
|
209
|
+
const upsertData = {
|
|
210
|
+
...config,
|
|
211
|
+
created_at: new Date().toISOString(),
|
|
212
|
+
updated_at: new Date().toISOString(),
|
|
213
|
+
};
|
|
214
|
+
// Only include user_id if it's not undefined
|
|
215
|
+
if (this.userId !== undefined) {
|
|
216
|
+
upsertData.user_id = this.userId;
|
|
217
|
+
}
|
|
218
|
+
const { _data, error } = await this.client
|
|
219
|
+
.from('shell_configuration')
|
|
220
|
+
.upsert([upsertData], {
|
|
221
|
+
onConflict: 'user_id,config_key'
|
|
222
|
+
});
|
|
223
|
+
if (error) {
|
|
224
|
+
console.error('Failed to save configuration:', error);
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
console.error('Error saving configuration:', error);
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Get shell configuration
|
|
236
|
+
*/
|
|
237
|
+
async getConfiguration(key) {
|
|
238
|
+
try {
|
|
239
|
+
let query = this.client
|
|
240
|
+
.from('shell_configuration')
|
|
241
|
+
.select('*');
|
|
242
|
+
// Only filter by user_id if it's not undefined
|
|
243
|
+
if (this.userId !== undefined) {
|
|
244
|
+
query = query.eq('user_id', this.userId);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// If no user_id, get only default configurations
|
|
248
|
+
query = query.is('user_id', null);
|
|
249
|
+
}
|
|
250
|
+
if (key) {
|
|
251
|
+
query = query.eq('config_key', key);
|
|
252
|
+
}
|
|
253
|
+
const { data, error } = await query;
|
|
254
|
+
if (error) {
|
|
255
|
+
console.error('Failed to get configuration:', error);
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
return data || [];
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
console.error('Error getting configuration:', error);
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Save shell alias
|
|
267
|
+
*/
|
|
268
|
+
async saveAlias(alias) {
|
|
269
|
+
try {
|
|
270
|
+
const upsertData = {
|
|
271
|
+
...alias,
|
|
272
|
+
created_at: new Date().toISOString(),
|
|
273
|
+
updated_at: new Date().toISOString(),
|
|
274
|
+
};
|
|
275
|
+
// Only include user_id if it's not undefined
|
|
276
|
+
if (this.userId !== undefined) {
|
|
277
|
+
upsertData.user_id = this.userId;
|
|
278
|
+
}
|
|
279
|
+
const { _data, error } = await this.client
|
|
280
|
+
.from('shell_aliases')
|
|
281
|
+
.upsert([upsertData], {
|
|
282
|
+
onConflict: 'user_id,alias_name'
|
|
283
|
+
});
|
|
284
|
+
if (error) {
|
|
285
|
+
console.error('Failed to save alias:', error);
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
console.error('Error saving alias:', error);
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Get shell aliases
|
|
297
|
+
*/
|
|
298
|
+
async getAliases() {
|
|
299
|
+
try {
|
|
300
|
+
let query = this.client
|
|
301
|
+
.from('shell_aliases')
|
|
302
|
+
.select('*')
|
|
303
|
+
.eq('is_active', true);
|
|
304
|
+
// Only filter by user_id if it's not undefined
|
|
305
|
+
if (this.userId !== undefined) {
|
|
306
|
+
query = query.eq('user_id', this.userId);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
query = query.is('user_id', null);
|
|
310
|
+
}
|
|
311
|
+
const { data, error } = await query;
|
|
312
|
+
if (error) {
|
|
313
|
+
console.error('Failed to get aliases:', error);
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
return data || [];
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
console.error('Error getting aliases:', error);
|
|
320
|
+
return [];
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Save shell function
|
|
325
|
+
*/
|
|
326
|
+
async saveFunction(func) {
|
|
327
|
+
try {
|
|
328
|
+
const upsertData = {
|
|
329
|
+
...func,
|
|
330
|
+
created_at: new Date().toISOString(),
|
|
331
|
+
updated_at: new Date().toISOString(),
|
|
332
|
+
};
|
|
333
|
+
// Only include user_id if it's not undefined
|
|
334
|
+
if (this.userId !== undefined) {
|
|
335
|
+
upsertData.user_id = this.userId;
|
|
336
|
+
}
|
|
337
|
+
const { _data, error } = await this.client
|
|
338
|
+
.from('shell_functions')
|
|
339
|
+
.upsert([upsertData], {
|
|
340
|
+
onConflict: 'user_id,function_name'
|
|
341
|
+
});
|
|
342
|
+
if (error) {
|
|
343
|
+
console.error('Failed to save function:', error);
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
console.error('Error saving function:', error);
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Get shell functions
|
|
355
|
+
*/
|
|
356
|
+
async getFunctions() {
|
|
357
|
+
try {
|
|
358
|
+
let query = this.client
|
|
359
|
+
.from('shell_functions')
|
|
360
|
+
.select('*')
|
|
361
|
+
.eq('is_active', true);
|
|
362
|
+
// Only filter by user_id if it's not undefined
|
|
363
|
+
if (this.userId !== undefined) {
|
|
364
|
+
query = query.eq('user_id', this.userId);
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
query = query.is('user_id', null);
|
|
368
|
+
}
|
|
369
|
+
const { data, error } = await query;
|
|
370
|
+
if (error) {
|
|
371
|
+
console.error('Failed to get functions:', error);
|
|
372
|
+
return [];
|
|
373
|
+
}
|
|
374
|
+
return data || [];
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
console.error('Error getting functions:', error);
|
|
378
|
+
return [];
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Start a new shell session
|
|
383
|
+
*/
|
|
384
|
+
async startSession(workingDirectory, environmentVariables) {
|
|
385
|
+
try {
|
|
386
|
+
const insertData = {
|
|
387
|
+
session_id: this.sessionId,
|
|
388
|
+
hostname: os.hostname(),
|
|
389
|
+
working_directory: workingDirectory,
|
|
390
|
+
environment_variables: environmentVariables,
|
|
391
|
+
started_at: new Date().toISOString(),
|
|
392
|
+
is_active: true,
|
|
393
|
+
created_at: new Date().toISOString(),
|
|
394
|
+
updated_at: new Date().toISOString(),
|
|
395
|
+
};
|
|
396
|
+
// Only include user_id if it's not undefined
|
|
397
|
+
if (this.userId !== undefined) {
|
|
398
|
+
insertData.user_id = this.userId;
|
|
399
|
+
}
|
|
400
|
+
const { _data, error } = await this.client
|
|
401
|
+
.from('shell_sessions')
|
|
402
|
+
.insert([insertData]);
|
|
403
|
+
if (error) {
|
|
404
|
+
console.error('Failed to start session:', error);
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
console.error('Error starting session:', error);
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* End the current shell session
|
|
416
|
+
*/
|
|
417
|
+
async endSession() {
|
|
418
|
+
try {
|
|
419
|
+
let query = this.client
|
|
420
|
+
.from('shell_sessions')
|
|
421
|
+
.update({
|
|
422
|
+
ended_at: new Date().toISOString(),
|
|
423
|
+
is_active: false,
|
|
424
|
+
updated_at: new Date().toISOString(),
|
|
425
|
+
})
|
|
426
|
+
.eq('session_id', this.sessionId);
|
|
427
|
+
// Only filter by user_id if it's not undefined
|
|
428
|
+
if (this.userId !== undefined) {
|
|
429
|
+
query = query.eq('user_id', this.userId);
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
query = query.is('user_id', null);
|
|
433
|
+
}
|
|
434
|
+
const { _data, error } = await query;
|
|
435
|
+
if (error) {
|
|
436
|
+
console.error('Failed to end session:', error);
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
console.error('Error ending session:', error);
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Test database connectivity
|
|
448
|
+
*/
|
|
449
|
+
async testConnection() {
|
|
450
|
+
return await supabaseClient.testConnection();
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Get session ID
|
|
454
|
+
*/
|
|
455
|
+
getSessionId() {
|
|
456
|
+
return this.sessionId;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Get latest rows from all database tables
|
|
460
|
+
*/
|
|
461
|
+
async getLatestRows(limit = 5) {
|
|
462
|
+
const result = {};
|
|
463
|
+
try {
|
|
464
|
+
// Get latest shell history entries
|
|
465
|
+
const historyQuery = this.client
|
|
466
|
+
.from('shell_history')
|
|
467
|
+
.select('*')
|
|
468
|
+
.order('created_at', { ascending: false })
|
|
469
|
+
.limit(limit);
|
|
470
|
+
if (this.userId !== undefined) {
|
|
471
|
+
historyQuery.eq('user_id', this.userId);
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
historyQuery.is('user_id', null);
|
|
475
|
+
}
|
|
476
|
+
const { data: historyData } = await historyQuery;
|
|
477
|
+
result.shell_history = historyData || [];
|
|
478
|
+
// Get latest shell jobs
|
|
479
|
+
const jobsQuery = this.client
|
|
480
|
+
.from('shell_jobs')
|
|
481
|
+
.select('*')
|
|
482
|
+
.order('created_at', { ascending: false })
|
|
483
|
+
.limit(limit);
|
|
484
|
+
if (this.userId !== undefined) {
|
|
485
|
+
jobsQuery.eq('user_id', this.userId);
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
jobsQuery.is('user_id', null);
|
|
489
|
+
}
|
|
490
|
+
const { data: jobsData } = await jobsQuery;
|
|
491
|
+
result.shell_jobs = jobsData || [];
|
|
492
|
+
// Get latest shell configuration
|
|
493
|
+
const configQuery = this.client
|
|
494
|
+
.from('shell_configuration')
|
|
495
|
+
.select('*')
|
|
496
|
+
.order('created_at', { ascending: false })
|
|
497
|
+
.limit(limit);
|
|
498
|
+
if (this.userId !== undefined) {
|
|
499
|
+
configQuery.eq('user_id', this.userId);
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
configQuery.is('user_id', null);
|
|
503
|
+
}
|
|
504
|
+
const { data: configData } = await configQuery;
|
|
505
|
+
result.shell_configuration = configData || [];
|
|
506
|
+
// Get latest shell sessions
|
|
507
|
+
const sessionsQuery = this.client
|
|
508
|
+
.from('shell_sessions')
|
|
509
|
+
.select('*')
|
|
510
|
+
.order('created_at', { ascending: false })
|
|
511
|
+
.limit(limit);
|
|
512
|
+
if (this.userId !== undefined) {
|
|
513
|
+
sessionsQuery.eq('user_id', this.userId);
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
sessionsQuery.is('user_id', null);
|
|
517
|
+
}
|
|
518
|
+
const { data: sessionsData } = await sessionsQuery;
|
|
519
|
+
result.shell_sessions = sessionsData || [];
|
|
520
|
+
// Get latest shell aliases
|
|
521
|
+
const aliasesQuery = this.client
|
|
522
|
+
.from('shell_aliases')
|
|
523
|
+
.select('*')
|
|
524
|
+
.order('created_at', { ascending: false })
|
|
525
|
+
.limit(limit);
|
|
526
|
+
if (this.userId !== undefined) {
|
|
527
|
+
aliasesQuery.eq('user_id', this.userId);
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
aliasesQuery.is('user_id', null);
|
|
531
|
+
}
|
|
532
|
+
const { data: aliasesData } = await aliasesQuery;
|
|
533
|
+
result.shell_aliases = aliasesData || [];
|
|
534
|
+
// Get latest shell functions
|
|
535
|
+
const functionsQuery = this.client
|
|
536
|
+
.from('shell_functions')
|
|
537
|
+
.select('*')
|
|
538
|
+
.order('created_at', { ascending: false })
|
|
539
|
+
.limit(limit);
|
|
540
|
+
if (this.userId !== undefined) {
|
|
541
|
+
functionsQuery.eq('user_id', this.userId);
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
functionsQuery.is('user_id', null);
|
|
545
|
+
}
|
|
546
|
+
const { data: functionsData } = await functionsQuery;
|
|
547
|
+
result.shell_functions = functionsData || [];
|
|
548
|
+
// Get latest shell completions
|
|
549
|
+
const completionsQuery = this.client
|
|
550
|
+
.from('shell_completions')
|
|
551
|
+
.select('*')
|
|
552
|
+
.order('created_at', { ascending: false })
|
|
553
|
+
.limit(limit);
|
|
554
|
+
if (this.userId !== undefined) {
|
|
555
|
+
completionsQuery.eq('user_id', this.userId);
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
completionsQuery.is('user_id', null);
|
|
559
|
+
}
|
|
560
|
+
const { data: completionsData } = await completionsQuery;
|
|
561
|
+
result.shell_completions = completionsData || [];
|
|
562
|
+
// Get latest politician trading disclosures (global data, no user filtering)
|
|
563
|
+
const { data: tradingData } = await this.client
|
|
564
|
+
.from('trading_disclosures')
|
|
565
|
+
.select('*')
|
|
566
|
+
.order('created_at', { ascending: false })
|
|
567
|
+
.limit(limit);
|
|
568
|
+
result.trading_disclosures = tradingData || [];
|
|
569
|
+
// Get latest politicians (global data, no user filtering)
|
|
570
|
+
const { data: politiciansData } = await this.client
|
|
571
|
+
.from('politicians')
|
|
572
|
+
.select('*')
|
|
573
|
+
.order('created_at', { ascending: false })
|
|
574
|
+
.limit(limit);
|
|
575
|
+
result.politicians = politiciansData || [];
|
|
576
|
+
// Get latest data pull jobs (global data, no user filtering)
|
|
577
|
+
const { data: dataPullJobsData } = await this.client
|
|
578
|
+
.from('data_pull_jobs')
|
|
579
|
+
.select('*')
|
|
580
|
+
.order('created_at', { ascending: false })
|
|
581
|
+
.limit(limit);
|
|
582
|
+
result.data_pull_jobs = dataPullJobsData || [];
|
|
583
|
+
return result;
|
|
584
|
+
}
|
|
585
|
+
catch (error) {
|
|
586
|
+
console.error('Error getting latest rows:', error);
|
|
587
|
+
return {};
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Get latest rows from a specific table
|
|
592
|
+
*/
|
|
593
|
+
async getLatestRowsFromTable(tableName, limit = 5) {
|
|
594
|
+
try {
|
|
595
|
+
const validTables = [
|
|
596
|
+
'shell_history',
|
|
597
|
+
'shell_jobs',
|
|
598
|
+
'shell_configuration',
|
|
599
|
+
'shell_sessions',
|
|
600
|
+
'shell_aliases',
|
|
601
|
+
'shell_functions',
|
|
602
|
+
'shell_completions',
|
|
603
|
+
'trading_disclosures',
|
|
604
|
+
'politicians',
|
|
605
|
+
'data_pull_jobs'
|
|
606
|
+
];
|
|
607
|
+
if (!validTables.includes(tableName)) {
|
|
608
|
+
throw new Error(`Invalid table name: ${tableName}`);
|
|
609
|
+
}
|
|
610
|
+
let query = this.client
|
|
611
|
+
.from(tableName)
|
|
612
|
+
.select('*')
|
|
613
|
+
.order('created_at', { ascending: false })
|
|
614
|
+
.limit(limit);
|
|
615
|
+
// Filter by user_id if available (except for politician trading tables which are global)
|
|
616
|
+
const globalTables = ['trading_disclosures', 'politicians', 'data_pull_jobs'];
|
|
617
|
+
if (!globalTables.includes(tableName)) {
|
|
618
|
+
if (this.userId !== undefined) {
|
|
619
|
+
query = query.eq('user_id', this.userId);
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
query = query.is('user_id', null);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const { data, error } = await query;
|
|
626
|
+
if (error) {
|
|
627
|
+
console.error(`Failed to get latest rows from ${tableName}:`, error);
|
|
628
|
+
return [];
|
|
629
|
+
}
|
|
630
|
+
return data || [];
|
|
631
|
+
}
|
|
632
|
+
catch (error) {
|
|
633
|
+
console.error(`Error getting latest rows from ${tableName}:`, error);
|
|
634
|
+
return [];
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
export default DatabasePersistence;
|