lsh-framework 1.2.0 → 1.3.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 +40 -3
- package/dist/cli.js +104 -486
- package/dist/commands/doctor.js +427 -0
- package/dist/commands/init.js +371 -0
- package/dist/constants/api.js +94 -0
- package/dist/constants/commands.js +64 -0
- package/dist/constants/config.js +56 -0
- package/dist/constants/database.js +21 -0
- package/dist/constants/errors.js +79 -0
- package/dist/constants/index.js +28 -0
- package/dist/constants/paths.js +28 -0
- package/dist/constants/ui.js +73 -0
- package/dist/constants/validation.js +124 -0
- package/dist/daemon/lshd.js +11 -32
- package/dist/lib/daemon-client-helper.js +7 -4
- package/dist/lib/daemon-client.js +9 -2
- package/dist/lib/format-utils.js +163 -0
- package/dist/lib/fuzzy-match.js +123 -0
- package/dist/lib/job-manager.js +2 -1
- package/dist/lib/platform-utils.js +211 -0
- package/dist/lib/secrets-manager.js +11 -1
- package/dist/lib/string-utils.js +128 -0
- package/dist/services/daemon/daemon-registrar.js +3 -2
- package/dist/services/secrets/secrets.js +119 -59
- package/package.json +10 -74
- package/dist/app.js +0 -33
- package/dist/cicd/analytics.js +0 -261
- package/dist/cicd/auth.js +0 -269
- package/dist/cicd/cache-manager.js +0 -172
- package/dist/cicd/data-retention.js +0 -305
- package/dist/cicd/performance-monitor.js +0 -224
- package/dist/cicd/webhook-receiver.js +0 -640
- package/dist/commands/api.js +0 -346
- package/dist/commands/theme.js +0 -261
- package/dist/commands/zsh-import.js +0 -240
- package/dist/components/App.js +0 -1
- package/dist/components/Divider.js +0 -29
- package/dist/components/REPL.js +0 -43
- package/dist/components/Terminal.js +0 -232
- package/dist/components/UserInput.js +0 -30
- package/dist/daemon/api-server.js +0 -316
- package/dist/daemon/monitoring-api.js +0 -220
- package/dist/lib/api-error-handler.js +0 -185
- package/dist/lib/associative-arrays.js +0 -285
- package/dist/lib/base-api-server.js +0 -290
- package/dist/lib/brace-expansion.js +0 -160
- package/dist/lib/builtin-commands.js +0 -439
- package/dist/lib/executors/builtin-executor.js +0 -52
- package/dist/lib/extended-globbing.js +0 -411
- package/dist/lib/extended-parameter-expansion.js +0 -227
- package/dist/lib/interactive-shell.js +0 -460
- package/dist/lib/job-builtins.js +0 -582
- package/dist/lib/pathname-expansion.js +0 -216
- package/dist/lib/script-runner.js +0 -226
- package/dist/lib/shell-executor.js +0 -2504
- package/dist/lib/shell-parser.js +0 -958
- package/dist/lib/shell-types.js +0 -6
- package/dist/lib/shell.lib.js +0 -40
- package/dist/lib/theme-manager.js +0 -476
- package/dist/lib/variable-expansion.js +0 -385
- package/dist/lib/zsh-compatibility.js +0 -659
- package/dist/lib/zsh-import-manager.js +0 -707
- package/dist/lib/zsh-options.js +0 -328
- package/dist/pipeline/job-tracker.js +0 -491
- package/dist/pipeline/mcli-bridge.js +0 -309
- package/dist/pipeline/pipeline-service.js +0 -1119
- package/dist/pipeline/workflow-engine.js +0 -870
- package/dist/services/api/api.js +0 -58
- package/dist/services/api/auth.js +0 -35
- package/dist/services/api/config.js +0 -7
- package/dist/services/api/file.js +0 -22
- package/dist/services/shell/shell.js +0 -28
- package/dist/services/zapier.js +0 -16
- package/dist/simple-api-server.js +0 -148
package/dist/commands/api.js
DELETED
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* API Server Commands for LSH
|
|
3
|
-
*/
|
|
4
|
-
import chalk from 'chalk';
|
|
5
|
-
import crypto from 'crypto';
|
|
6
|
-
import DaemonClient from '../lib/daemon-client.js';
|
|
7
|
-
export function registerApiCommands(program) {
|
|
8
|
-
const api = program
|
|
9
|
-
.command('api')
|
|
10
|
-
.description('API server management and configuration');
|
|
11
|
-
// Start daemon with API server enabled
|
|
12
|
-
api
|
|
13
|
-
.command('start')
|
|
14
|
-
.description('Start daemon with API server enabled')
|
|
15
|
-
.option('-p, --port <port>', 'API port', '3030')
|
|
16
|
-
.option('-k, --api-key <key>', 'API key (generated if not provided)')
|
|
17
|
-
.option('--webhooks', 'Enable webhook support', false)
|
|
18
|
-
.action(async (options) => {
|
|
19
|
-
try {
|
|
20
|
-
const apiKey = options.apiKey || crypto.randomBytes(32).toString('hex');
|
|
21
|
-
const daemonClient = new DaemonClient();
|
|
22
|
-
// Check if daemon is already running
|
|
23
|
-
if (daemonClient.isDaemonRunning()) {
|
|
24
|
-
console.log(chalk.yellow('⚠️ Daemon is already running. Restarting with API enabled...'));
|
|
25
|
-
// Stop existing daemon
|
|
26
|
-
await daemonClient.connect();
|
|
27
|
-
await daemonClient.stopDaemon();
|
|
28
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
29
|
-
}
|
|
30
|
-
// Write API configuration to environment or a config file
|
|
31
|
-
process.env.LSH_API_ENABLED = 'true';
|
|
32
|
-
process.env.LSH_API_PORT = options.port;
|
|
33
|
-
process.env.LSH_API_KEY = apiKey;
|
|
34
|
-
process.env.LSH_ENABLE_WEBHOOKS = options.webhooks ? 'true' : 'false';
|
|
35
|
-
// Restart the daemon which will pick up the environment variables
|
|
36
|
-
await daemonClient.restartDaemon();
|
|
37
|
-
console.log(chalk.green('✅ Daemon started with API server'));
|
|
38
|
-
console.log(chalk.blue(`\n📡 API Server: http://localhost:${options.port}`));
|
|
39
|
-
console.log(chalk.yellow(`🔑 API Key: ${apiKey}`));
|
|
40
|
-
console.log(chalk.gray('\nStore this API key securely. You will need it to authenticate API requests.'));
|
|
41
|
-
console.log(chalk.gray(`\nExample usage:`));
|
|
42
|
-
console.log(chalk.gray(` curl -H "X-API-Key: ${apiKey}" http://localhost:${options.port}/api/status`));
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
const err = error;
|
|
46
|
-
console.error(chalk.red(`❌ Failed to start API server: ${err.message}`));
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
// Generate API key
|
|
51
|
-
api
|
|
52
|
-
.command('key')
|
|
53
|
-
.description('Generate a new API key')
|
|
54
|
-
.action(() => {
|
|
55
|
-
const apiKey = crypto.randomBytes(32).toString('hex');
|
|
56
|
-
console.log(chalk.green('🔑 Generated API Key:'));
|
|
57
|
-
console.log(apiKey);
|
|
58
|
-
console.log(chalk.gray('\nSet this as LSH_API_KEY environment variable:'));
|
|
59
|
-
console.log(chalk.gray(` export LSH_API_KEY="${apiKey}"`));
|
|
60
|
-
});
|
|
61
|
-
// Test API connection
|
|
62
|
-
api
|
|
63
|
-
.command('test')
|
|
64
|
-
.description('Test API server connection')
|
|
65
|
-
.option('-p, --port <port>', 'API port', '3030')
|
|
66
|
-
.option('-k, --api-key <key>', 'API key')
|
|
67
|
-
.action(async (options) => {
|
|
68
|
-
try {
|
|
69
|
-
const apiKey = options.apiKey || process.env.LSH_API_KEY;
|
|
70
|
-
if (!apiKey) {
|
|
71
|
-
console.error(chalk.red('❌ API key required. Use --api-key or set LSH_API_KEY environment variable'));
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
74
|
-
const response = await fetch(`http://localhost:${options.port}/api/status`, {
|
|
75
|
-
headers: {
|
|
76
|
-
'X-API-Key': apiKey
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
if (response.ok) {
|
|
80
|
-
const status = await response.json();
|
|
81
|
-
console.log(chalk.green('✅ API server is running'));
|
|
82
|
-
console.log('Status:', JSON.stringify(status, null, 2));
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
console.error(chalk.red(`❌ API server returned ${response.status}: ${response.statusText}`));
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
catch (error) {
|
|
89
|
-
const err = error;
|
|
90
|
-
console.error(chalk.red(`❌ Failed to connect to API server: ${err.message}`));
|
|
91
|
-
console.log(chalk.yellow('Make sure the daemon is running with API enabled:'));
|
|
92
|
-
console.log(chalk.gray(' lsh api start'));
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
// Configure webhooks
|
|
96
|
-
api
|
|
97
|
-
.command('webhook')
|
|
98
|
-
.description('Configure webhook endpoints')
|
|
99
|
-
.argument('<action>', 'Action: add, list, remove')
|
|
100
|
-
.argument('[endpoint]', 'Webhook endpoint URL')
|
|
101
|
-
.option('-p, --port <port>', 'API port', '3030')
|
|
102
|
-
.option('-k, --api-key <key>', 'API key')
|
|
103
|
-
.action(async (action, endpoint, options) => {
|
|
104
|
-
try {
|
|
105
|
-
const apiKey = options.apiKey || process.env.LSH_API_KEY;
|
|
106
|
-
if (!apiKey) {
|
|
107
|
-
console.error(chalk.red('❌ API key required'));
|
|
108
|
-
process.exit(1);
|
|
109
|
-
}
|
|
110
|
-
const baseUrl = `http://localhost:${options.port}`;
|
|
111
|
-
switch (action) {
|
|
112
|
-
case 'add': {
|
|
113
|
-
if (!endpoint) {
|
|
114
|
-
console.error(chalk.red('❌ Endpoint URL required'));
|
|
115
|
-
process.exit(1);
|
|
116
|
-
}
|
|
117
|
-
const addResponse = await fetch(`${baseUrl}/api/webhooks`, {
|
|
118
|
-
method: 'POST',
|
|
119
|
-
headers: {
|
|
120
|
-
'X-API-Key': apiKey,
|
|
121
|
-
'Content-Type': 'application/json'
|
|
122
|
-
},
|
|
123
|
-
body: JSON.stringify({ endpoint })
|
|
124
|
-
});
|
|
125
|
-
if (addResponse.ok) {
|
|
126
|
-
const result = await addResponse.json();
|
|
127
|
-
console.log(chalk.green('✅ Webhook added successfully'));
|
|
128
|
-
console.log('Endpoints:', result.endpoints);
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
console.error(chalk.red('❌ Failed to add webhook'));
|
|
132
|
-
}
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
case 'list': {
|
|
136
|
-
const listResponse = await fetch(`${baseUrl}/api/webhooks`, {
|
|
137
|
-
headers: { 'X-API-Key': apiKey }
|
|
138
|
-
});
|
|
139
|
-
if (listResponse.ok) {
|
|
140
|
-
const webhooks = await listResponse.json();
|
|
141
|
-
console.log(chalk.blue('📮 Webhook Configuration:'));
|
|
142
|
-
console.log(`Enabled: ${webhooks.enabled}`);
|
|
143
|
-
console.log('Endpoints:', webhooks.endpoints);
|
|
144
|
-
}
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
default:
|
|
148
|
-
console.error(chalk.red(`❌ Unknown action: ${action}`));
|
|
149
|
-
console.log('Valid actions: add, list, remove');
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
const err = error;
|
|
154
|
-
console.error(chalk.red(`❌ Failed: ${err.message}`));
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
// Example client code generator
|
|
158
|
-
api
|
|
159
|
-
.command('example')
|
|
160
|
-
.description('Generate example client code')
|
|
161
|
-
.option('-l, --language <lang>', 'Language (js, python, curl)', 'js')
|
|
162
|
-
.option('-p, --port <port>', 'API port', '3030')
|
|
163
|
-
.action((options) => {
|
|
164
|
-
const apiKey = process.env.LSH_API_KEY || 'YOUR_API_KEY';
|
|
165
|
-
switch (options.language) {
|
|
166
|
-
case 'js':
|
|
167
|
-
console.log(chalk.blue('// JavaScript Example Client\n'));
|
|
168
|
-
console.log(`const LSHClient = {
|
|
169
|
-
baseURL: 'http://localhost:${options.port}',
|
|
170
|
-
apiKey: '${apiKey}',
|
|
171
|
-
|
|
172
|
-
async request(path, options = {}) {
|
|
173
|
-
const response = await fetch(\`\${this.baseURL}\${path}\`, {
|
|
174
|
-
...options,
|
|
175
|
-
headers: {
|
|
176
|
-
'X-API-Key': this.apiKey,
|
|
177
|
-
'Content-Type': 'application/json',
|
|
178
|
-
...options.headers
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
if (!response.ok) {
|
|
183
|
-
throw new Error(\`API error: \${response.statusText}\`);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return response.json();
|
|
187
|
-
},
|
|
188
|
-
|
|
189
|
-
// Get daemon status
|
|
190
|
-
async getStatus() {
|
|
191
|
-
return this.request('/api/status');
|
|
192
|
-
},
|
|
193
|
-
|
|
194
|
-
// List all jobs
|
|
195
|
-
async listJobs() {
|
|
196
|
-
return this.request('/api/jobs');
|
|
197
|
-
},
|
|
198
|
-
|
|
199
|
-
// Create a new job
|
|
200
|
-
async createJob(jobSpec) {
|
|
201
|
-
return this.request('/api/jobs', {
|
|
202
|
-
method: 'POST',
|
|
203
|
-
body: JSON.stringify(jobSpec)
|
|
204
|
-
});
|
|
205
|
-
},
|
|
206
|
-
|
|
207
|
-
// Trigger job execution
|
|
208
|
-
async triggerJob(jobId) {
|
|
209
|
-
return this.request(\`/api/jobs/\${jobId}/trigger\`, {
|
|
210
|
-
method: 'POST'
|
|
211
|
-
});
|
|
212
|
-
},
|
|
213
|
-
|
|
214
|
-
// Stream events using EventSource
|
|
215
|
-
streamEvents() {
|
|
216
|
-
const eventSource = new EventSource(\`\${this.baseURL}/api/events\`);
|
|
217
|
-
|
|
218
|
-
eventSource.onmessage = (event) => {
|
|
219
|
-
const data = JSON.parse(event.data);
|
|
220
|
-
console.log('Event:', data);
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
eventSource.onerror = (error) => {
|
|
224
|
-
console.error('EventSource error:', error);
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
return eventSource;
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
// Example usage
|
|
232
|
-
(async () => {
|
|
233
|
-
try {
|
|
234
|
-
const status = await LSHClient.getStatus();
|
|
235
|
-
console.log('Daemon status:', status);
|
|
236
|
-
|
|
237
|
-
// Create a job
|
|
238
|
-
const job = await LSHClient.createJob({
|
|
239
|
-
name: 'Example Job',
|
|
240
|
-
command: 'echo "Hello from API"',
|
|
241
|
-
type: 'shell'
|
|
242
|
-
});
|
|
243
|
-
console.log('Created job:', job);
|
|
244
|
-
|
|
245
|
-
// Trigger the job
|
|
246
|
-
const result = await LSHClient.triggerJob(job.id);
|
|
247
|
-
console.log('Job result:', result);
|
|
248
|
-
|
|
249
|
-
} catch (error) {
|
|
250
|
-
console.error('Error:', error);
|
|
251
|
-
}
|
|
252
|
-
})();`);
|
|
253
|
-
break;
|
|
254
|
-
case 'python':
|
|
255
|
-
console.log(chalk.blue('# Python Example Client\n'));
|
|
256
|
-
console.log(`import requests
|
|
257
|
-
import json
|
|
258
|
-
from typing import Dict, Any, Optional
|
|
259
|
-
|
|
260
|
-
class LSHClient:
|
|
261
|
-
def __init__(self, base_url: str = "http://localhost:${options.port}", api_key: str = "${apiKey}"):
|
|
262
|
-
self.base_url = base_url
|
|
263
|
-
self.api_key = api_key
|
|
264
|
-
self.headers = {
|
|
265
|
-
"X-API-Key": api_key,
|
|
266
|
-
"Content-Type": "application/json"
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
def request(self, method: str, path: str, data: Optional[Dict] = None) -> Dict[str, Any]:
|
|
270
|
-
"""Make an API request"""
|
|
271
|
-
url = f"{self.base_url}{path}"
|
|
272
|
-
response = requests.request(method, url, headers=self.headers, json=data)
|
|
273
|
-
response.raise_for_status()
|
|
274
|
-
return response.json()
|
|
275
|
-
|
|
276
|
-
def get_status(self) -> Dict[str, Any]:
|
|
277
|
-
"""Get daemon status"""
|
|
278
|
-
return self.request("GET", "/api/status")
|
|
279
|
-
|
|
280
|
-
def list_jobs(self) -> list:
|
|
281
|
-
"""List all jobs"""
|
|
282
|
-
return self.request("GET", "/api/jobs")
|
|
283
|
-
|
|
284
|
-
def create_job(self, job_spec: Dict[str, Any]) -> Dict[str, Any]:
|
|
285
|
-
"""Create a new job"""
|
|
286
|
-
return self.request("POST", "/api/jobs", job_spec)
|
|
287
|
-
|
|
288
|
-
def trigger_job(self, job_id: str) -> Dict[str, Any]:
|
|
289
|
-
"""Trigger job execution"""
|
|
290
|
-
return self.request("POST", f"/api/jobs/{job_id}/trigger")
|
|
291
|
-
|
|
292
|
-
# Example usage
|
|
293
|
-
if __name__ == "__main__":
|
|
294
|
-
client = LSHClient()
|
|
295
|
-
|
|
296
|
-
# Get status
|
|
297
|
-
status = client.get_status()
|
|
298
|
-
print(f"Daemon status: {json.dumps(status, indent=2)}")
|
|
299
|
-
|
|
300
|
-
# Create a job
|
|
301
|
-
job = client.create_job({
|
|
302
|
-
"name": "Example Job",
|
|
303
|
-
"command": "echo 'Hello from Python'",
|
|
304
|
-
"type": "shell"
|
|
305
|
-
})
|
|
306
|
-
print(f"Created job: {job}")
|
|
307
|
-
|
|
308
|
-
# Trigger the job
|
|
309
|
-
result = client.trigger_job(job["id"])
|
|
310
|
-
print(f"Job result: {result}")`);
|
|
311
|
-
break;
|
|
312
|
-
case 'curl':
|
|
313
|
-
console.log(chalk.blue('# cURL Examples\n'));
|
|
314
|
-
console.log(`# Get daemon status
|
|
315
|
-
curl -H "X-API-Key: ${apiKey}" \\
|
|
316
|
-
http://localhost:${options.port}/api/status
|
|
317
|
-
|
|
318
|
-
# List all jobs
|
|
319
|
-
curl -H "X-API-Key: ${apiKey}" \\
|
|
320
|
-
http://localhost:${options.port}/api/jobs
|
|
321
|
-
|
|
322
|
-
# Create a new job
|
|
323
|
-
curl -X POST \\
|
|
324
|
-
-H "X-API-Key: ${apiKey}" \\
|
|
325
|
-
-H "Content-Type: application/json" \\
|
|
326
|
-
-d '{
|
|
327
|
-
"name": "Example Job",
|
|
328
|
-
"command": "echo Hello World",
|
|
329
|
-
"type": "shell"
|
|
330
|
-
}' \\
|
|
331
|
-
http://localhost:${options.port}/api/jobs
|
|
332
|
-
|
|
333
|
-
# Trigger a job
|
|
334
|
-
curl -X POST \\
|
|
335
|
-
-H "X-API-Key: ${apiKey}" \\
|
|
336
|
-
http://localhost:${options.port}/api/jobs/JOB_ID/trigger
|
|
337
|
-
|
|
338
|
-
# Stream events
|
|
339
|
-
curl -H "X-API-Key: ${apiKey}" \\
|
|
340
|
-
-H "Accept: text/event-stream" \\
|
|
341
|
-
http://localhost:${options.port}/api/events`);
|
|
342
|
-
break;
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
export default registerApiCommands;
|
package/dist/commands/theme.js
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Theme Commands
|
|
3
|
-
* Import, preview, and apply ZSH themes
|
|
4
|
-
*/
|
|
5
|
-
import { ThemeManager } from '../lib/theme-manager.js';
|
|
6
|
-
import chalk from 'chalk';
|
|
7
|
-
import * as fs from 'fs';
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
import * as os from 'os';
|
|
10
|
-
export function registerThemeCommands(program) {
|
|
11
|
-
const themeCommand = program
|
|
12
|
-
.command('theme')
|
|
13
|
-
.description('Manage shell themes (import Oh-My-Zsh themes, apply built-in themes)');
|
|
14
|
-
themeCommand
|
|
15
|
-
.command('list')
|
|
16
|
-
.description('List available themes')
|
|
17
|
-
.option('--ohmyzsh', 'Show only Oh-My-Zsh themes')
|
|
18
|
-
.option('--builtin', 'Show only built-in themes')
|
|
19
|
-
.option('--custom', 'Show only custom themes')
|
|
20
|
-
.action(async (options) => {
|
|
21
|
-
const manager = new ThemeManager();
|
|
22
|
-
const themes = manager.listThemes();
|
|
23
|
-
console.log(chalk.bold('\n🎨 Available Themes\n'));
|
|
24
|
-
if (!options.ohmyzsh && !options.custom || options.builtin) {
|
|
25
|
-
console.log(chalk.cyan('Built-in Themes:'));
|
|
26
|
-
themes.builtin.forEach(name => {
|
|
27
|
-
console.log(` ${chalk.green('✓')} ${name}`);
|
|
28
|
-
});
|
|
29
|
-
console.log('');
|
|
30
|
-
}
|
|
31
|
-
if (!options.builtin && !options.custom || options.ohmyzsh) {
|
|
32
|
-
if (themes.ohmyzsh.length > 0) {
|
|
33
|
-
console.log(chalk.cyan('Oh-My-Zsh Themes:'));
|
|
34
|
-
themes.ohmyzsh.slice(0, 20).forEach(name => {
|
|
35
|
-
console.log(` ${chalk.yellow('◆')} ${name}`);
|
|
36
|
-
});
|
|
37
|
-
if (themes.ohmyzsh.length > 20) {
|
|
38
|
-
console.log(chalk.dim(` ... and ${themes.ohmyzsh.length - 20} more`));
|
|
39
|
-
}
|
|
40
|
-
console.log('');
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
console.log(chalk.dim('No Oh-My-Zsh themes found. Install Oh-My-Zsh first.'));
|
|
44
|
-
console.log('');
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
if (!options.builtin && !options.ohmyzsh || options.custom) {
|
|
48
|
-
if (themes.custom.length > 0) {
|
|
49
|
-
console.log(chalk.cyan('Custom Themes:'));
|
|
50
|
-
themes.custom.forEach(name => {
|
|
51
|
-
console.log(` ${chalk.magenta('●')} ${name}`);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
console.log(chalk.dim('No custom themes found.'));
|
|
56
|
-
}
|
|
57
|
-
console.log('');
|
|
58
|
-
}
|
|
59
|
-
console.log(chalk.dim('Usage:'));
|
|
60
|
-
console.log(chalk.dim(' lsh theme preview <name>'));
|
|
61
|
-
console.log(chalk.dim(' lsh theme import <name> # For Oh-My-Zsh themes'));
|
|
62
|
-
console.log(chalk.dim(' lsh theme apply <name>'));
|
|
63
|
-
console.log('');
|
|
64
|
-
// Note: Removed process.exit(0) to allow proper Jest testing
|
|
65
|
-
// Commander will handle exit automatically
|
|
66
|
-
});
|
|
67
|
-
themeCommand
|
|
68
|
-
.command('import <name>')
|
|
69
|
-
.description('Import Oh-My-Zsh theme')
|
|
70
|
-
.option('--preview', 'Preview before importing')
|
|
71
|
-
.action(async (name, options) => {
|
|
72
|
-
try {
|
|
73
|
-
const manager = new ThemeManager();
|
|
74
|
-
console.log(chalk.dim(`Importing theme: ${name}...`));
|
|
75
|
-
const theme = await manager.importOhMyZshTheme(name);
|
|
76
|
-
console.log(chalk.green(`✓ Successfully imported theme: ${name}`));
|
|
77
|
-
if (options.preview) {
|
|
78
|
-
manager.previewTheme(theme);
|
|
79
|
-
}
|
|
80
|
-
console.log(chalk.dim('\nTo apply this theme:'));
|
|
81
|
-
console.log(chalk.cyan(` lsh theme apply ${name}`));
|
|
82
|
-
console.log('');
|
|
83
|
-
process.exit(0);
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
const err = error;
|
|
87
|
-
console.error(chalk.red(`✗ Failed to import theme: ${err.message}`));
|
|
88
|
-
process.exit(1);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
themeCommand
|
|
92
|
-
.command('preview <name>')
|
|
93
|
-
.description('Preview theme without applying')
|
|
94
|
-
.action(async (name) => {
|
|
95
|
-
try {
|
|
96
|
-
const manager = new ThemeManager();
|
|
97
|
-
// Try to load theme
|
|
98
|
-
let theme;
|
|
99
|
-
try {
|
|
100
|
-
theme = await manager.importOhMyZshTheme(name);
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
// Try built-in
|
|
104
|
-
theme = manager.getBuiltinTheme(name);
|
|
105
|
-
}
|
|
106
|
-
manager.previewTheme(theme);
|
|
107
|
-
console.log(chalk.dim('To apply this theme:'));
|
|
108
|
-
console.log(chalk.cyan(` lsh theme apply ${name}`));
|
|
109
|
-
console.log('');
|
|
110
|
-
process.exit(0);
|
|
111
|
-
}
|
|
112
|
-
catch (_error) {
|
|
113
|
-
console.error(chalk.red(`✗ Theme not found: ${name}`));
|
|
114
|
-
console.log(chalk.dim('\nAvailable themes:'));
|
|
115
|
-
console.log(chalk.dim(' lsh theme list'));
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
themeCommand
|
|
120
|
-
.command('apply <name>')
|
|
121
|
-
.description('Apply theme to current shell')
|
|
122
|
-
.option('--save', 'Save to ~/.lshrc for persistent use')
|
|
123
|
-
.action(async (name, options) => {
|
|
124
|
-
try {
|
|
125
|
-
const manager = new ThemeManager();
|
|
126
|
-
// Try to import/load theme
|
|
127
|
-
let theme;
|
|
128
|
-
try {
|
|
129
|
-
// Try as Oh-My-Zsh theme first
|
|
130
|
-
theme = await manager.importOhMyZshTheme(name);
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
// Try as built-in theme
|
|
134
|
-
theme = manager.getBuiltinTheme(name);
|
|
135
|
-
}
|
|
136
|
-
const commands = manager.applyTheme(theme);
|
|
137
|
-
console.log(chalk.green(`✓ Applied theme: ${name}`));
|
|
138
|
-
console.log('');
|
|
139
|
-
console.log(chalk.dim('Theme settings:'));
|
|
140
|
-
console.log(commands);
|
|
141
|
-
console.log('');
|
|
142
|
-
if (options.save) {
|
|
143
|
-
const lshrcPath = path.join(os.homedir(), '.lshrc');
|
|
144
|
-
let lshrcContent = '';
|
|
145
|
-
if (fs.existsSync(lshrcPath)) {
|
|
146
|
-
lshrcContent = fs.readFileSync(lshrcPath, 'utf8');
|
|
147
|
-
// Remove old theme settings
|
|
148
|
-
lshrcContent = lshrcContent.replace(/# LSH Theme[\s\S]*?# End LSH Theme\n*/g, '');
|
|
149
|
-
}
|
|
150
|
-
const themeBlock = `
|
|
151
|
-
# LSH Theme: ${name}
|
|
152
|
-
${commands}
|
|
153
|
-
# End LSH Theme
|
|
154
|
-
`;
|
|
155
|
-
lshrcContent += themeBlock;
|
|
156
|
-
fs.writeFileSync(lshrcPath, lshrcContent, 'utf8');
|
|
157
|
-
console.log(chalk.green('✓ Theme saved to ~/.lshrc'));
|
|
158
|
-
console.log(chalk.dim(' Theme will be applied automatically on next shell start'));
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
console.log(chalk.dim('To save permanently:'));
|
|
162
|
-
console.log(chalk.cyan(` lsh theme apply ${name} --save`));
|
|
163
|
-
}
|
|
164
|
-
console.log('');
|
|
165
|
-
process.exit(0);
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
const err = error;
|
|
169
|
-
console.error(chalk.red(`✗ Failed to apply theme: ${err.message}`));
|
|
170
|
-
process.exit(1);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
themeCommand
|
|
174
|
-
.command('current')
|
|
175
|
-
.description('Show current theme')
|
|
176
|
-
.action(() => {
|
|
177
|
-
const lshrcPath = path.join(os.homedir(), '.lshrc');
|
|
178
|
-
if (!fs.existsSync(lshrcPath)) {
|
|
179
|
-
console.log(chalk.dim('No theme configured (using default)'));
|
|
180
|
-
process.exit(0);
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
const content = fs.readFileSync(lshrcPath, 'utf8');
|
|
184
|
-
const themeMatch = content.match(/# LSH Theme: (.+)/);
|
|
185
|
-
if (themeMatch) {
|
|
186
|
-
console.log(chalk.bold(`Current theme: ${chalk.cyan(themeMatch[1])}`));
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
console.log(chalk.dim('No theme configured (using default)'));
|
|
190
|
-
}
|
|
191
|
-
process.exit(0);
|
|
192
|
-
});
|
|
193
|
-
themeCommand
|
|
194
|
-
.command('reset')
|
|
195
|
-
.description('Reset to default theme')
|
|
196
|
-
.action(() => {
|
|
197
|
-
const lshrcPath = path.join(os.homedir(), '.lshrc');
|
|
198
|
-
if (fs.existsSync(lshrcPath)) {
|
|
199
|
-
let content = fs.readFileSync(lshrcPath, 'utf8');
|
|
200
|
-
content = content.replace(/# LSH Theme[\s\S]*?# End LSH Theme\n*/g, '');
|
|
201
|
-
fs.writeFileSync(lshrcPath, content, 'utf8');
|
|
202
|
-
}
|
|
203
|
-
console.log(chalk.green('✓ Reset to default theme'));
|
|
204
|
-
console.log(chalk.dim('Restart shell to see changes'));
|
|
205
|
-
process.exit(0);
|
|
206
|
-
});
|
|
207
|
-
themeCommand
|
|
208
|
-
.command('from-zshrc')
|
|
209
|
-
.description('Import current ZSH theme from ~/.zshrc')
|
|
210
|
-
.option('--apply', 'Apply after importing')
|
|
211
|
-
.action(async (options) => {
|
|
212
|
-
try {
|
|
213
|
-
const zshrcPath = path.join(os.homedir(), '.zshrc');
|
|
214
|
-
if (!fs.existsSync(zshrcPath)) {
|
|
215
|
-
console.error(chalk.red('✗ ~/.zshrc not found'));
|
|
216
|
-
process.exit(1);
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
const zshrcContent = fs.readFileSync(zshrcPath, 'utf8');
|
|
220
|
-
const themeMatch = zshrcContent.match(/ZSH_THEME="([^"]+)"/);
|
|
221
|
-
if (!themeMatch) {
|
|
222
|
-
console.error(chalk.red('✗ No theme found in ~/.zshrc'));
|
|
223
|
-
process.exit(1);
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
const themeName = themeMatch[1];
|
|
227
|
-
console.log(chalk.dim(`Found ZSH theme: ${themeName}`));
|
|
228
|
-
const manager = new ThemeManager();
|
|
229
|
-
const theme = await manager.importOhMyZshTheme(themeName);
|
|
230
|
-
console.log(chalk.green(`✓ Imported theme: ${themeName}`));
|
|
231
|
-
if (options.apply) {
|
|
232
|
-
const commands = manager.applyTheme(theme);
|
|
233
|
-
const lshrcPath = path.join(os.homedir(), '.lshrc');
|
|
234
|
-
let lshrcContent = '';
|
|
235
|
-
if (fs.existsSync(lshrcPath)) {
|
|
236
|
-
lshrcContent = fs.readFileSync(lshrcPath, 'utf8');
|
|
237
|
-
lshrcContent = lshrcContent.replace(/# LSH Theme[\s\S]*?# End LSH Theme\n*/g, '');
|
|
238
|
-
}
|
|
239
|
-
const themeBlock = `
|
|
240
|
-
# LSH Theme: ${themeName}
|
|
241
|
-
${commands}
|
|
242
|
-
# End LSH Theme
|
|
243
|
-
`;
|
|
244
|
-
lshrcContent += themeBlock;
|
|
245
|
-
fs.writeFileSync(lshrcPath, lshrcContent, 'utf8');
|
|
246
|
-
console.log(chalk.green('✓ Theme applied and saved to ~/.lshrc'));
|
|
247
|
-
}
|
|
248
|
-
else {
|
|
249
|
-
console.log(chalk.dim('\nTo apply:'));
|
|
250
|
-
console.log(chalk.cyan(` lsh theme apply ${themeName} --save`));
|
|
251
|
-
}
|
|
252
|
-
console.log('');
|
|
253
|
-
process.exit(0);
|
|
254
|
-
}
|
|
255
|
-
catch (error) {
|
|
256
|
-
const err = error;
|
|
257
|
-
console.error(chalk.red(`✗ Failed to import theme: ${err.message}`));
|
|
258
|
-
process.exit(1);
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
}
|