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,302 @@
1
+ import { EventEmitter } from 'events';
2
+ import axios from 'axios';
3
+ import { JobStatus } from './job-tracker.js';
4
+ export class MCLIBridge extends EventEmitter {
5
+ client;
6
+ jobTracker;
7
+ config;
8
+ jobMapping = new Map(); // MCLI ID -> Pipeline Job ID
9
+ constructor(config, jobTracker) {
10
+ super();
11
+ this.config = config;
12
+ this.jobTracker = jobTracker;
13
+ this.client = axios.create({
14
+ baseURL: config.baseUrl,
15
+ timeout: config.timeout || 30000,
16
+ headers: {
17
+ 'Content-Type': 'application/json',
18
+ ...(config.apiKey && { 'X-API-Key': config.apiKey })
19
+ }
20
+ });
21
+ this.setupJobTrackerListeners();
22
+ }
23
+ setupJobTrackerListeners() {
24
+ // Listen for job created events
25
+ this.jobTracker.on('job:created', async (event) => {
26
+ const job = event.data;
27
+ if (job.targetSystem === 'mcli') {
28
+ try {
29
+ await this.submitJobToMCLI(job);
30
+ }
31
+ catch (error) {
32
+ console.error(`Failed to submit job ${job.id} to MCLI:`, error);
33
+ await this.jobTracker.updateJobStatus(job.id, JobStatus.FAILED, `Failed to submit to MCLI: ${error instanceof Error ? error.message : 'Unknown error'}`);
34
+ }
35
+ }
36
+ });
37
+ // Listen for job retry events
38
+ this.jobTracker.on('job:retry', async (event) => {
39
+ const job = await this.jobTracker.getJob(event.jobId);
40
+ if (job && job.targetSystem === 'mcli') {
41
+ try {
42
+ await this.submitJobToMCLI(job);
43
+ }
44
+ catch (error) {
45
+ console.error(`Failed to retry job ${job.id} to MCLI:`, error);
46
+ }
47
+ }
48
+ });
49
+ }
50
+ async submitJobToMCLI(job) {
51
+ const request = {
52
+ name: job.name,
53
+ type: job.type,
54
+ config: job.config,
55
+ parameters: job.parameters,
56
+ callback_url: this.config.webhookUrl ? `${this.config.webhookUrl}/webhook/mcli` : undefined,
57
+ metadata: {
58
+ pipeline_job_id: job.id,
59
+ source_system: job.sourceSystem,
60
+ owner: job.owner,
61
+ team: job.team,
62
+ tags: job.tags
63
+ }
64
+ };
65
+ try {
66
+ // Submit to MCLI
67
+ const response = await this.client.post('/api/jobs/submit', request);
68
+ // Store mapping
69
+ this.jobMapping.set(response.data.job_id, job.id);
70
+ // Update job with external ID
71
+ await this.updateJobExternalId(job.id, response.data.job_id);
72
+ // Update status to queued
73
+ await this.jobTracker.updateJobStatus(job.id, JobStatus.QUEUED);
74
+ // Get current execution
75
+ const executions = await this.getLatestExecution(job.id);
76
+ if (executions) {
77
+ await this.jobTracker.startExecution(executions.id, 'mcli', response.data.job_id);
78
+ }
79
+ // Emit submission event
80
+ this.emit('mcli:submitted', {
81
+ pipelineJobId: job.id,
82
+ mcliJobId: response.data.job_id,
83
+ timestamp: new Date()
84
+ });
85
+ return response.data;
86
+ }
87
+ catch (error) {
88
+ if (axios.isAxiosError(error)) {
89
+ const errorMessage = error.response?.data?.error || error.message;
90
+ throw new Error(`MCLI submission failed: ${errorMessage}`);
91
+ }
92
+ throw error;
93
+ }
94
+ }
95
+ async getJobStatus(mcliJobId) {
96
+ try {
97
+ const response = await this.client.get(`/api/jobs/${mcliJobId}`);
98
+ return response.data;
99
+ }
100
+ catch (error) {
101
+ if (axios.isAxiosError(error)) {
102
+ throw new Error(`Failed to get MCLI job status: ${error.response?.data?.error || error.message}`);
103
+ }
104
+ throw error;
105
+ }
106
+ }
107
+ async cancelJob(mcliJobId) {
108
+ try {
109
+ await this.client.post(`/api/jobs/${mcliJobId}/cancel`);
110
+ // Update pipeline job status
111
+ const pipelineJobId = this.jobMapping.get(mcliJobId);
112
+ if (pipelineJobId) {
113
+ await this.jobTracker.updateJobStatus(pipelineJobId, JobStatus.CANCELLED);
114
+ }
115
+ this.emit('mcli:cancelled', {
116
+ mcliJobId,
117
+ pipelineJobId,
118
+ timestamp: new Date()
119
+ });
120
+ }
121
+ catch (error) {
122
+ if (axios.isAxiosError(error)) {
123
+ throw new Error(`Failed to cancel MCLI job: ${error.response?.data?.error || error.message}`);
124
+ }
125
+ throw error;
126
+ }
127
+ }
128
+ async getJobLogs(mcliJobId) {
129
+ try {
130
+ const response = await this.client.get(`/api/jobs/${mcliJobId}/logs`);
131
+ return response.data.logs;
132
+ }
133
+ catch (error) {
134
+ if (axios.isAxiosError(error)) {
135
+ throw new Error(`Failed to get MCLI job logs: ${error.response?.data?.error || error.message}`);
136
+ }
137
+ throw error;
138
+ }
139
+ }
140
+ // Webhook handler for MCLI callbacks
141
+ async handleWebhook(payload) {
142
+ const { job_id, status, result, error, metrics, artifacts } = payload;
143
+ // Get pipeline job ID
144
+ let pipelineJobId = this.jobMapping.get(job_id);
145
+ if (!pipelineJobId && payload.metadata?.pipeline_job_id) {
146
+ pipelineJobId = payload.metadata.pipeline_job_id;
147
+ if (pipelineJobId) {
148
+ this.jobMapping.set(job_id, pipelineJobId);
149
+ }
150
+ }
151
+ if (!pipelineJobId) {
152
+ console.warn(`Received webhook for unknown MCLI job: ${job_id}`);
153
+ return;
154
+ }
155
+ // Get latest execution
156
+ const execution = await this.getLatestExecution(pipelineJobId);
157
+ if (!execution) {
158
+ console.warn(`No execution found for pipeline job: ${pipelineJobId}`);
159
+ return;
160
+ }
161
+ // Update based on status
162
+ switch (status) {
163
+ case 'running':
164
+ await this.jobTracker.updateJobStatus(pipelineJobId, JobStatus.RUNNING);
165
+ break;
166
+ case 'completed':
167
+ case 'success':
168
+ await this.jobTracker.completeExecution(execution.id, result, metrics, artifacts);
169
+ break;
170
+ case 'failed':
171
+ case 'error':
172
+ await this.jobTracker.failExecution(execution.id, error?.message || 'Job failed in MCLI', error);
173
+ break;
174
+ case 'cancelled':
175
+ await this.jobTracker.updateJobStatus(pipelineJobId, JobStatus.CANCELLED);
176
+ break;
177
+ default:
178
+ console.warn(`Unknown MCLI job status: ${status}`);
179
+ }
180
+ // Record event
181
+ await this.jobTracker.recordEvent('mcli_webhook', 'mcli', payload, pipelineJobId, execution.id);
182
+ // Emit webhook event
183
+ this.emit('mcli:webhook', {
184
+ mcliJobId: job_id,
185
+ pipelineJobId,
186
+ status,
187
+ timestamp: new Date()
188
+ });
189
+ }
190
+ // Sync job status from MCLI
191
+ async syncJobStatus(mcliJobId) {
192
+ try {
193
+ const mcliJob = await this.getJobStatus(mcliJobId);
194
+ const pipelineJobId = this.jobMapping.get(mcliJobId);
195
+ if (!pipelineJobId) {
196
+ console.warn(`No pipeline job found for MCLI job: ${mcliJobId}`);
197
+ return;
198
+ }
199
+ // Map MCLI status to pipeline status
200
+ const statusMap = {
201
+ 'pending': JobStatus.PENDING,
202
+ 'queued': JobStatus.QUEUED,
203
+ 'running': JobStatus.RUNNING,
204
+ 'completed': JobStatus.COMPLETED,
205
+ 'success': JobStatus.COMPLETED,
206
+ 'failed': JobStatus.FAILED,
207
+ 'error': JobStatus.FAILED,
208
+ 'cancelled': JobStatus.CANCELLED
209
+ };
210
+ const pipelineStatus = statusMap[mcliJob.status] || JobStatus.PENDING;
211
+ await this.jobTracker.updateJobStatus(pipelineJobId, pipelineStatus);
212
+ // If completed or failed, update execution
213
+ if (mcliJob.status === 'completed' || mcliJob.status === 'success') {
214
+ const execution = await this.getLatestExecution(pipelineJobId);
215
+ if (execution) {
216
+ await this.jobTracker.completeExecution(execution.id, mcliJob.result, undefined, undefined);
217
+ }
218
+ }
219
+ else if (mcliJob.status === 'failed' || mcliJob.status === 'error') {
220
+ const execution = await this.getLatestExecution(pipelineJobId);
221
+ if (execution) {
222
+ await this.jobTracker.failExecution(execution.id, mcliJob.error || 'Job failed in MCLI', undefined);
223
+ }
224
+ }
225
+ this.emit('mcli:synced', {
226
+ mcliJobId,
227
+ pipelineJobId,
228
+ status: pipelineStatus,
229
+ timestamp: new Date()
230
+ });
231
+ }
232
+ catch (error) {
233
+ console.error(`Failed to sync MCLI job status for ${mcliJobId}:`, error);
234
+ }
235
+ }
236
+ // Batch sync multiple jobs
237
+ async syncAllActiveJobs() {
238
+ const activeJobs = await this.jobTracker.getActiveJobs();
239
+ for (const job of activeJobs) {
240
+ if (job.targetSystem === 'mcli' && job.externalId) {
241
+ await this.syncJobStatus(job.externalId);
242
+ // Add delay to avoid overwhelming MCLI API
243
+ await new Promise(resolve => setTimeout(resolve, 100));
244
+ }
245
+ }
246
+ }
247
+ // Start periodic sync
248
+ startPeriodicSync(intervalMs = 30000) {
249
+ return setInterval(async () => {
250
+ try {
251
+ await this.syncAllActiveJobs();
252
+ }
253
+ catch (error) {
254
+ console.error('Periodic sync error:', error);
255
+ }
256
+ }, intervalMs);
257
+ }
258
+ // Helper methods
259
+ async updateJobExternalId(jobId, externalId) {
260
+ // This would be implemented in JobTracker, but for now we'll use raw SQL
261
+ const pool = this.jobTracker.pool;
262
+ await pool.query('UPDATE pipeline_jobs SET external_id = $1 WHERE id = $2', [externalId, jobId]);
263
+ }
264
+ async getLatestExecution(jobId) {
265
+ // This would be implemented in JobTracker
266
+ const pool = this.jobTracker.pool;
267
+ const result = await pool.query(`SELECT * FROM job_executions
268
+ WHERE job_id = $1
269
+ ORDER BY execution_number DESC
270
+ LIMIT 1`, [jobId]);
271
+ if (result.rows.length === 0) {
272
+ return null;
273
+ }
274
+ return this.jobTracker.parseExecutionRow(result.rows[0]);
275
+ }
276
+ // Health check
277
+ async healthCheck() {
278
+ try {
279
+ const response = await this.client.get('/health');
280
+ return response.status === 200;
281
+ }
282
+ catch (_error) {
283
+ return false;
284
+ }
285
+ }
286
+ // Get MCLI statistics
287
+ async getStatistics() {
288
+ try {
289
+ const response = await this.client.get('/api/statistics');
290
+ return response.data;
291
+ }
292
+ catch (error) {
293
+ console.error('Failed to get MCLI statistics:', error);
294
+ return null;
295
+ }
296
+ }
297
+ // Cleanup
298
+ cleanup() {
299
+ this.removeAllListeners();
300
+ this.jobMapping.clear();
301
+ }
302
+ }