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,344 @@
1
+ /**
2
+ * Tab Completion System Implementation
3
+ * Provides ZSH-compatible completion functionality
4
+ */
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ export class CompletionSystem {
8
+ completionFunctions = new Map();
9
+ defaultCompletions = [];
10
+ isEnabled = true;
11
+ constructor() {
12
+ this.setupDefaultCompletions();
13
+ }
14
+ /**
15
+ * Register a completion function for a specific command
16
+ */
17
+ registerCompletion(command, func) {
18
+ this.completionFunctions.set(command, func);
19
+ }
20
+ /**
21
+ * Register a default completion function
22
+ */
23
+ registerDefaultCompletion(func) {
24
+ this.defaultCompletions.push(func);
25
+ }
26
+ /**
27
+ * Get completions for the current context
28
+ */
29
+ async getCompletions(context) {
30
+ if (!this.isEnabled)
31
+ return [];
32
+ const candidates = [];
33
+ // Try command-specific completion first
34
+ const commandFunc = this.completionFunctions.get(context.command);
35
+ if (commandFunc) {
36
+ try {
37
+ const commandCompletions = await commandFunc(context);
38
+ candidates.push(...commandCompletions);
39
+ }
40
+ catch (_error) {
41
+ // Continue with default completions if command-specific fails
42
+ }
43
+ }
44
+ // If no command-specific completions, try default completions
45
+ if (candidates.length === 0) {
46
+ for (const defaultFunc of this.defaultCompletions) {
47
+ try {
48
+ const defaultCompletions = await defaultFunc(context);
49
+ candidates.push(...defaultCompletions);
50
+ }
51
+ catch (_error) {
52
+ // Continue with other default completions
53
+ }
54
+ }
55
+ }
56
+ // Filter and sort candidates
57
+ return this.filterAndSortCandidates(candidates, context.currentWord);
58
+ }
59
+ /**
60
+ * Enable/disable completion
61
+ */
62
+ setEnabled(enabled) {
63
+ this.isEnabled = enabled;
64
+ }
65
+ /**
66
+ * Setup default completion functions
67
+ */
68
+ setupDefaultCompletions() {
69
+ // File and directory completion
70
+ this.registerDefaultCompletion(async (context) => {
71
+ return this.completeFilesAndDirectories(context);
72
+ });
73
+ // Command completion
74
+ this.registerDefaultCompletion(async (context) => {
75
+ if (context.wordIndex === 0) {
76
+ return this.completeCommands(context);
77
+ }
78
+ return [];
79
+ });
80
+ // Variable completion
81
+ this.registerDefaultCompletion(async (context) => {
82
+ if (context.currentWord.startsWith('$')) {
83
+ return this.completeVariables(context);
84
+ }
85
+ return [];
86
+ });
87
+ // Built-in command completions
88
+ this.setupBuiltinCompletions();
89
+ }
90
+ /**
91
+ * Complete files and directories
92
+ */
93
+ async completeFilesAndDirectories(context) {
94
+ const candidates = [];
95
+ const currentWord = context.currentWord;
96
+ // Determine search directory
97
+ let searchDir = context.cwd;
98
+ let pattern = currentWord;
99
+ if (currentWord.includes('/')) {
100
+ const lastSlash = currentWord.lastIndexOf('/');
101
+ searchDir = path.resolve(context.cwd, currentWord.substring(0, lastSlash + 1));
102
+ pattern = currentWord.substring(lastSlash + 1);
103
+ }
104
+ try {
105
+ const entries = await fs.promises.readdir(searchDir, { withFileTypes: true });
106
+ for (const entry of entries) {
107
+ // Skip hidden files unless explicitly requested
108
+ if (!pattern.startsWith('.') && entry.name.startsWith('.')) {
109
+ continue;
110
+ }
111
+ // Check if entry matches pattern
112
+ if (this.matchesPattern(entry.name, pattern)) {
113
+ const fullPath = path.join(searchDir, entry.name);
114
+ const relativePath = path.relative(context.cwd, fullPath);
115
+ candidates.push({
116
+ word: entry.isDirectory() ? relativePath + '/' : relativePath,
117
+ type: entry.isDirectory() ? 'directory' : 'file',
118
+ description: entry.isDirectory() ? 'Directory' : 'File',
119
+ });
120
+ }
121
+ }
122
+ }
123
+ catch (_error) {
124
+ // Directory doesn't exist or not readable
125
+ }
126
+ return candidates;
127
+ }
128
+ /**
129
+ * Complete commands from PATH
130
+ */
131
+ async completeCommands(context) {
132
+ const candidates = [];
133
+ const pattern = context.currentWord;
134
+ const pathDirs = (context.env.PATH || '').split(':').filter(dir => dir);
135
+ // Add built-in commands
136
+ const builtins = [
137
+ 'cd', 'pwd', 'echo', 'printf', 'test', '[', 'export', 'unset', 'set',
138
+ 'eval', 'exec', 'return', 'shift', 'local', 'jobs', 'fg', 'bg', 'wait',
139
+ 'read', 'getopts', 'trap', 'true', 'false', 'exit'
140
+ ];
141
+ for (const builtin of builtins) {
142
+ if (this.matchesPattern(builtin, pattern)) {
143
+ candidates.push({
144
+ word: builtin,
145
+ type: 'command',
146
+ description: 'Built-in command',
147
+ });
148
+ }
149
+ }
150
+ // Search PATH for executables
151
+ for (const dir of pathDirs) {
152
+ try {
153
+ const entries = await fs.promises.readdir(dir, { withFileTypes: true });
154
+ for (const entry of entries) {
155
+ if (entry.isFile() && this.isExecutable(path.join(dir, entry.name))) {
156
+ if (this.matchesPattern(entry.name, pattern)) {
157
+ candidates.push({
158
+ word: entry.name,
159
+ type: 'command',
160
+ description: `Command in ${dir}`,
161
+ });
162
+ }
163
+ }
164
+ }
165
+ }
166
+ catch (_error) {
167
+ // Directory doesn't exist or not readable
168
+ }
169
+ }
170
+ return candidates;
171
+ }
172
+ /**
173
+ * Complete variables
174
+ */
175
+ async completeVariables(context) {
176
+ const candidates = [];
177
+ const pattern = context.currentWord.substring(1); // Remove $
178
+ // Complete environment variables
179
+ for (const [name, value] of Object.entries(context.env)) {
180
+ if (this.matchesPattern(name, pattern)) {
181
+ candidates.push({
182
+ word: `$${name}`,
183
+ type: 'variable',
184
+ description: `Environment variable: ${value}`,
185
+ });
186
+ }
187
+ }
188
+ return candidates;
189
+ }
190
+ /**
191
+ * Setup built-in command completions
192
+ */
193
+ setupBuiltinCompletions() {
194
+ // cd completion
195
+ this.registerCompletion('cd', async (context) => {
196
+ return this.completeDirectories(context);
197
+ });
198
+ // export completion
199
+ this.registerCompletion('export', async (context) => {
200
+ if (context.wordIndex === 1) {
201
+ return this.completeVariables(context);
202
+ }
203
+ return [];
204
+ });
205
+ // unset completion
206
+ this.registerCompletion('unset', async (context) => {
207
+ if (context.wordIndex === 1) {
208
+ return this.completeVariables(context);
209
+ }
210
+ return [];
211
+ });
212
+ // test completion
213
+ this.registerCompletion('test', async (context) => {
214
+ return this.completeTestOptions(context);
215
+ });
216
+ // Job management completions
217
+ this.registerCompletion('job-start', async (context) => {
218
+ return this.completeJobIds(context);
219
+ });
220
+ this.registerCompletion('job-stop', async (context) => {
221
+ return this.completeJobIds(context);
222
+ });
223
+ this.registerCompletion('job-show', async (context) => {
224
+ return this.completeJobIds(context);
225
+ });
226
+ }
227
+ /**
228
+ * Complete directories only
229
+ */
230
+ async completeDirectories(context) {
231
+ const candidates = [];
232
+ const currentWord = context.currentWord;
233
+ let searchDir = context.cwd;
234
+ let pattern = currentWord;
235
+ if (currentWord.includes('/')) {
236
+ const lastSlash = currentWord.lastIndexOf('/');
237
+ searchDir = path.resolve(context.cwd, currentWord.substring(0, lastSlash + 1));
238
+ pattern = currentWord.substring(lastSlash + 1);
239
+ }
240
+ try {
241
+ const entries = await fs.promises.readdir(searchDir, { withFileTypes: true });
242
+ for (const entry of entries) {
243
+ if (entry.isDirectory() && this.matchesPattern(entry.name, pattern)) {
244
+ const fullPath = path.join(searchDir, entry.name);
245
+ const relativePath = path.relative(context.cwd, fullPath);
246
+ candidates.push({
247
+ word: relativePath + '/',
248
+ type: 'directory',
249
+ description: 'Directory',
250
+ });
251
+ }
252
+ }
253
+ }
254
+ catch (_error) {
255
+ // Directory doesn't exist or not readable
256
+ }
257
+ return candidates;
258
+ }
259
+ /**
260
+ * Complete test command options
261
+ */
262
+ async completeTestOptions(context) {
263
+ const testOptions = [
264
+ { word: '-f', description: 'File exists and is regular file' },
265
+ { word: '-d', description: 'File exists and is directory' },
266
+ { word: '-e', description: 'File exists' },
267
+ { word: '-r', description: 'File exists and is readable' },
268
+ { word: '-w', description: 'File exists and is writable' },
269
+ { word: '-x', description: 'File exists and is executable' },
270
+ { word: '-s', description: 'File exists and has size > 0' },
271
+ { word: '-z', description: 'String is empty' },
272
+ { word: '-n', description: 'String is not empty' },
273
+ { word: '=', description: 'Strings are equal' },
274
+ { word: '!=', description: 'Strings are not equal' },
275
+ { word: '-eq', description: 'Numbers are equal' },
276
+ { word: '-ne', description: 'Numbers are not equal' },
277
+ { word: '-lt', description: 'Number is less than' },
278
+ { word: '-le', description: 'Number is less than or equal' },
279
+ { word: '-gt', description: 'Number is greater than' },
280
+ { word: '-ge', description: 'Number is greater than or equal' },
281
+ ];
282
+ return testOptions.filter(opt => this.matchesPattern(opt.word, context.currentWord)).map(opt => ({
283
+ word: opt.word,
284
+ type: 'option',
285
+ description: opt.description,
286
+ }));
287
+ }
288
+ /**
289
+ * Complete job IDs (placeholder - would integrate with job manager)
290
+ */
291
+ async completeJobIds(_context) {
292
+ // This would integrate with the job manager to get actual job IDs
293
+ return [
294
+ { word: '1', description: 'Job ID 1' },
295
+ { word: '2', description: 'Job ID 2' },
296
+ { word: '3', description: 'Job ID 3' },
297
+ ];
298
+ }
299
+ /**
300
+ * Check if a pattern matches a string
301
+ */
302
+ matchesPattern(str, pattern) {
303
+ if (!pattern)
304
+ return true;
305
+ // Simple case-insensitive prefix matching
306
+ return str.toLowerCase().startsWith(pattern.toLowerCase());
307
+ }
308
+ /**
309
+ * Check if a file is executable
310
+ */
311
+ isExecutable(filePath) {
312
+ try {
313
+ fs.accessSync(filePath, fs.constants.X_OK);
314
+ return true;
315
+ }
316
+ catch {
317
+ return false;
318
+ }
319
+ }
320
+ /**
321
+ * Filter and sort completion candidates
322
+ */
323
+ filterAndSortCandidates(candidates, _currentWord) {
324
+ // Remove duplicates
325
+ const unique = new Map();
326
+ for (const candidate of candidates) {
327
+ if (!unique.has(candidate.word)) {
328
+ unique.set(candidate.word, candidate);
329
+ }
330
+ }
331
+ // Sort by type priority and alphabetically
332
+ const sorted = Array.from(unique.values()).sort((a, b) => {
333
+ const typeOrder = { directory: 0, file: 1, command: 2, variable: 3, function: 4, option: 5 };
334
+ const aOrder = typeOrder[a.type || 'file'];
335
+ const bOrder = typeOrder[b.type || 'file'];
336
+ if (aOrder !== bOrder) {
337
+ return aOrder - bOrder;
338
+ }
339
+ return a.word.localeCompare(b.word);
340
+ });
341
+ return sorted;
342
+ }
343
+ }
344
+ export default CompletionSystem;