klio 1.4.9 → 1.5.1
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/.dockerignore +9 -0
- package/.nvmrc +1 -0
- package/Dockerfile +18 -0
- package/README.md +22 -0
- package/compose.yaml +6 -0
- package/package.json +8 -3
- package/src/astrology/astrologyConstants.js +7 -0
- package/src/astrology/astrologyService.js +327 -302
- package/src/astrology/astrologyServiceWeb.js +369 -0
- package/src/astrology/swephWasmLoader.js +106 -0
- package/src/astrology/swissephAdapter.js +279 -0
- package/src/cli/cli.js +148 -152
- package/src/cli/cliService.js +1197 -0
- package/src/cli/cliServiceWeb.js +406 -0
- package/src/config/configService.js +59 -35
- package/src/gui/public/index.html +839 -298
- package/src/gui/public/sweph/astro.data +0 -0
- package/src/gui/public/sweph/astro.js +3934 -0
- package/src/gui/public/sweph/astro.wasm +0 -0
- package/src/gui/public/tailwind.css +3 -0
- package/src/gui/public/tailwind.generated.css +1 -0
- package/src/gui/public/webcontainerService.js +435 -0
- package/src/gui/routes/api.js +64 -101
- package/src/gui/server.js +80 -31
- package/src/gui/webcontainerSetup.js +244 -0
- package/src/health/fileAnalysis.js +2 -2
- package/commands.db +0 -0
- package/src/gui/commandLogger.js +0 -67
- package/src/gui/database.js +0 -135
package/src/gui/server.js
CHANGED
|
@@ -1,30 +1,80 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const apiRouter = require('./routes/api');
|
|
4
|
-
const
|
|
4
|
+
const session = require('express-session');
|
|
5
|
+
const FileStore = require('session-file-store')(session);
|
|
5
6
|
|
|
6
7
|
class GUIServer {
|
|
7
8
|
constructor() {
|
|
8
9
|
this.app = express();
|
|
9
10
|
this.server = null;
|
|
10
|
-
|
|
11
|
+
const envPort = parseInt(process.env.PORT, 10);
|
|
12
|
+
this.port = Number.isFinite(envPort) ? envPort : 37421; // Less common port
|
|
13
|
+
this.verboseLogging = false; // Default to minimal logging
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
async initialize() {
|
|
14
|
-
//
|
|
15
|
-
|
|
17
|
+
// Session configuration
|
|
18
|
+
const sessionStore = new FileStore();
|
|
19
|
+
|
|
20
|
+
this.app.use(session({
|
|
21
|
+
secret: 'astrocli-secret-key-change-in-production',
|
|
22
|
+
resave: false,
|
|
23
|
+
saveUninitialized: true,
|
|
24
|
+
store: sessionStore,
|
|
25
|
+
cookie: {
|
|
26
|
+
secure: false, // Set to true if using HTTPS
|
|
27
|
+
maxAge: 24 * 60 * 60 * 1000, // 1 day
|
|
28
|
+
httpOnly: true
|
|
29
|
+
}
|
|
30
|
+
}));
|
|
16
31
|
|
|
17
32
|
// Middleware
|
|
18
33
|
this.app.use(express.json());
|
|
19
34
|
this.app.use(express.urlencoded({ extended: true }));
|
|
20
35
|
|
|
36
|
+
// Session middleware to set default user
|
|
37
|
+
this.app.use((req, res, next) => {
|
|
38
|
+
if (!req.session.userId) {
|
|
39
|
+
// Generate a unique user ID for new sessions
|
|
40
|
+
req.session.userId = 'user-' + Math.random().toString(36).substr(2, 9);
|
|
41
|
+
if (this.verboseLogging) {
|
|
42
|
+
console.log(`👤 New user session created: ${req.session.userId}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
next();
|
|
46
|
+
});
|
|
47
|
+
|
|
21
48
|
// API routes
|
|
22
49
|
this.app.use('/api', apiRouter);
|
|
23
50
|
|
|
51
|
+
// Add security headers for cross-origin isolation (required for WebContainers)
|
|
52
|
+
this.app.use((req, res, next) => {
|
|
53
|
+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
|
|
54
|
+
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
|
|
55
|
+
|
|
56
|
+
// Add debug header to verify headers are being set
|
|
57
|
+
if (this.verboseLogging) {
|
|
58
|
+
console.log('🔒 Setting cross-origin isolation headers');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Add warning about HTTPS requirement
|
|
62
|
+
if (this.verboseLogging && req.protocol !== 'https') {
|
|
63
|
+
console.warn('⚠️ Cross-origin isolation headers require HTTPS in production. For development, use localhost or configure HTTPS.');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
next();
|
|
67
|
+
});
|
|
68
|
+
|
|
24
69
|
// Serve static files from public directory
|
|
25
70
|
this.app.use(express.static(path.join(__dirname, 'public')));
|
|
26
71
|
|
|
27
|
-
// Serve index.html
|
|
72
|
+
// Serve index.html as the main interface
|
|
73
|
+
this.app.get('/', (req, res) => {
|
|
74
|
+
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Serve index.html for other routes (for backward compatibility)
|
|
28
78
|
this.app.get('*', (req, res) => {
|
|
29
79
|
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
|
30
80
|
});
|
|
@@ -36,12 +86,23 @@ class GUIServer {
|
|
|
36
86
|
});
|
|
37
87
|
}
|
|
38
88
|
|
|
89
|
+
// Method to enable verbose logging
|
|
90
|
+
enableVerboseLogging(enable = true) {
|
|
91
|
+
this.verboseLogging = enable;
|
|
92
|
+
}
|
|
93
|
+
|
|
39
94
|
start(port = this.port) {
|
|
40
95
|
return new Promise((resolve, reject) => {
|
|
41
|
-
this.server = this.app.listen(port, () => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
96
|
+
this.server = this.app.listen(port, '0.0.0.0', () => {
|
|
97
|
+
if (this.verboseLogging) {
|
|
98
|
+
console.log(`🌐 GUI Server running on http://localhost:${port}`);
|
|
99
|
+
console.log(`📊 Command history available at http://localhost:${port}`);
|
|
100
|
+
console.log('💡 Press Ctrl+C to stop the server');
|
|
101
|
+
} else {
|
|
102
|
+
console.log(`🌐 GUI Server started successfully!`);
|
|
103
|
+
console.log(`📊 Open your browser and navigate to: http://localhost:${port}`);
|
|
104
|
+
console.log(`💡 Press Ctrl+C to stop the server and return to CLI`);
|
|
105
|
+
}
|
|
45
106
|
resolve();
|
|
46
107
|
});
|
|
47
108
|
|
|
@@ -55,28 +116,16 @@ class GUIServer {
|
|
|
55
116
|
});
|
|
56
117
|
});
|
|
57
118
|
}
|
|
119
|
+
}
|
|
58
120
|
|
|
59
|
-
|
|
60
|
-
return new Promise((resolve, reject) => {
|
|
61
|
-
if (this.server) {
|
|
62
|
-
this.server.close(async () => {
|
|
63
|
-
await commandDatabase.close();
|
|
64
|
-
console.log('GUI Server stopped');
|
|
65
|
-
resolve();
|
|
66
|
-
});
|
|
67
|
-
} else {
|
|
68
|
-
resolve();
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
}
|
|
121
|
+
module.exports = new GUIServer();
|
|
72
122
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
123
|
+
if (require.main === module) {
|
|
124
|
+
const server = module.exports;
|
|
125
|
+
server.initialize()
|
|
126
|
+
.then(() => server.start())
|
|
127
|
+
.catch((err) => {
|
|
128
|
+
console.error('Failed to start GUI server:', err);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
});
|
|
80
131
|
}
|
|
81
|
-
|
|
82
|
-
module.exports = new GUIServer();
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// WebContainer Setup Module
|
|
2
|
+
// This module provides server-side functionality for WebContainer file management
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
class WebContainerSetup {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.projectRoot = path.resolve(__dirname, '..', '..');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Get project files for WebContainer
|
|
13
|
+
async getProjectFiles() {
|
|
14
|
+
try {
|
|
15
|
+
const files = {};
|
|
16
|
+
|
|
17
|
+
// Read package.json
|
|
18
|
+
const packageJsonPath = path.join(this.projectRoot, 'package.json');
|
|
19
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
20
|
+
const rawPackageJson = fs.readFileSync(packageJsonPath, 'utf8');
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(rawPackageJson);
|
|
23
|
+
const minimalDependencies = {
|
|
24
|
+
commander: '^14.0.3',
|
|
25
|
+
'moment-timezone': '^0.6.0',
|
|
26
|
+
axios: '^1.13.4',
|
|
27
|
+
'csv-parser': '^3.0.0',
|
|
28
|
+
'csv-parse': '^5.5.6',
|
|
29
|
+
'fast-xml-parser': '^5.3.4'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const webPackageJson = {
|
|
33
|
+
name: parsed.name || 'klio-web',
|
|
34
|
+
version: parsed.version || '0.0.0',
|
|
35
|
+
description: parsed.description || 'Klio WebContainer runtime',
|
|
36
|
+
type: parsed.type || 'commonjs',
|
|
37
|
+
dependencies: minimalDependencies
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
files['package.json'] = JSON.stringify(webPackageJson, null, 2);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
files['package.json'] = rawPackageJson;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Read main entry file
|
|
47
|
+
const mainFilePath = path.join(this.projectRoot, 'index.js');
|
|
48
|
+
if (fs.existsSync(mainFilePath)) {
|
|
49
|
+
files['index.js'] = fs.readFileSync(mainFilePath, 'utf8');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Read src directory files
|
|
53
|
+
const srcDir = path.join(this.projectRoot, 'src');
|
|
54
|
+
if (fs.existsSync(srcDir)) {
|
|
55
|
+
this._readDirectory(srcDir, 'src/', files);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Read Sweph WASM assets
|
|
59
|
+
const swephDir = path.join(this.projectRoot, 'src', 'gui', 'public', 'sweph');
|
|
60
|
+
if (fs.existsSync(swephDir)) {
|
|
61
|
+
this._readDirectory(swephDir, 'src/gui/public/sweph/', files);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Create webcontainer-entry.js for command execution
|
|
65
|
+
files['webcontainer-entry.js'] = this._createWebContainerEntry();
|
|
66
|
+
|
|
67
|
+
return files;
|
|
68
|
+
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('Error getting project files:', error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Read directory recursively
|
|
76
|
+
_readDirectory(dirPath, relativePath, files) {
|
|
77
|
+
const items = fs.readdirSync(dirPath);
|
|
78
|
+
|
|
79
|
+
for (const item of items) {
|
|
80
|
+
const fullPath = path.join(dirPath, item);
|
|
81
|
+
const itemRelativePath = path.join(relativePath, item);
|
|
82
|
+
|
|
83
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
84
|
+
// Skip node_modules and other large directories
|
|
85
|
+
if (item === 'node_modules' || item === '.git' || item === 'ephe') {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
this._readDirectory(fullPath, itemRelativePath, files);
|
|
89
|
+
} else {
|
|
90
|
+
// Skip large files and binary files
|
|
91
|
+
if (item.endsWith('.db') || item.endsWith('.json') && item !== 'package.json') {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
if (item.endsWith('.wasm') || item.endsWith('.data')) {
|
|
97
|
+
const content = fs.readFileSync(fullPath);
|
|
98
|
+
files[itemRelativePath] = {
|
|
99
|
+
encoding: 'base64',
|
|
100
|
+
contents: content.toString('base64')
|
|
101
|
+
};
|
|
102
|
+
} else {
|
|
103
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
104
|
+
files[itemRelativePath] = content;
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.warn(`Skipping file ${itemRelativePath}: ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Create WebContainer entry file
|
|
114
|
+
_createWebContainerEntry() {
|
|
115
|
+
return `// WebContainer Entry Point
|
|
116
|
+
// Executes CLI commands using the WebContainer-compatible service.
|
|
117
|
+
|
|
118
|
+
const fs = require('fs');
|
|
119
|
+
const path = require('path');
|
|
120
|
+
|
|
121
|
+
const findFile = (startDir, target, maxDepth = 5) => {
|
|
122
|
+
if (maxDepth < 0) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let entries;
|
|
127
|
+
try {
|
|
128
|
+
entries = fs.readdirSync(startDir, { withFileTypes: true });
|
|
129
|
+
} catch (error) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
for (const entry of entries) {
|
|
134
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('.')) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const fullPath = path.join(startDir, entry.name);
|
|
139
|
+
if (entry.isDirectory()) {
|
|
140
|
+
const found = findFile(fullPath, target, maxDepth - 1);
|
|
141
|
+
if (found) {
|
|
142
|
+
return found;
|
|
143
|
+
}
|
|
144
|
+
} else if (entry.isFile() && entry.name === target) {
|
|
145
|
+
return fullPath;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return null;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const loadCliService = () => {
|
|
153
|
+
const candidates = [
|
|
154
|
+
path.resolve(process.cwd(), 'src', 'cli', 'cliService.js'),
|
|
155
|
+
path.resolve(process.cwd(), 'cli', 'cliService.js'),
|
|
156
|
+
path.resolve(__dirname, 'src', 'cli', 'cliService.js'),
|
|
157
|
+
'/src/cli/cliService.js',
|
|
158
|
+
path.resolve(__dirname, 'src', 'cli', 'cliService'),
|
|
159
|
+
path.resolve(__dirname, 'cli', 'cliService.js'),
|
|
160
|
+
'/cli/cliService.js'
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
for (const candidate of candidates) {
|
|
164
|
+
if (fs.existsSync(candidate)) {
|
|
165
|
+
return require(candidate);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const searched = findFile(process.cwd(), 'cliService.js');
|
|
170
|
+
if (searched) {
|
|
171
|
+
return require(searched);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
throw new Error('CLI service not found. Checked: ' + candidates.join(', ') + '. Searched from: ' + process.cwd());
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const loadSwephAdapter = () => {
|
|
178
|
+
const candidates = [
|
|
179
|
+
path.resolve(process.cwd(), 'src', 'astrology', 'swissephAdapter.js'),
|
|
180
|
+
path.resolve(__dirname, 'src', 'astrology', 'swissephAdapter.js'),
|
|
181
|
+
'/src/astrology/swissephAdapter.js'
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
for (const candidate of candidates) {
|
|
185
|
+
if (fs.existsSync(candidate)) {
|
|
186
|
+
return require(candidate);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const searched = findFile(process.cwd(), 'swissephAdapter.js');
|
|
191
|
+
if (searched) {
|
|
192
|
+
return require(searched);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
throw new Error('Sweph adapter not found. Checked: ' + candidates.join(', ') + '. Searched from: ' + process.cwd());
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const cliService = loadCliService();
|
|
199
|
+
const swephAdapter = loadSwephAdapter();
|
|
200
|
+
|
|
201
|
+
const rawCommand = process.argv.slice(2).join(' ').trim();
|
|
202
|
+
|
|
203
|
+
if (!rawCommand) {
|
|
204
|
+
console.error('No command provided');
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const command = rawCommand.startsWith('klio ') ? rawCommand.slice(5).trim() : rawCommand;
|
|
209
|
+
|
|
210
|
+
process.env.KLIO_CAPTURE_ONLY = '1';
|
|
211
|
+
process.env.KLIO_CONFIG_PATH = '/src/.astrocli/config.json';
|
|
212
|
+
try {
|
|
213
|
+
const configText = fs.readFileSync(process.env.KLIO_CONFIG_PATH, 'utf8');
|
|
214
|
+
if (configText) {
|
|
215
|
+
process.env.KLIO_CONFIG_JSON = configText;
|
|
216
|
+
}
|
|
217
|
+
} catch (error) {
|
|
218
|
+
// Config file may not exist yet.
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
swephAdapter.initialize().then(() => cliService.executeCommand(command)).then((result) => {
|
|
222
|
+
if (!result || !result.success) {
|
|
223
|
+
const message = result && result.output ? result.output : 'Command failed in WebContainer';
|
|
224
|
+
console.error(message);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (result.output) {
|
|
229
|
+
const output = result.output.endsWith('\\n') ? result.output : result.output + '\\n';
|
|
230
|
+
process.stdout.write(output);
|
|
231
|
+
}
|
|
232
|
+
}).catch((error) => {
|
|
233
|
+
const message = error && error.stack ? error.stack : (error && error.message ? error.message : String(error));
|
|
234
|
+
console.error(message);
|
|
235
|
+
process.exit(1);
|
|
236
|
+
});
|
|
237
|
+
`;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Singleton instance
|
|
242
|
+
const webcontainerSetup = new WebContainerSetup();
|
|
243
|
+
|
|
244
|
+
module.exports = webcontainerSetup;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { getAstrologicalData, calculateHouses, getPlanetHouse } = require('../astrology/astrologyService');
|
|
2
2
|
const { readFilesInFolder, getFileCreationDate, parseDateToComponents } = require('../utils/fileUtils');
|
|
3
|
-
const swisseph = require('
|
|
3
|
+
const swisseph = require('../astrology/swissephAdapter');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
6
|
// Function to analyze house distribution for a planet in a folder
|
|
@@ -150,4 +150,4 @@ async function filterFilesByHouse(planetName, folderPath, targetHouse, houseSyst
|
|
|
150
150
|
module.exports = {
|
|
151
151
|
analyzeHouseDistribution,
|
|
152
152
|
filterFilesByHouse
|
|
153
|
-
};
|
|
153
|
+
};
|
package/commands.db
DELETED
|
Binary file
|
package/src/gui/commandLogger.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
const commandDatabase = require('./database');
|
|
2
|
-
|
|
3
|
-
class CommandLogger {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.isInitialized = false;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
async initialize() {
|
|
9
|
-
try {
|
|
10
|
-
await commandDatabase.initialize();
|
|
11
|
-
this.isInitialized = true;
|
|
12
|
-
} catch (err) {
|
|
13
|
-
console.error('Failed to initialize command logger:', err);
|
|
14
|
-
this.isInitialized = false;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async logCommand(command, arguments = '', category = 'general', tags = '') {
|
|
19
|
-
if (!this.isInitialized) {
|
|
20
|
-
await this.initialize();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (!this.isInitialized) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
// Capture the command result by temporarily overriding console.log
|
|
29
|
-
const originalLog = console.log;
|
|
30
|
-
let capturedOutput = '';
|
|
31
|
-
|
|
32
|
-
console.log = (...args) => {
|
|
33
|
-
capturedOutput += args.join(' ') + '\n';
|
|
34
|
-
originalLog(...args);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Execute the command (this would be called from CLI)
|
|
38
|
-
// For now, we'll just save the command structure
|
|
39
|
-
|
|
40
|
-
const commandId = await commandDatabase.saveCommand(
|
|
41
|
-
command,
|
|
42
|
-
arguments,
|
|
43
|
-
'', // Result will be captured later
|
|
44
|
-
tags,
|
|
45
|
-
category
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
// Restore original console.log
|
|
49
|
-
console.log = originalLog;
|
|
50
|
-
|
|
51
|
-
return commandId;
|
|
52
|
-
} catch (err) {
|
|
53
|
-
console.error('Error logging command:', err);
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async close() {
|
|
59
|
-
try {
|
|
60
|
-
await commandDatabase.close();
|
|
61
|
-
} catch (err) {
|
|
62
|
-
console.error('Error closing command logger:', err);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
module.exports = new CommandLogger();
|
package/src/gui/database.js
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
const sqlite3 = require('sqlite3').verbose();
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
|
|
5
|
-
class CommandDatabase {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.dbPath = path.join(__dirname, '..', '..', 'commands.db');
|
|
8
|
-
this.db = null;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async initialize() {
|
|
12
|
-
// Create directory if it doesn't exist
|
|
13
|
-
const dbDir = path.dirname(this.dbPath);
|
|
14
|
-
if (!fs.existsSync(dbDir)) {
|
|
15
|
-
fs.mkdirSync(dbDir, { recursive: true });
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
this.db = new sqlite3.Database(this.dbPath);
|
|
19
|
-
|
|
20
|
-
return new Promise((resolve, reject) => {
|
|
21
|
-
this.db.serialize(() => {
|
|
22
|
-
this.db.run(`
|
|
23
|
-
CREATE TABLE IF NOT EXISTS commands (
|
|
24
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
25
|
-
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
26
|
-
command TEXT NOT NULL,
|
|
27
|
-
arguments TEXT,
|
|
28
|
-
result TEXT,
|
|
29
|
-
tags TEXT,
|
|
30
|
-
category TEXT
|
|
31
|
-
)
|
|
32
|
-
`);
|
|
33
|
-
|
|
34
|
-
// Create indexes for better search performance
|
|
35
|
-
this.db.run('CREATE INDEX IF NOT EXISTS idx_command ON commands(command)');
|
|
36
|
-
this.db.run('CREATE INDEX IF NOT EXISTS idx_timestamp ON commands(timestamp)');
|
|
37
|
-
this.db.run('CREATE INDEX IF NOT EXISTS idx_tags ON commands(tags)');
|
|
38
|
-
this.db.run('CREATE INDEX IF NOT EXISTS idx_category ON commands(category)');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
resolve();
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async saveCommand(command, args = '', result = '', tags = '', category = '') {
|
|
46
|
-
return new Promise((resolve, reject) => {
|
|
47
|
-
const stmt = this.db.prepare(
|
|
48
|
-
'INSERT INTO commands (command, arguments, result, tags, category) VALUES (?, ?, ?, ?, ?)'
|
|
49
|
-
);
|
|
50
|
-
stmt.run([command, args, result, tags, category], function(err) {
|
|
51
|
-
if (err) {
|
|
52
|
-
reject(err);
|
|
53
|
-
} else {
|
|
54
|
-
resolve(this.lastID);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
stmt.finalize();
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async getAllCommands() {
|
|
62
|
-
return new Promise((resolve, reject) => {
|
|
63
|
-
this.db.all('SELECT * FROM commands ORDER BY timestamp DESC', (err, rows) => {
|
|
64
|
-
if (err) {
|
|
65
|
-
reject(err);
|
|
66
|
-
} else {
|
|
67
|
-
resolve(rows);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async searchCommands(query) {
|
|
74
|
-
return new Promise((resolve, reject) => {
|
|
75
|
-
const searchQuery = `%${query}%`;
|
|
76
|
-
this.db.all(`
|
|
77
|
-
SELECT * FROM commands
|
|
78
|
-
WHERE command LIKE ?
|
|
79
|
-
OR arguments LIKE ?
|
|
80
|
-
OR result LIKE ?
|
|
81
|
-
OR tags LIKE ?
|
|
82
|
-
OR category LIKE ?
|
|
83
|
-
ORDER BY timestamp DESC
|
|
84
|
-
`, [searchQuery, searchQuery, searchQuery, searchQuery, searchQuery], (err, rows) => {
|
|
85
|
-
if (err) {
|
|
86
|
-
reject(err);
|
|
87
|
-
} else {
|
|
88
|
-
resolve(rows);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async getCommandById(id) {
|
|
95
|
-
return new Promise((resolve, reject) => {
|
|
96
|
-
this.db.get('SELECT * FROM commands WHERE id = ?', [id], (err, row) => {
|
|
97
|
-
if (err) {
|
|
98
|
-
reject(err);
|
|
99
|
-
} else {
|
|
100
|
-
resolve(row);
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async getCommandsByCategory(category) {
|
|
107
|
-
return new Promise((resolve, reject) => {
|
|
108
|
-
this.db.all('SELECT * FROM commands WHERE category = ? ORDER BY timestamp DESC', [category], (err, rows) => {
|
|
109
|
-
if (err) {
|
|
110
|
-
reject(err);
|
|
111
|
-
} else {
|
|
112
|
-
resolve(rows);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async close() {
|
|
119
|
-
return new Promise((resolve, reject) => {
|
|
120
|
-
if (this.db) {
|
|
121
|
-
this.db.close((err) => {
|
|
122
|
-
if (err) {
|
|
123
|
-
reject(err);
|
|
124
|
-
} else {
|
|
125
|
-
resolve();
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
} else {
|
|
129
|
-
resolve();
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
module.exports = new CommandDatabase();
|