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,193 @@
1
+ /**
2
+ * LSH Secrets Manager
3
+ * Sync .env files across machines using encrypted Supabase storage
4
+ */
5
+ import * as fs from 'fs';
6
+ import * as crypto from 'crypto';
7
+ import DatabasePersistence from './database-persistence.js';
8
+ import { createLogger } from './logger.js';
9
+ const logger = createLogger('SecretsManager');
10
+ export class SecretsManager {
11
+ persistence;
12
+ encryptionKey;
13
+ constructor(userId, encryptionKey) {
14
+ this.persistence = new DatabasePersistence(userId);
15
+ // Use provided key or generate from machine ID + user
16
+ this.encryptionKey = encryptionKey || this.getDefaultEncryptionKey();
17
+ }
18
+ /**
19
+ * Get default encryption key from environment or machine
20
+ */
21
+ getDefaultEncryptionKey() {
22
+ // Check for explicit key
23
+ if (process.env.LSH_SECRETS_KEY) {
24
+ return process.env.LSH_SECRETS_KEY;
25
+ }
26
+ // Generate from machine ID and user
27
+ const machineId = process.env.HOSTNAME || 'localhost';
28
+ const user = process.env.USER || 'unknown';
29
+ const seed = `${machineId}-${user}-lsh-secrets`;
30
+ // Create deterministic key
31
+ return crypto.createHash('sha256').update(seed).digest('hex');
32
+ }
33
+ /**
34
+ * Encrypt a value
35
+ */
36
+ encrypt(text) {
37
+ const iv = crypto.randomBytes(16);
38
+ const key = Buffer.from(this.encryptionKey, 'hex');
39
+ const cipher = crypto.createCipheriv('aes-256-cbc', key.slice(0, 32), iv);
40
+ let encrypted = cipher.update(text, 'utf8', 'hex');
41
+ encrypted += cipher.final('hex');
42
+ return iv.toString('hex') + ':' + encrypted;
43
+ }
44
+ /**
45
+ * Decrypt a value
46
+ */
47
+ decrypt(text) {
48
+ const parts = text.split(':');
49
+ const iv = Buffer.from(parts[0], 'hex');
50
+ const encryptedText = parts[1];
51
+ const key = Buffer.from(this.encryptionKey, 'hex');
52
+ const decipher = crypto.createDecipheriv('aes-256-cbc', key.slice(0, 32), iv);
53
+ let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
54
+ decrypted += decipher.final('utf8');
55
+ return decrypted;
56
+ }
57
+ /**
58
+ * Parse .env file into key-value pairs
59
+ */
60
+ parseEnvFile(content) {
61
+ const env = {};
62
+ const lines = content.split('\n');
63
+ for (const line of lines) {
64
+ // Skip comments and empty lines
65
+ if (line.trim().startsWith('#') || !line.trim()) {
66
+ continue;
67
+ }
68
+ // Parse KEY=VALUE
69
+ const match = line.match(/^([^=]+)=(.*)$/);
70
+ if (match) {
71
+ const key = match[1].trim();
72
+ let value = match[2].trim();
73
+ // Remove quotes if present
74
+ if ((value.startsWith('"') && value.endsWith('"')) ||
75
+ (value.startsWith("'") && value.endsWith("'"))) {
76
+ value = value.slice(1, -1);
77
+ }
78
+ env[key] = value;
79
+ }
80
+ }
81
+ return env;
82
+ }
83
+ /**
84
+ * Format env vars as .env file content
85
+ */
86
+ formatEnvFile(vars) {
87
+ return Object.entries(vars)
88
+ .map(([key, value]) => {
89
+ // Quote values with spaces or special characters
90
+ const needsQuotes = /[\s#]/.test(value);
91
+ const quotedValue = needsQuotes ? `"${value}"` : value;
92
+ return `${key}=${quotedValue}`;
93
+ })
94
+ .join('\n') + '\n';
95
+ }
96
+ /**
97
+ * Push local .env to Supabase
98
+ */
99
+ async push(envFilePath = '.env', environment = 'dev') {
100
+ if (!fs.existsSync(envFilePath)) {
101
+ throw new Error(`File not found: ${envFilePath}`);
102
+ }
103
+ logger.info(`Pushing ${envFilePath} to Supabase (${environment})...`);
104
+ const content = fs.readFileSync(envFilePath, 'utf8');
105
+ const env = this.parseEnvFile(content);
106
+ // Encrypt entire .env content
107
+ const encrypted = this.encrypt(content);
108
+ // Store in Supabase (using job system for now)
109
+ const secretData = {
110
+ job_id: `secrets_${environment}_${Date.now()}`,
111
+ command: 'secrets_sync',
112
+ status: 'completed',
113
+ output: encrypted,
114
+ started_at: new Date().toISOString(),
115
+ completed_at: new Date().toISOString(),
116
+ working_directory: process.cwd(),
117
+ };
118
+ await this.persistence.saveJob(secretData);
119
+ logger.info(`āœ… Pushed ${Object.keys(env).length} secrets to Supabase`);
120
+ }
121
+ /**
122
+ * Pull .env from Supabase
123
+ */
124
+ async pull(envFilePath = '.env', environment = 'dev') {
125
+ logger.info(`Pulling ${environment} secrets from Supabase...`);
126
+ // Get latest secrets
127
+ const jobs = await this.persistence.getActiveJobs();
128
+ const secretsJobs = jobs
129
+ .filter(j => j.command === 'secrets_sync' && j.job_id.includes(environment))
130
+ .sort((a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime());
131
+ if (secretsJobs.length === 0) {
132
+ throw new Error(`No secrets found for environment: ${environment}`);
133
+ }
134
+ const latestSecret = secretsJobs[0];
135
+ if (!latestSecret.output) {
136
+ throw new Error(`No encrypted data found for environment: ${environment}`);
137
+ }
138
+ const decrypted = this.decrypt(latestSecret.output);
139
+ // Backup existing .env if it exists
140
+ if (fs.existsSync(envFilePath)) {
141
+ const backup = `${envFilePath}.backup.${Date.now()}`;
142
+ fs.copyFileSync(envFilePath, backup);
143
+ logger.info(`Backed up existing .env to ${backup}`);
144
+ }
145
+ // Write new .env
146
+ fs.writeFileSync(envFilePath, decrypted, 'utf8');
147
+ const env = this.parseEnvFile(decrypted);
148
+ logger.info(`āœ… Pulled ${Object.keys(env).length} secrets from Supabase`);
149
+ }
150
+ /**
151
+ * List all stored environments
152
+ */
153
+ async listEnvironments() {
154
+ const jobs = await this.persistence.getActiveJobs();
155
+ const secretsJobs = jobs.filter(j => j.command === 'secrets_sync');
156
+ const envs = new Set();
157
+ for (const job of secretsJobs) {
158
+ const match = job.job_id.match(/secrets_(.+?)_\d+/);
159
+ if (match) {
160
+ envs.add(match[1]);
161
+ }
162
+ }
163
+ return Array.from(envs).sort();
164
+ }
165
+ /**
166
+ * Show secrets (masked)
167
+ */
168
+ async show(environment = 'dev') {
169
+ const jobs = await this.persistence.getActiveJobs();
170
+ const secretsJobs = jobs
171
+ .filter(j => j.command === 'secrets_sync' && j.job_id.includes(environment))
172
+ .sort((a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime());
173
+ if (secretsJobs.length === 0) {
174
+ console.log(`No secrets found for environment: ${environment}`);
175
+ return;
176
+ }
177
+ const latestSecret = secretsJobs[0];
178
+ if (!latestSecret.output) {
179
+ throw new Error(`No encrypted data found for environment: ${environment}`);
180
+ }
181
+ const decrypted = this.decrypt(latestSecret.output);
182
+ const env = this.parseEnvFile(decrypted);
183
+ console.log(`\nšŸ“¦ Secrets for ${environment} (${Object.keys(env).length} total):\n`);
184
+ for (const [key, value] of Object.entries(env)) {
185
+ const masked = value.length > 4
186
+ ? value.substring(0, 4) + '*'.repeat(Math.min(value.length - 4, 20))
187
+ : '****';
188
+ console.log(` ${key}=${masked}`);
189
+ }
190
+ console.log();
191
+ }
192
+ }
193
+ export default SecretsManager;