lsh-framework 0.8.2 → 0.9.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.
- package/README.md +75 -1
- package/dist/cli.js +14 -10
- package/dist/daemon/lshd.js +23 -13
- package/dist/lib/api-error-handler.js +16 -14
- package/dist/lib/base-command-registrar.js +6 -5
- package/dist/lib/daemon-client.js +13 -9
- package/dist/lib/database-persistence.js +8 -8
- package/dist/lib/env-validator.js +0 -3
- package/dist/lib/logger.js +0 -1
- package/dist/lib/secrets-manager.js +254 -153
- package/dist/lib/zsh-import-manager.js +17 -9
- package/dist/pipeline/job-tracker.js +1 -1
- package/dist/pipeline/mcli-bridge.js +11 -5
- package/dist/pipeline/workflow-engine.js +10 -7
- package/dist/services/cron/cron-registrar.js +27 -22
- package/dist/services/daemon/daemon-registrar.js +27 -13
- package/dist/services/secrets/secrets.js +37 -28
- package/dist/services/supabase/supabase-registrar.js +40 -33
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -19,9 +19,44 @@ Traditional secret management tools are either too complex, too expensive, or re
|
|
|
19
19
|
|
|
20
20
|
## Quick Start (30 seconds)
|
|
21
21
|
|
|
22
|
+
### New in v0.8.2+: Smart Sync (Easiest Way!)
|
|
23
|
+
|
|
22
24
|
```bash
|
|
23
25
|
# 1. Install
|
|
24
|
-
npm install -g
|
|
26
|
+
npm install -g lsh-framework
|
|
27
|
+
|
|
28
|
+
# 2. Configure Supabase (free tier works!)
|
|
29
|
+
# Add to .env:
|
|
30
|
+
# SUPABASE_URL=https://your-project.supabase.co
|
|
31
|
+
# SUPABASE_ANON_KEY=<your-anon-key>
|
|
32
|
+
|
|
33
|
+
# 3. ONE command does everything!
|
|
34
|
+
cd ~/repos/your-project
|
|
35
|
+
lsh lib secrets sync
|
|
36
|
+
|
|
37
|
+
# That's it! Smart Sync:
|
|
38
|
+
# ✅ Auto-generates encryption key
|
|
39
|
+
# ✅ Creates .env from .env.example
|
|
40
|
+
# ✅ Adds .env to .gitignore
|
|
41
|
+
# ✅ Pushes to cloud
|
|
42
|
+
# ✅ Namespaces by repo name
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Sync AND Load in One Command
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Sync and load secrets into current shell
|
|
49
|
+
eval "$(lsh lib secrets sync --load)"
|
|
50
|
+
|
|
51
|
+
# Your secrets are now available!
|
|
52
|
+
echo $DATABASE_URL
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Traditional Method (Still Works)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# 1. Install
|
|
59
|
+
npm install -g lsh-framework
|
|
25
60
|
|
|
26
61
|
# 2. Generate encryption key
|
|
27
62
|
lsh lib secrets key
|
|
@@ -44,6 +79,35 @@ lsh lib secrets pull
|
|
|
44
79
|
|
|
45
80
|
## Core Features
|
|
46
81
|
|
|
82
|
+
### 🚀 Smart Sync (New in v0.8.2!)
|
|
83
|
+
|
|
84
|
+
**One command. Zero configuration. Automatic everything.**
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
cd ~/repos/my-app
|
|
88
|
+
lsh lib secrets sync # Auto-setup and sync
|
|
89
|
+
eval "$(lsh lib secrets sync --load)" # Sync AND load into shell
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
What Smart Sync does automatically:
|
|
93
|
+
- ✅ **Detects git repos** - Namespaces secrets by project name
|
|
94
|
+
- ✅ **Generates keys** - Creates encryption key if missing
|
|
95
|
+
- ✅ **Creates .env** - From .env.example or template
|
|
96
|
+
- ✅ **Updates .gitignore** - Ensures .env is never committed
|
|
97
|
+
- ✅ **Intelligent sync** - Pushes/pulls based on what's newer
|
|
98
|
+
- ✅ **Load mode** - Sync and load with `eval` in one command
|
|
99
|
+
|
|
100
|
+
**Repository Isolation:**
|
|
101
|
+
```bash
|
|
102
|
+
cd ~/repos/app1
|
|
103
|
+
lsh lib secrets sync # Stored as: app1_dev
|
|
104
|
+
|
|
105
|
+
cd ~/repos/app2
|
|
106
|
+
lsh lib secrets sync # Stored as: app2_dev (separate!)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
No more conflicts between projects using the same environment names!
|
|
110
|
+
|
|
47
111
|
### 🔐 Secrets Management
|
|
48
112
|
|
|
49
113
|
- **AES-256 Encryption** - Military-grade encryption for all secrets
|
|
@@ -52,6 +116,7 @@ lsh lib secrets pull
|
|
|
52
116
|
- **Masked Viewing** - View secrets safely without exposing full values
|
|
53
117
|
- **Automatic Backup** - Never lose your `.env` files
|
|
54
118
|
- **Version Control** - Track changes to your secrets over time
|
|
119
|
+
- **Smart Sync** - Auto-setup with git repo detection (v0.8.2+)
|
|
55
120
|
|
|
56
121
|
### 🔄 Automatic Rotation (Unique Feature!)
|
|
57
122
|
|
|
@@ -536,12 +601,21 @@ lsh lib daemon start
|
|
|
536
601
|
|
|
537
602
|
## Documentation
|
|
538
603
|
|
|
604
|
+
### Secrets Management
|
|
605
|
+
- **[SMART_SYNC_GUIDE.md](docs/features/secrets/SMART_SYNC_GUIDE.md)** - 🆕 Smart Sync complete guide (v0.8.2+)
|
|
539
606
|
- **[SECRETS_GUIDE.md](docs/features/secrets/SECRETS_GUIDE.md)** - Complete secrets management guide
|
|
540
607
|
- **[SECRETS_QUICK_REFERENCE.md](docs/features/secrets/SECRETS_QUICK_REFERENCE.md)** - Quick reference for daily use
|
|
541
608
|
- **[SECRETS_CHEATSHEET.txt](SECRETS_CHEATSHEET.txt)** - Command cheatsheet
|
|
609
|
+
|
|
610
|
+
### Installation & Development
|
|
542
611
|
- **[INSTALL.md](docs/deployment/INSTALL.md)** - Detailed installation instructions
|
|
543
612
|
- **[CLAUDE.md](CLAUDE.md)** - Developer guide for contributors
|
|
544
613
|
|
|
614
|
+
### Release Notes
|
|
615
|
+
- **[v0.8.3](docs/releases/0.8.3.md)** - Hotfix: Logger output in load mode
|
|
616
|
+
- **[v0.8.2](docs/releases/0.8.2.md)** - Smart Sync feature release
|
|
617
|
+
- **[v0.8.1](docs/releases/0.8.1.md)** - Previous releases
|
|
618
|
+
|
|
545
619
|
## Architecture
|
|
546
620
|
|
|
547
621
|
### Secrets Flow
|
package/dist/cli.js
CHANGED
|
@@ -72,13 +72,17 @@ program
|
|
|
72
72
|
console.log('LSH - Encrypted Secrets Manager with Automatic Rotation');
|
|
73
73
|
console.log('');
|
|
74
74
|
console.log('🔐 Secrets Management (Primary Features):');
|
|
75
|
-
console.log('
|
|
76
|
-
console.log('
|
|
77
|
-
console.log('
|
|
78
|
-
console.log('
|
|
79
|
-
console.log('
|
|
80
|
-
console.log('
|
|
81
|
-
console.log('
|
|
75
|
+
console.log(' sync Check sync status & get recommendations');
|
|
76
|
+
console.log(' push Upload .env to encrypted cloud storage');
|
|
77
|
+
console.log(' pull Download .env from cloud storage');
|
|
78
|
+
console.log(' list List all stored environments');
|
|
79
|
+
console.log(' show View secrets (masked)');
|
|
80
|
+
console.log(' key Generate encryption key');
|
|
81
|
+
console.log(' create Create new .env file');
|
|
82
|
+
console.log(' get <key> Get a specific secret value');
|
|
83
|
+
console.log(' set <key> <value> Set a specific secret value');
|
|
84
|
+
console.log(' delete Delete .env file');
|
|
85
|
+
console.log(' status Get detailed secrets status');
|
|
82
86
|
console.log('');
|
|
83
87
|
console.log('🔄 Automation (Schedule secret rotation):');
|
|
84
88
|
console.log(' lib cron add Schedule automatic tasks');
|
|
@@ -86,9 +90,9 @@ program
|
|
|
86
90
|
console.log(' lib daemon start Start persistent daemon');
|
|
87
91
|
console.log('');
|
|
88
92
|
console.log('🚀 Quick Start:');
|
|
89
|
-
console.log(' lsh
|
|
90
|
-
console.log(' lsh
|
|
91
|
-
console.log(' lsh
|
|
93
|
+
console.log(' lsh key # Generate encryption key');
|
|
94
|
+
console.log(' lsh push --env dev # Push your secrets');
|
|
95
|
+
console.log(' lsh pull --env dev # Pull on another machine');
|
|
92
96
|
console.log('');
|
|
93
97
|
console.log('📚 More Commands:');
|
|
94
98
|
console.log(' lib api API server management');
|
package/dist/daemon/lshd.js
CHANGED
|
@@ -92,7 +92,8 @@ export class LSHJobDaemon extends EventEmitter {
|
|
|
92
92
|
this.log('INFO', `API Server started on port ${this.config.apiPort}`);
|
|
93
93
|
}
|
|
94
94
|
catch (error) {
|
|
95
|
-
|
|
95
|
+
const err = error;
|
|
96
|
+
this.log('ERROR', `Failed to start API server: ${err.message}`);
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
// Setup cleanup handlers
|
|
@@ -151,14 +152,23 @@ export class LSHJobDaemon extends EventEmitter {
|
|
|
151
152
|
async getStatus() {
|
|
152
153
|
const stats = this.jobManager.getJobStats();
|
|
153
154
|
const uptime = process.uptime();
|
|
155
|
+
const memUsage = process.memoryUsage();
|
|
154
156
|
return {
|
|
155
|
-
|
|
157
|
+
running: this.isRunning,
|
|
156
158
|
pid: process.pid,
|
|
157
159
|
uptime,
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
jobCount: stats.total || 0,
|
|
161
|
+
memoryUsage: {
|
|
162
|
+
heapUsed: memUsage.heapUsed,
|
|
163
|
+
heapTotal: memUsage.heapTotal,
|
|
164
|
+
external: memUsage.external
|
|
165
|
+
},
|
|
166
|
+
jobs: {
|
|
167
|
+
total: stats.total || 0,
|
|
168
|
+
running: stats.running || 0,
|
|
169
|
+
completed: stats.completed,
|
|
170
|
+
failed: stats.failed
|
|
171
|
+
}
|
|
162
172
|
};
|
|
163
173
|
}
|
|
164
174
|
/**
|
|
@@ -425,7 +435,7 @@ export class LSHJobDaemon extends EventEmitter {
|
|
|
425
435
|
job.completedAt = undefined;
|
|
426
436
|
job.stdout = '';
|
|
427
437
|
job.stderr = '';
|
|
428
|
-
this.jobManager.persistJobs();
|
|
438
|
+
await this.jobManager.persistJobs();
|
|
429
439
|
this.log('INFO', `🔄 Reset completed job for recurring execution: ${job.id} (${job.name})`);
|
|
430
440
|
}
|
|
431
441
|
// Track that we're running this job now
|
|
@@ -534,7 +544,7 @@ export class LSHJobDaemon extends EventEmitter {
|
|
|
534
544
|
job.stderr = '';
|
|
535
545
|
// Force persistence by calling internal method via reflection
|
|
536
546
|
// Note: This is a temporary workaround for private method access
|
|
537
|
-
this.jobManager.persistJobs();
|
|
547
|
+
await this.jobManager.persistJobs();
|
|
538
548
|
this.log('INFO', `🔄 Reset recurring job status: ${jobId} (${job.name}) for next scheduled run`);
|
|
539
549
|
}
|
|
540
550
|
}
|
|
@@ -636,7 +646,7 @@ export class LSHJobDaemon extends EventEmitter {
|
|
|
636
646
|
fs.unlinkSync(this.config.socketPath);
|
|
637
647
|
// Retry after cleanup
|
|
638
648
|
setTimeout(() => {
|
|
639
|
-
this.ipcServer
|
|
649
|
+
this.ipcServer?.listen(this.config.socketPath);
|
|
640
650
|
}, 1000);
|
|
641
651
|
}
|
|
642
652
|
catch (cleanupError) {
|
|
@@ -647,7 +657,7 @@ export class LSHJobDaemon extends EventEmitter {
|
|
|
647
657
|
}
|
|
648
658
|
}
|
|
649
659
|
async handleIPCMessage(message) {
|
|
650
|
-
const { command, args } = message;
|
|
660
|
+
const { command, args = {} } = message;
|
|
651
661
|
switch (command) {
|
|
652
662
|
case 'status':
|
|
653
663
|
return await this.getStatus();
|
|
@@ -764,14 +774,13 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
764
774
|
id: `job_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
765
775
|
name: `Manual Job - ${jobCommand}`,
|
|
766
776
|
command: jobCommand,
|
|
767
|
-
type: '
|
|
777
|
+
type: 'shell',
|
|
768
778
|
schedule: { interval: 0 }, // Run once
|
|
769
779
|
env: process.env,
|
|
770
780
|
cwd: process.cwd(),
|
|
771
781
|
user: process.env.USER,
|
|
772
782
|
priority: 5,
|
|
773
783
|
tags: ['manual'],
|
|
774
|
-
enabled: true,
|
|
775
784
|
maxRetries: 0,
|
|
776
785
|
timeout: 0,
|
|
777
786
|
};
|
|
@@ -784,7 +793,8 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
784
793
|
process.exit(0);
|
|
785
794
|
}
|
|
786
795
|
catch (error) {
|
|
787
|
-
|
|
796
|
+
const err = error;
|
|
797
|
+
cliLogger.error('Failed to add job', err);
|
|
788
798
|
process.exit(1);
|
|
789
799
|
}
|
|
790
800
|
})();
|
|
@@ -37,20 +37,21 @@ export class ApiError extends Error {
|
|
|
37
37
|
* @param statusCode - HTTP status code (default: 500)
|
|
38
38
|
*/
|
|
39
39
|
export function sendError(res, error, statusCode) {
|
|
40
|
-
const
|
|
40
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
41
|
+
const status = statusCode || (err instanceof ApiError ? err.statusCode : 500);
|
|
41
42
|
const response = {
|
|
42
|
-
error:
|
|
43
|
+
error: err.message || 'An unexpected error occurred',
|
|
43
44
|
timestamp: new Date().toISOString(),
|
|
44
45
|
};
|
|
45
|
-
if (
|
|
46
|
-
if (
|
|
47
|
-
response.code =
|
|
48
|
-
if (
|
|
49
|
-
response.details =
|
|
46
|
+
if (err instanceof ApiError) {
|
|
47
|
+
if (err.code)
|
|
48
|
+
response.code = err.code;
|
|
49
|
+
if (err.details)
|
|
50
|
+
response.details = err.details;
|
|
50
51
|
}
|
|
51
52
|
// Log error for debugging (in production, use proper logger)
|
|
52
53
|
if (status >= 500) {
|
|
53
|
-
console.error('API Error:',
|
|
54
|
+
console.error('API Error:', err);
|
|
54
55
|
}
|
|
55
56
|
res.status(status).json(response);
|
|
56
57
|
}
|
|
@@ -137,21 +138,22 @@ export async function handleApiOperation(res, operation, config = {}, webhookTri
|
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
catch (error) {
|
|
141
|
+
const err = error;
|
|
140
142
|
// Determine appropriate status code
|
|
141
143
|
let statusCode = 400;
|
|
142
|
-
if (
|
|
143
|
-
statusCode =
|
|
144
|
+
if (err instanceof ApiError) {
|
|
145
|
+
statusCode = err.statusCode;
|
|
144
146
|
}
|
|
145
|
-
else if (
|
|
147
|
+
else if (err.message.includes('not found') || err.message.includes('Not found')) {
|
|
146
148
|
statusCode = 404;
|
|
147
149
|
}
|
|
148
|
-
else if (
|
|
150
|
+
else if (err.message.includes('permission') || err.message.includes('unauthorized')) {
|
|
149
151
|
statusCode = 403;
|
|
150
152
|
}
|
|
151
|
-
else if (
|
|
153
|
+
else if (err.message.includes('exists') || err.message.includes('duplicate')) {
|
|
152
154
|
statusCode = 409;
|
|
153
155
|
}
|
|
154
|
-
sendError(res,
|
|
156
|
+
sendError(res, err, statusCode);
|
|
155
157
|
}
|
|
156
158
|
}
|
|
157
159
|
/**
|
|
@@ -86,7 +86,8 @@ export class BaseCommandRegistrar {
|
|
|
86
86
|
await config.action(...args);
|
|
87
87
|
}
|
|
88
88
|
catch (error) {
|
|
89
|
-
|
|
89
|
+
const err = error;
|
|
90
|
+
this.logError('Command failed', err);
|
|
90
91
|
process.exit(1);
|
|
91
92
|
}
|
|
92
93
|
});
|
|
@@ -133,14 +134,14 @@ export class BaseCommandRegistrar {
|
|
|
133
134
|
logSuccess(message, data) {
|
|
134
135
|
this.logger.info(message);
|
|
135
136
|
if (data !== undefined) {
|
|
136
|
-
if (typeof data === 'object' && !Array.isArray(data)) {
|
|
137
|
+
if (typeof data === 'object' && data !== null && !Array.isArray(data)) {
|
|
137
138
|
Object.entries(data).forEach(([key, value]) => {
|
|
138
139
|
this.logger.info(` ${key}: ${value}`);
|
|
139
140
|
});
|
|
140
141
|
}
|
|
141
142
|
else if (Array.isArray(data)) {
|
|
142
143
|
data.forEach(item => {
|
|
143
|
-
if (typeof item === 'object') {
|
|
144
|
+
if (typeof item === 'object' && item !== null) {
|
|
144
145
|
this.logger.info(` ${JSON.stringify(item, null, 2)}`);
|
|
145
146
|
}
|
|
146
147
|
else {
|
|
@@ -230,8 +231,8 @@ export class BaseCommandRegistrar {
|
|
|
230
231
|
cron: options.schedule,
|
|
231
232
|
interval: options.interval ? parseInt(options.interval) : undefined,
|
|
232
233
|
},
|
|
233
|
-
|
|
234
|
-
|
|
234
|
+
cwd: options.workingDir,
|
|
235
|
+
env: options.env ? this.parseJSON(options.env, 'environment variables') : {},
|
|
235
236
|
tags: options.tags ? this.parseTags(options.tags) : [],
|
|
236
237
|
priority: options.priority ? parseInt(options.priority) : 5,
|
|
237
238
|
maxRetries: options.maxRetries ? parseInt(options.maxRetries) : 3,
|
|
@@ -288,23 +288,25 @@ export class DaemonClient extends EventEmitter {
|
|
|
288
288
|
// Record job execution in database
|
|
289
289
|
if (this.databasePersistence) {
|
|
290
290
|
try {
|
|
291
|
+
const jobResult = result;
|
|
291
292
|
await this.databasePersistence.saveJob({
|
|
292
293
|
user_id: this.userId,
|
|
293
294
|
session_id: this.sessionId,
|
|
294
295
|
job_id: jobId,
|
|
295
296
|
command: `Triggered execution of ${jobId}`,
|
|
296
|
-
status:
|
|
297
|
+
status: jobResult.success ? 'completed' : 'failed',
|
|
297
298
|
working_directory: process.cwd(),
|
|
298
299
|
started_at: new Date().toISOString(),
|
|
299
300
|
completed_at: new Date().toISOString(),
|
|
300
|
-
exit_code:
|
|
301
|
-
output:
|
|
302
|
-
error:
|
|
301
|
+
exit_code: jobResult.success ? 0 : 1,
|
|
302
|
+
output: jobResult.output,
|
|
303
|
+
error: jobResult.error
|
|
303
304
|
});
|
|
304
305
|
}
|
|
305
306
|
catch (error) {
|
|
306
307
|
// Don't fail the trigger if database save fails
|
|
307
|
-
|
|
308
|
+
const err = error;
|
|
309
|
+
this.logger.warn(`Failed to save job execution to database: ${err.message}`);
|
|
308
310
|
}
|
|
309
311
|
}
|
|
310
312
|
return result;
|
|
@@ -339,7 +341,7 @@ export class DaemonClient extends EventEmitter {
|
|
|
339
341
|
if (Array.isArray(result)) {
|
|
340
342
|
return result;
|
|
341
343
|
}
|
|
342
|
-
else if (result && typeof result === 'object' && Array.isArray(result.jobs)) {
|
|
344
|
+
else if (result && typeof result === 'object' && 'jobs' in result && Array.isArray(result.jobs)) {
|
|
343
345
|
return result.jobs;
|
|
344
346
|
}
|
|
345
347
|
else {
|
|
@@ -464,15 +466,17 @@ export class DaemonClient extends EventEmitter {
|
|
|
464
466
|
calculateJobStatistics(jobs) {
|
|
465
467
|
const total = jobs.length;
|
|
466
468
|
const byStatus = jobs.reduce((acc, job) => {
|
|
467
|
-
|
|
469
|
+
const status = String(job.status);
|
|
470
|
+
acc[status] = (acc[status] || 0) + 1;
|
|
468
471
|
return acc;
|
|
469
472
|
}, {});
|
|
470
|
-
const
|
|
473
|
+
const completedCount = byStatus.completed || 0;
|
|
474
|
+
const successRate = total > 0 ? (completedCount / total) * 100 : 0;
|
|
471
475
|
return {
|
|
472
476
|
totalJobs: total,
|
|
473
477
|
byStatus,
|
|
474
478
|
successRate,
|
|
475
|
-
lastExecution: jobs.length > 0 ? jobs[0].started_at : null,
|
|
479
|
+
lastExecution: jobs.length > 0 ? (jobs[0].started_at || null) : null,
|
|
476
480
|
};
|
|
477
481
|
}
|
|
478
482
|
/**
|
|
@@ -62,7 +62,7 @@ export class DatabasePersistence {
|
|
|
62
62
|
if (this.userId !== undefined) {
|
|
63
63
|
insertData.user_id = this.userId;
|
|
64
64
|
}
|
|
65
|
-
const {
|
|
65
|
+
const { error } = await this.client
|
|
66
66
|
.from('shell_history')
|
|
67
67
|
.insert([insertData]);
|
|
68
68
|
if (error) {
|
|
@@ -120,7 +120,7 @@ export class DatabasePersistence {
|
|
|
120
120
|
if (this.userId !== undefined) {
|
|
121
121
|
insertData.user_id = this.userId;
|
|
122
122
|
}
|
|
123
|
-
const {
|
|
123
|
+
const { error } = await this.client
|
|
124
124
|
.from('shell_jobs')
|
|
125
125
|
.insert([insertData]);
|
|
126
126
|
if (error) {
|
|
@@ -160,7 +160,7 @@ export class DatabasePersistence {
|
|
|
160
160
|
else {
|
|
161
161
|
query = query.is('user_id', null);
|
|
162
162
|
}
|
|
163
|
-
const {
|
|
163
|
+
const { error } = await query;
|
|
164
164
|
if (error) {
|
|
165
165
|
console.error('Failed to update job status:', error);
|
|
166
166
|
return false;
|
|
@@ -215,7 +215,7 @@ export class DatabasePersistence {
|
|
|
215
215
|
if (this.userId !== undefined) {
|
|
216
216
|
upsertData.user_id = this.userId;
|
|
217
217
|
}
|
|
218
|
-
const {
|
|
218
|
+
const { error } = await this.client
|
|
219
219
|
.from('shell_configuration')
|
|
220
220
|
.upsert([upsertData], {
|
|
221
221
|
onConflict: 'user_id,config_key'
|
|
@@ -276,7 +276,7 @@ export class DatabasePersistence {
|
|
|
276
276
|
if (this.userId !== undefined) {
|
|
277
277
|
upsertData.user_id = this.userId;
|
|
278
278
|
}
|
|
279
|
-
const {
|
|
279
|
+
const { error } = await this.client
|
|
280
280
|
.from('shell_aliases')
|
|
281
281
|
.upsert([upsertData], {
|
|
282
282
|
onConflict: 'user_id,alias_name'
|
|
@@ -334,7 +334,7 @@ export class DatabasePersistence {
|
|
|
334
334
|
if (this.userId !== undefined) {
|
|
335
335
|
upsertData.user_id = this.userId;
|
|
336
336
|
}
|
|
337
|
-
const {
|
|
337
|
+
const { error } = await this.client
|
|
338
338
|
.from('shell_functions')
|
|
339
339
|
.upsert([upsertData], {
|
|
340
340
|
onConflict: 'user_id,function_name'
|
|
@@ -397,7 +397,7 @@ export class DatabasePersistence {
|
|
|
397
397
|
if (this.userId !== undefined) {
|
|
398
398
|
insertData.user_id = this.userId;
|
|
399
399
|
}
|
|
400
|
-
const {
|
|
400
|
+
const { error } = await this.client
|
|
401
401
|
.from('shell_sessions')
|
|
402
402
|
.insert([insertData]);
|
|
403
403
|
if (error) {
|
|
@@ -431,7 +431,7 @@ export class DatabasePersistence {
|
|
|
431
431
|
else {
|
|
432
432
|
query = query.is('user_id', null);
|
|
433
433
|
}
|
|
434
|
-
const {
|
|
434
|
+
const { error } = await query;
|
|
435
435
|
if (error) {
|
|
436
436
|
console.error('Failed to end session:', error);
|
|
437
437
|
return false;
|
|
@@ -232,13 +232,10 @@ export function printValidationResults(result, exitOnError = false) {
|
|
|
232
232
|
result.warnings.forEach(warn => console.warn(` - ${warn}`));
|
|
233
233
|
}
|
|
234
234
|
if (result.recommendations.length > 0) {
|
|
235
|
-
// eslint-disable-next-line no-console
|
|
236
235
|
console.log('\nℹ️ Environment Variable Recommendations:');
|
|
237
|
-
// eslint-disable-next-line no-console
|
|
238
236
|
result.recommendations.forEach(rec => console.log(` - ${rec}`));
|
|
239
237
|
}
|
|
240
238
|
if (result.isValid) {
|
|
241
|
-
// eslint-disable-next-line no-console
|
|
242
239
|
console.log('\n✅ Environment validation passed');
|
|
243
240
|
}
|
|
244
241
|
else {
|