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/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 commandDatabase = require('./database');
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
- this.port = 37421; // Less common port
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
- // Initialize database
15
- await commandDatabase.initialize();
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 for all other routes (for SPA behavior)
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
- console.log(`🌐 GUI Server running on http://localhost:${port}`);
43
- console.log(`📊 Command history available at http://localhost:${port}`);
44
- console.log('💡 Press Ctrl+C to stop the server');
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
- async stop() {
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
- getPort() {
74
- if (this.server) {
75
- const address = this.server.address();
76
- return address && address.port;
77
- }
78
- return this.port;
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('swisseph');
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
@@ -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();
@@ -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();