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.
- package/.env.example +51 -0
- package/README.md +399 -0
- package/dist/app.js +33 -0
- package/dist/cicd/analytics.js +261 -0
- package/dist/cicd/auth.js +269 -0
- package/dist/cicd/cache-manager.js +172 -0
- package/dist/cicd/data-retention.js +305 -0
- package/dist/cicd/performance-monitor.js +224 -0
- package/dist/cicd/webhook-receiver.js +634 -0
- package/dist/cli.js +500 -0
- package/dist/commands/api.js +343 -0
- package/dist/commands/self.js +318 -0
- package/dist/commands/theme.js +257 -0
- package/dist/commands/zsh-import.js +240 -0
- package/dist/components/App.js +1 -0
- package/dist/components/Divider.js +29 -0
- package/dist/components/REPL.js +43 -0
- package/dist/components/Terminal.js +232 -0
- package/dist/components/UserInput.js +30 -0
- package/dist/daemon/api-server.js +315 -0
- package/dist/daemon/job-registry.js +554 -0
- package/dist/daemon/lshd.js +822 -0
- package/dist/daemon/monitoring-api.js +220 -0
- package/dist/examples/supabase-integration.js +106 -0
- package/dist/lib/api-error-handler.js +183 -0
- package/dist/lib/associative-arrays.js +285 -0
- package/dist/lib/base-api-server.js +290 -0
- package/dist/lib/base-command-registrar.js +286 -0
- package/dist/lib/base-job-manager.js +293 -0
- package/dist/lib/brace-expansion.js +160 -0
- package/dist/lib/builtin-commands.js +439 -0
- package/dist/lib/cloud-config-manager.js +347 -0
- package/dist/lib/command-validator.js +190 -0
- package/dist/lib/completion-system.js +344 -0
- package/dist/lib/cron-job-manager.js +364 -0
- package/dist/lib/daemon-client-helper.js +141 -0
- package/dist/lib/daemon-client.js +501 -0
- package/dist/lib/database-persistence.js +638 -0
- package/dist/lib/database-schema.js +259 -0
- package/dist/lib/enhanced-history-system.js +246 -0
- package/dist/lib/env-validator.js +265 -0
- package/dist/lib/executors/builtin-executor.js +52 -0
- package/dist/lib/extended-globbing.js +411 -0
- package/dist/lib/extended-parameter-expansion.js +227 -0
- package/dist/lib/floating-point-arithmetic.js +256 -0
- package/dist/lib/history-system.js +245 -0
- package/dist/lib/interactive-shell.js +460 -0
- package/dist/lib/job-builtins.js +580 -0
- package/dist/lib/job-manager.js +386 -0
- package/dist/lib/job-storage-database.js +156 -0
- package/dist/lib/job-storage-memory.js +73 -0
- package/dist/lib/logger.js +274 -0
- package/dist/lib/lshrc-init.js +177 -0
- package/dist/lib/pathname-expansion.js +216 -0
- package/dist/lib/prompt-system.js +328 -0
- package/dist/lib/script-runner.js +226 -0
- package/dist/lib/secrets-manager.js +193 -0
- package/dist/lib/shell-executor.js +2504 -0
- package/dist/lib/shell-parser.js +958 -0
- package/dist/lib/shell-types.js +6 -0
- package/dist/lib/shell.lib.js +40 -0
- package/dist/lib/supabase-client.js +58 -0
- package/dist/lib/theme-manager.js +476 -0
- package/dist/lib/variable-expansion.js +385 -0
- package/dist/lib/zsh-compatibility.js +658 -0
- package/dist/lib/zsh-import-manager.js +699 -0
- package/dist/lib/zsh-options.js +328 -0
- package/dist/pipeline/job-tracker.js +491 -0
- package/dist/pipeline/mcli-bridge.js +302 -0
- package/dist/pipeline/pipeline-service.js +1116 -0
- package/dist/pipeline/workflow-engine.js +867 -0
- package/dist/services/api/api.js +58 -0
- package/dist/services/api/auth.js +35 -0
- package/dist/services/api/config.js +7 -0
- package/dist/services/api/file.js +22 -0
- package/dist/services/cron/cron-registrar.js +235 -0
- package/dist/services/cron/cron.js +9 -0
- package/dist/services/daemon/daemon-registrar.js +565 -0
- package/dist/services/daemon/daemon.js +9 -0
- package/dist/services/lib/lib.js +86 -0
- package/dist/services/log-file-extractor.js +170 -0
- package/dist/services/secrets/secrets.js +94 -0
- package/dist/services/shell/shell.js +28 -0
- package/dist/services/supabase/supabase-registrar.js +367 -0
- package/dist/services/supabase/supabase.js +9 -0
- package/dist/services/zapier.js +16 -0
- package/dist/simple-api-server.js +148 -0
- package/dist/store/store.js +31 -0
- package/dist/util/lib.util.js +11 -0
- package/package.json +144 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log File Extractor
|
|
3
|
+
* Utility for extracting relevant log entries from log files
|
|
4
|
+
* based on patterns and filters.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
import readline from 'readline';
|
|
8
|
+
/**
|
|
9
|
+
* Extract relevant log entries from a log file
|
|
10
|
+
*/
|
|
11
|
+
export async function extractRelevantLogs(filePath, options) {
|
|
12
|
+
const { pattern, maxLines = 1000, tailLines, contextBefore = 0, contextAfter = 0, } = options;
|
|
13
|
+
// Read the file
|
|
14
|
+
const fileHandle = await fs.open(filePath, 'r');
|
|
15
|
+
const stream = fileHandle.createReadStream();
|
|
16
|
+
const rl = readline.createInterface({
|
|
17
|
+
input: stream,
|
|
18
|
+
crlfDelay: Infinity,
|
|
19
|
+
});
|
|
20
|
+
const allLines = [];
|
|
21
|
+
const matchingIndices = [];
|
|
22
|
+
const result = [];
|
|
23
|
+
// If tailLines is specified, we need to buffer all lines first
|
|
24
|
+
if (tailLines !== undefined) {
|
|
25
|
+
for await (const line of rl) {
|
|
26
|
+
allLines.push(line);
|
|
27
|
+
}
|
|
28
|
+
// Only process last N lines
|
|
29
|
+
const startIndex = Math.max(0, allLines.length - tailLines);
|
|
30
|
+
const linesToProcess = allLines.slice(startIndex);
|
|
31
|
+
// Find matching lines
|
|
32
|
+
linesToProcess.forEach((line, index) => {
|
|
33
|
+
if (pattern.test(line)) {
|
|
34
|
+
matchingIndices.push(startIndex + index);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// Process line by line without buffering all
|
|
40
|
+
let lineIndex = 0;
|
|
41
|
+
for await (const line of rl) {
|
|
42
|
+
allLines.push(line);
|
|
43
|
+
if (pattern.test(line)) {
|
|
44
|
+
matchingIndices.push(lineIndex);
|
|
45
|
+
}
|
|
46
|
+
lineIndex++;
|
|
47
|
+
// Stop if we have enough matches (considering context)
|
|
48
|
+
if (matchingIndices.length >= maxLines) {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
await fileHandle.close();
|
|
54
|
+
// Extract lines with context
|
|
55
|
+
const includedIndices = new Set();
|
|
56
|
+
for (const matchIndex of matchingIndices) {
|
|
57
|
+
if (result.length >= maxLines) {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
// Calculate range of lines to include
|
|
61
|
+
const startIndex = Math.max(0, matchIndex - contextBefore);
|
|
62
|
+
const endIndex = Math.min(allLines.length - 1, matchIndex + contextAfter);
|
|
63
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
64
|
+
if (!includedIndices.has(i)) {
|
|
65
|
+
result.push(allLines[i]);
|
|
66
|
+
includedIndices.add(i);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result.slice(0, maxLines);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Extract error logs from a file
|
|
74
|
+
*/
|
|
75
|
+
export async function extractErrors(filePath, options) {
|
|
76
|
+
return extractRelevantLogs(filePath, {
|
|
77
|
+
pattern: /ERROR|FATAL|CRITICAL/i,
|
|
78
|
+
maxLines: 100,
|
|
79
|
+
contextBefore: 2,
|
|
80
|
+
contextAfter: 2,
|
|
81
|
+
...options,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Extract warning logs from a file
|
|
86
|
+
*/
|
|
87
|
+
export async function extractWarnings(filePath, options) {
|
|
88
|
+
return extractRelevantLogs(filePath, {
|
|
89
|
+
pattern: /WARN|WARNING/i,
|
|
90
|
+
maxLines: 100,
|
|
91
|
+
contextBefore: 1,
|
|
92
|
+
contextAfter: 1,
|
|
93
|
+
...options,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Extract logs from the last N minutes
|
|
98
|
+
*/
|
|
99
|
+
export async function extractRecent(filePath, minutes, options) {
|
|
100
|
+
const now = new Date();
|
|
101
|
+
const cutoffTime = new Date(now.getTime() - minutes * 60 * 1000);
|
|
102
|
+
// Common timestamp patterns
|
|
103
|
+
const timestampPattern = /\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}/;
|
|
104
|
+
return extractRelevantLogs(filePath, {
|
|
105
|
+
pattern: timestampPattern,
|
|
106
|
+
maxLines: 1000,
|
|
107
|
+
...options,
|
|
108
|
+
}).then((lines) => {
|
|
109
|
+
// Filter lines by timestamp
|
|
110
|
+
return lines.filter((line) => {
|
|
111
|
+
const match = line.match(timestampPattern);
|
|
112
|
+
if (match) {
|
|
113
|
+
try {
|
|
114
|
+
const lineTime = new Date(match[0].replace(' ', 'T'));
|
|
115
|
+
return lineTime >= cutoffTime;
|
|
116
|
+
}
|
|
117
|
+
catch (_error) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get summary statistics from a log file
|
|
127
|
+
*/
|
|
128
|
+
export async function getLogStatistics(filePath) {
|
|
129
|
+
const fileHandle = await fs.open(filePath, 'r');
|
|
130
|
+
const stream = fileHandle.createReadStream();
|
|
131
|
+
const rl = readline.createInterface({
|
|
132
|
+
input: stream,
|
|
133
|
+
crlfDelay: Infinity,
|
|
134
|
+
});
|
|
135
|
+
let totalLines = 0;
|
|
136
|
+
let errorCount = 0;
|
|
137
|
+
let warningCount = 0;
|
|
138
|
+
let infoCount = 0;
|
|
139
|
+
let debugCount = 0;
|
|
140
|
+
for await (const line of rl) {
|
|
141
|
+
totalLines++;
|
|
142
|
+
if (/ERROR|FATAL|CRITICAL/i.test(line)) {
|
|
143
|
+
errorCount++;
|
|
144
|
+
}
|
|
145
|
+
else if (/WARN|WARNING/i.test(line)) {
|
|
146
|
+
warningCount++;
|
|
147
|
+
}
|
|
148
|
+
else if (/INFO/i.test(line)) {
|
|
149
|
+
infoCount++;
|
|
150
|
+
}
|
|
151
|
+
else if (/DEBUG|TRACE/i.test(line)) {
|
|
152
|
+
debugCount++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
await fileHandle.close();
|
|
156
|
+
return {
|
|
157
|
+
totalLines,
|
|
158
|
+
errorCount,
|
|
159
|
+
warningCount,
|
|
160
|
+
infoCount,
|
|
161
|
+
debugCount,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
export default {
|
|
165
|
+
extractRelevantLogs,
|
|
166
|
+
extractErrors,
|
|
167
|
+
extractWarnings,
|
|
168
|
+
extractRecent,
|
|
169
|
+
getLogStatistics,
|
|
170
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secrets Management Commands
|
|
3
|
+
* Sync .env files across development environments
|
|
4
|
+
*/
|
|
5
|
+
import SecretsManager from '../../lib/secrets-manager.js';
|
|
6
|
+
export async function init_secrets(program) {
|
|
7
|
+
const secretsCmd = program
|
|
8
|
+
.command('secrets')
|
|
9
|
+
.description('Manage environment secrets across machines');
|
|
10
|
+
// Push secrets to cloud
|
|
11
|
+
secretsCmd
|
|
12
|
+
.command('push')
|
|
13
|
+
.description('Push local .env to encrypted cloud storage')
|
|
14
|
+
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
15
|
+
.option('-e, --env <name>', 'Environment name (dev/staging/prod)', 'dev')
|
|
16
|
+
.action(async (options) => {
|
|
17
|
+
try {
|
|
18
|
+
const manager = new SecretsManager();
|
|
19
|
+
await manager.push(options.file, options.env);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error('❌ Failed to push secrets:', error.message);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
// Pull secrets from cloud
|
|
27
|
+
secretsCmd
|
|
28
|
+
.command('pull')
|
|
29
|
+
.description('Pull .env from encrypted cloud storage')
|
|
30
|
+
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
31
|
+
.option('-e, --env <name>', 'Environment name (dev/staging/prod)', 'dev')
|
|
32
|
+
.action(async (options) => {
|
|
33
|
+
try {
|
|
34
|
+
const manager = new SecretsManager();
|
|
35
|
+
await manager.pull(options.file, options.env);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error('❌ Failed to pull secrets:', error.message);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
// List environments
|
|
43
|
+
secretsCmd
|
|
44
|
+
.command('list')
|
|
45
|
+
.alias('ls')
|
|
46
|
+
.description('List all stored environments')
|
|
47
|
+
.action(async () => {
|
|
48
|
+
try {
|
|
49
|
+
const manager = new SecretsManager();
|
|
50
|
+
const envs = await manager.listEnvironments();
|
|
51
|
+
if (envs.length === 0) {
|
|
52
|
+
console.log('No environments found. Push your first .env with: lsh secrets push');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
console.log('\n📦 Available environments:\n');
|
|
56
|
+
for (const env of envs) {
|
|
57
|
+
console.log(` • ${env}`);
|
|
58
|
+
}
|
|
59
|
+
console.log();
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error('❌ Failed to list environments:', error.message);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
// Show secrets (masked)
|
|
67
|
+
secretsCmd
|
|
68
|
+
.command('show')
|
|
69
|
+
.description('Show secrets for an environment (masked)')
|
|
70
|
+
.option('-e, --env <name>', 'Environment name', 'dev')
|
|
71
|
+
.action(async (options) => {
|
|
72
|
+
try {
|
|
73
|
+
const manager = new SecretsManager();
|
|
74
|
+
await manager.show(options.env);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error('❌ Failed to show secrets:', error.message);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
// Generate encryption key
|
|
82
|
+
secretsCmd
|
|
83
|
+
.command('key')
|
|
84
|
+
.description('Generate a new encryption key')
|
|
85
|
+
.action(async () => {
|
|
86
|
+
const { randomBytes } = await import('crypto');
|
|
87
|
+
const key = randomBytes(32).toString('hex');
|
|
88
|
+
console.log('\n🔑 New encryption key (add to your .env):\n');
|
|
89
|
+
console.log(`LSH_SECRETS_KEY=${key}\n`);
|
|
90
|
+
console.log('💡 Tip: Share this key securely with your team to sync secrets.');
|
|
91
|
+
console.log(' Never commit it to git!\n');
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
export default init_secrets;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { render } from "ink";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Terminal } from "../../components/Terminal.js";
|
|
4
|
+
export async function get_ishell(_type = "Jvm", _spec = { function: "currentUsage" }) { }
|
|
5
|
+
export async function init_ishell(program) {
|
|
6
|
+
await cmd_interactive(program);
|
|
7
|
+
}
|
|
8
|
+
async function cmd_interactive(program) {
|
|
9
|
+
program
|
|
10
|
+
.command("repl")
|
|
11
|
+
.description("lsh interactive shell")
|
|
12
|
+
.action(async (_type, _action, _spec) => {
|
|
13
|
+
// Check if raw mode is supported before rendering
|
|
14
|
+
if (process.stdin.isTTY && process.stdin.setRawMode !== undefined) {
|
|
15
|
+
render(React.createElement(Terminal, null));
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
console.log('⚠️ Interactive mode not supported');
|
|
19
|
+
console.log('Raw mode is not supported in this environment.');
|
|
20
|
+
console.log('To use the interactive REPL, run this command in a proper terminal:');
|
|
21
|
+
console.log(' npm start repl');
|
|
22
|
+
console.log('or');
|
|
23
|
+
console.log(' node dist/app.js repl');
|
|
24
|
+
console.log('For testing, use the shell lib functions directly in your Node.js code.');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supabase Command Registrar
|
|
3
|
+
* Registers all Supabase-related CLI commands using BaseCommandRegistrar
|
|
4
|
+
*/
|
|
5
|
+
import { BaseCommandRegistrar } from '../../lib/base-command-registrar.js';
|
|
6
|
+
import { supabaseClient } from '../../lib/supabase-client.js';
|
|
7
|
+
import DatabasePersistence from '../../lib/database-persistence.js';
|
|
8
|
+
import CloudConfigManager from '../../lib/cloud-config-manager.js';
|
|
9
|
+
import { CREATE_TABLES_SQL } from '../../lib/database-schema.js';
|
|
10
|
+
export class SupabaseCommandRegistrar extends BaseCommandRegistrar {
|
|
11
|
+
constructor() {
|
|
12
|
+
super('SupabaseService');
|
|
13
|
+
}
|
|
14
|
+
async register(program) {
|
|
15
|
+
const supabaseCmd = this.createCommand(program, 'supabase', 'Supabase database management commands');
|
|
16
|
+
this.registerConnectionCommands(supabaseCmd);
|
|
17
|
+
this.registerDataCommands(supabaseCmd);
|
|
18
|
+
this.registerMLCommands(supabaseCmd);
|
|
19
|
+
}
|
|
20
|
+
registerConnectionCommands(supabaseCmd) {
|
|
21
|
+
// Test connection
|
|
22
|
+
this.addSubcommand(supabaseCmd, {
|
|
23
|
+
name: 'test',
|
|
24
|
+
description: 'Test Supabase database connection',
|
|
25
|
+
action: async () => {
|
|
26
|
+
this.logInfo('Testing Supabase connection...');
|
|
27
|
+
const isConnected = await supabaseClient.testConnection();
|
|
28
|
+
if (isConnected) {
|
|
29
|
+
this.logSuccess('Supabase connection successful');
|
|
30
|
+
const info = supabaseClient.getConnectionInfo();
|
|
31
|
+
this.logInfo(`Connection info: ${JSON.stringify(info)}`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
throw new Error('Supabase connection failed');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
// Initialize schema
|
|
39
|
+
this.addSubcommand(supabaseCmd, {
|
|
40
|
+
name: 'init',
|
|
41
|
+
description: 'Initialize database schema',
|
|
42
|
+
action: async () => {
|
|
43
|
+
this.logInfo('Initializing database schema...');
|
|
44
|
+
const persistence = new DatabasePersistence();
|
|
45
|
+
const success = await persistence.initializeSchema();
|
|
46
|
+
if (success) {
|
|
47
|
+
this.logSuccess('Database schema initialized');
|
|
48
|
+
this.logInfo('Note: Run the following SQL in your Supabase dashboard:');
|
|
49
|
+
this.logInfo(CREATE_TABLES_SQL);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
throw new Error('Failed to initialize schema');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
// Sync management
|
|
57
|
+
this.addSubcommand(supabaseCmd, {
|
|
58
|
+
name: 'sync',
|
|
59
|
+
description: 'Synchronize data with Supabase',
|
|
60
|
+
options: [
|
|
61
|
+
{ flags: '-f, --force', description: 'Force full synchronization', defaultValue: false }
|
|
62
|
+
],
|
|
63
|
+
action: async (_options) => {
|
|
64
|
+
this.logInfo('Synchronizing data with Supabase...');
|
|
65
|
+
const persistence = new DatabasePersistence();
|
|
66
|
+
// Test connection first
|
|
67
|
+
const isConnected = await persistence.testConnection();
|
|
68
|
+
if (!isConnected) {
|
|
69
|
+
throw new Error('Cannot sync - database not available');
|
|
70
|
+
}
|
|
71
|
+
// Sync configuration
|
|
72
|
+
this.logInfo('Syncing configuration...');
|
|
73
|
+
// Configuration sync is handled automatically by CloudConfigManager
|
|
74
|
+
// Sync history (this would be done automatically by the enhanced history system)
|
|
75
|
+
this.logInfo('Syncing history...');
|
|
76
|
+
// History sync is handled by EnhancedHistorySystem
|
|
77
|
+
this.logSuccess('Synchronization completed');
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
registerDataCommands(supabaseCmd) {
|
|
82
|
+
// History management
|
|
83
|
+
this.addSubcommand(supabaseCmd, {
|
|
84
|
+
name: 'history',
|
|
85
|
+
description: 'Manage shell history',
|
|
86
|
+
options: [
|
|
87
|
+
{ flags: '-l, --list', description: 'List recent history entries', defaultValue: false },
|
|
88
|
+
{ flags: '-c, --count <number>', description: 'Number of entries to show', defaultValue: '10' },
|
|
89
|
+
{ flags: '-s, --search <query>', description: 'Search history entries' }
|
|
90
|
+
],
|
|
91
|
+
action: async (options) => {
|
|
92
|
+
const persistence = new DatabasePersistence();
|
|
93
|
+
if (options.list) {
|
|
94
|
+
const count = parseInt(options.count);
|
|
95
|
+
const entries = await persistence.getHistoryEntries(count);
|
|
96
|
+
this.logInfo(`Recent ${entries.length} history entries:`);
|
|
97
|
+
entries.forEach((entry, index) => {
|
|
98
|
+
const timestamp = new Date(entry.timestamp).toLocaleString();
|
|
99
|
+
const exitCode = entry.exit_code ? ` (exit: ${entry.exit_code})` : '';
|
|
100
|
+
this.logInfo(`${index + 1}. [${timestamp}] ${entry.command}${exitCode}`);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else if (options.search) {
|
|
104
|
+
const entries = await persistence.getHistoryEntries(100);
|
|
105
|
+
const filtered = entries.filter(entry => entry.command.toLowerCase().includes(options.search.toLowerCase()));
|
|
106
|
+
this.logInfo(`Found ${filtered.length} matching entries:`);
|
|
107
|
+
filtered.forEach((entry, index) => {
|
|
108
|
+
const timestamp = new Date(entry.timestamp).toLocaleString();
|
|
109
|
+
this.logInfo(`${index + 1}. [${timestamp}] ${entry.command}`);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
this.logInfo('Use --list or --search to manage history');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
// Configuration management
|
|
118
|
+
this.addSubcommand(supabaseCmd, {
|
|
119
|
+
name: 'config',
|
|
120
|
+
description: 'Manage shell configuration',
|
|
121
|
+
options: [
|
|
122
|
+
{ flags: '-l, --list', description: 'List all configuration', defaultValue: false },
|
|
123
|
+
{ flags: '-g, --get <key>', description: 'Get configuration value' },
|
|
124
|
+
{ flags: '-s, --set <key> <value>', description: 'Set configuration value' },
|
|
125
|
+
{ flags: '-d, --delete <key>', description: 'Delete configuration key' },
|
|
126
|
+
{ flags: '-e, --export', description: 'Export configuration to JSON', defaultValue: false }
|
|
127
|
+
],
|
|
128
|
+
action: async (options) => {
|
|
129
|
+
const configManager = new CloudConfigManager();
|
|
130
|
+
if (options.list) {
|
|
131
|
+
const config = configManager.getAll();
|
|
132
|
+
this.logInfo('Current configuration:');
|
|
133
|
+
config.forEach(item => {
|
|
134
|
+
this.logInfo(` ${item.key}: ${JSON.stringify(item.value)}`);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
else if (options.get) {
|
|
138
|
+
const value = configManager.get(options.get);
|
|
139
|
+
if (value !== undefined) {
|
|
140
|
+
this.logInfo(`${options.get}: ${JSON.stringify(value)}`);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
this.logWarning(`Configuration key '${options.get}' not found`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else if (options.set) {
|
|
147
|
+
const [key, value] = options.set;
|
|
148
|
+
configManager.set(key, value);
|
|
149
|
+
this.logSuccess(`Configuration '${key}' set to: ${value}`);
|
|
150
|
+
}
|
|
151
|
+
else if (options.delete) {
|
|
152
|
+
configManager.delete(options.delete);
|
|
153
|
+
this.logSuccess(`Configuration '${options.delete}' deleted`);
|
|
154
|
+
}
|
|
155
|
+
else if (options.export) {
|
|
156
|
+
const exported = configManager.export();
|
|
157
|
+
this.logInfo(exported);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
this.logInfo('Use --list, --get, --set, --delete, or --export to manage configuration');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
// Jobs management
|
|
165
|
+
this.addSubcommand(supabaseCmd, {
|
|
166
|
+
name: 'jobs',
|
|
167
|
+
description: 'Manage shell jobs',
|
|
168
|
+
options: [
|
|
169
|
+
{ flags: '-l, --list', description: 'List active jobs', defaultValue: false },
|
|
170
|
+
{ flags: '-h, --history', description: 'List job history', defaultValue: false }
|
|
171
|
+
],
|
|
172
|
+
action: async (options) => {
|
|
173
|
+
const persistence = new DatabasePersistence();
|
|
174
|
+
if (options.list) {
|
|
175
|
+
const jobs = await persistence.getActiveJobs();
|
|
176
|
+
this.logInfo(`Active jobs (${jobs.length}):`);
|
|
177
|
+
jobs.forEach(job => {
|
|
178
|
+
const started = new Date(job.started_at).toLocaleString();
|
|
179
|
+
this.logInfo(`${job.job_id}: ${job.command} (${job.status}) - Started: ${started}`);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
else if (options.history) {
|
|
183
|
+
this.logInfo('Job history feature not yet implemented');
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
this.logInfo('Use --list or --history to manage jobs');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
// Database rows management
|
|
191
|
+
this.addSubcommand(supabaseCmd, {
|
|
192
|
+
name: 'rows',
|
|
193
|
+
description: 'Show latest database entries',
|
|
194
|
+
options: [
|
|
195
|
+
{ flags: '-l, --limit <number>', description: 'Number of rows to show per table', defaultValue: '5' },
|
|
196
|
+
{ flags: '-t, --table <name>', description: 'Show rows from specific table only' }
|
|
197
|
+
],
|
|
198
|
+
action: async (options) => {
|
|
199
|
+
const persistence = new DatabasePersistence();
|
|
200
|
+
const limit = parseInt(options.limit);
|
|
201
|
+
// Test connection first
|
|
202
|
+
const isConnected = await persistence.testConnection();
|
|
203
|
+
if (!isConnected) {
|
|
204
|
+
throw new Error('Cannot fetch rows - database not available');
|
|
205
|
+
}
|
|
206
|
+
if (options.table) {
|
|
207
|
+
// Show rows from specific table
|
|
208
|
+
this.logInfo(`Latest ${limit} entries from table '${options.table}':`);
|
|
209
|
+
const rows = await persistence.getLatestRowsFromTable(options.table, limit);
|
|
210
|
+
if (rows.length === 0) {
|
|
211
|
+
this.logInfo('No entries found.');
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
rows.forEach((row, index) => {
|
|
215
|
+
const timestamp = row.created_at ? new Date(row.created_at).toLocaleString() : 'N/A';
|
|
216
|
+
this.logInfo(`\n${index + 1}. [${timestamp}]`);
|
|
217
|
+
this.logInfo(JSON.stringify(row, null, 2));
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
// Show rows from all tables
|
|
223
|
+
this.logInfo(`Latest ${limit} entries from each table:`);
|
|
224
|
+
const allRows = await persistence.getLatestRows(limit);
|
|
225
|
+
for (const [tableName, rows] of Object.entries(allRows)) {
|
|
226
|
+
this.logInfo(`\n=== ${tableName.toUpperCase()} ===`);
|
|
227
|
+
if (rows.length === 0) {
|
|
228
|
+
this.logInfo('No entries found.');
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
rows.forEach((row, index) => {
|
|
232
|
+
const timestamp = row.created_at ? new Date(row.created_at).toLocaleString() : 'N/A';
|
|
233
|
+
this.logInfo(`\n${index + 1}. [${timestamp}]`);
|
|
234
|
+
this.logInfo(JSON.stringify(row, null, 2));
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
registerMLCommands(supabaseCmd) {
|
|
243
|
+
// ML Training Jobs
|
|
244
|
+
this.addSubcommand(supabaseCmd, {
|
|
245
|
+
name: 'ml-train',
|
|
246
|
+
description: 'Manage ML training jobs',
|
|
247
|
+
options: [
|
|
248
|
+
{ flags: '-l, --list', description: 'List training jobs', defaultValue: false },
|
|
249
|
+
{ flags: '-s, --status <status>', description: 'Filter by status (pending, running, completed, failed)' },
|
|
250
|
+
{ flags: '-c, --create <name>', description: 'Create new training job' },
|
|
251
|
+
{ flags: '--model-type <type>', description: 'Model type for new job' },
|
|
252
|
+
{ flags: '--dataset <name>', description: 'Dataset name for new job' }
|
|
253
|
+
],
|
|
254
|
+
action: async (options) => {
|
|
255
|
+
if (options.list) {
|
|
256
|
+
let query = supabaseClient.getClient()
|
|
257
|
+
.from('ml_training_jobs')
|
|
258
|
+
.select('*')
|
|
259
|
+
.order('created_at', { ascending: false });
|
|
260
|
+
if (options.status) {
|
|
261
|
+
query = query.eq('status', options.status);
|
|
262
|
+
}
|
|
263
|
+
const { data: jobs, error } = await query.limit(20);
|
|
264
|
+
if (error) {
|
|
265
|
+
throw new Error(`Failed to fetch training jobs: ${error.message}`);
|
|
266
|
+
}
|
|
267
|
+
this.logInfo(`Training Jobs (${jobs?.length || 0}):`);
|
|
268
|
+
jobs?.forEach(job => {
|
|
269
|
+
const created = new Date(job.created_at).toLocaleString();
|
|
270
|
+
this.logInfo(`\n${job.job_name} (${job.model_type})`);
|
|
271
|
+
this.logInfo(` Status: ${job.status}`);
|
|
272
|
+
this.logInfo(` Created: ${created}`);
|
|
273
|
+
this.logInfo(` Dataset: ${job.dataset_name}`);
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
else if (options.create) {
|
|
277
|
+
if (!options.modelType || !options.dataset) {
|
|
278
|
+
throw new Error('Both --model-type and --dataset are required to create a job');
|
|
279
|
+
}
|
|
280
|
+
const { data, error } = await supabaseClient.getClient()
|
|
281
|
+
.from('ml_training_jobs')
|
|
282
|
+
.insert({
|
|
283
|
+
job_name: options.create,
|
|
284
|
+
model_type: options.modelType,
|
|
285
|
+
dataset_name: options.dataset,
|
|
286
|
+
status: 'pending',
|
|
287
|
+
created_at: new Date().toISOString()
|
|
288
|
+
})
|
|
289
|
+
.select();
|
|
290
|
+
if (error) {
|
|
291
|
+
throw new Error(`Failed to create training job: ${error.message}`);
|
|
292
|
+
}
|
|
293
|
+
this.logSuccess(`Created training job: ${options.create}`);
|
|
294
|
+
this.logInfo(JSON.stringify(data, null, 2));
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
this.logInfo('Use --list or --create to manage training jobs');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
// ML Models
|
|
302
|
+
this.addSubcommand(supabaseCmd, {
|
|
303
|
+
name: 'ml-models',
|
|
304
|
+
description: 'Manage ML models',
|
|
305
|
+
options: [
|
|
306
|
+
{ flags: '-l, --list', description: 'List ML models', defaultValue: false },
|
|
307
|
+
{ flags: '--deployed', description: 'Filter by deployed models only', defaultValue: false }
|
|
308
|
+
],
|
|
309
|
+
action: async (options) => {
|
|
310
|
+
if (options.list) {
|
|
311
|
+
let query = supabaseClient.getClient()
|
|
312
|
+
.from('ml_models')
|
|
313
|
+
.select('*')
|
|
314
|
+
.order('created_at', { ascending: false });
|
|
315
|
+
if (options.deployed) {
|
|
316
|
+
query = query.eq('deployed', true);
|
|
317
|
+
}
|
|
318
|
+
const { data: models, error } = await query.limit(20);
|
|
319
|
+
if (error) {
|
|
320
|
+
throw new Error(`Failed to fetch models: ${error.message}`);
|
|
321
|
+
}
|
|
322
|
+
this.logInfo(`ML Models (${models?.length || 0}):`);
|
|
323
|
+
models?.forEach(model => {
|
|
324
|
+
const created = new Date(model.created_at).toLocaleString();
|
|
325
|
+
this.logInfo(`\n${model.model_name} (v${model.version})`);
|
|
326
|
+
this.logInfo(` Type: ${model.model_type}`);
|
|
327
|
+
this.logInfo(` Accuracy: ${model.accuracy}`);
|
|
328
|
+
this.logInfo(` Deployed: ${model.deployed ? 'Yes' : 'No'}`);
|
|
329
|
+
this.logInfo(` Created: ${created}`);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
this.logInfo('Use --list to manage ML models');
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
// ML Features
|
|
338
|
+
this.addSubcommand(supabaseCmd, {
|
|
339
|
+
name: 'ml-features',
|
|
340
|
+
description: 'Manage ML feature definitions',
|
|
341
|
+
options: [
|
|
342
|
+
{ flags: '-l, --list', description: 'List feature definitions', defaultValue: false }
|
|
343
|
+
],
|
|
344
|
+
action: async (options) => {
|
|
345
|
+
if (options.list) {
|
|
346
|
+
const { data: features, error } = await supabaseClient.getClient()
|
|
347
|
+
.from('ml_features')
|
|
348
|
+
.select('*')
|
|
349
|
+
.order('created_at', { ascending: false })
|
|
350
|
+
.limit(20);
|
|
351
|
+
if (error) {
|
|
352
|
+
throw new Error(`Failed to fetch features: ${error.message}`);
|
|
353
|
+
}
|
|
354
|
+
this.logInfo(`ML Features (${features?.length || 0}):`);
|
|
355
|
+
features?.forEach(feature => {
|
|
356
|
+
this.logInfo(`\n${feature.feature_name}`);
|
|
357
|
+
this.logInfo(` Type: ${feature.feature_type}`);
|
|
358
|
+
this.logInfo(` Importance: ${feature.importance_score}`);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
this.logInfo('Use --list to manage ML features');
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supabase Service - CLI command registration
|
|
3
|
+
* Uses SupabaseCommandRegistrar for clean, maintainable command setup
|
|
4
|
+
*/
|
|
5
|
+
import { SupabaseCommandRegistrar } from './supabase-registrar.js';
|
|
6
|
+
export async function init_supabase(program) {
|
|
7
|
+
const registrar = new SupabaseCommandRegistrar();
|
|
8
|
+
await registrar.register(program);
|
|
9
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
export async function test() {
|
|
3
|
+
const options = {
|
|
4
|
+
method: 'POST',
|
|
5
|
+
url: 'https://stoplight.io/mocks/zapier/public-api/181772442/authentications',
|
|
6
|
+
headers: { 'Content-Type': 'application/vnd.api+json', Accept: 'application/vnd.api+json' },
|
|
7
|
+
data: '{\n "data": {\n "title": "SuperExampleCRM (example@zapier.com)",\n "app": "868f9d3c-2ea0-4f19-a32d-a61b276ab8de",\n "authentication_fields": {}\n }\n}'
|
|
8
|
+
};
|
|
9
|
+
try {
|
|
10
|
+
const { data } = await axios.request(options);
|
|
11
|
+
console.log(data);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
console.error(error);
|
|
15
|
+
}
|
|
16
|
+
}
|