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.
@@ -0,0 +1,406 @@
1
+ // WebContainer-compatible CLI Service
2
+ // This is a simplified version of the CLI service that works in WebContainers
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { planets, signs } = require('../astrology/astrologyConstants');
7
+ const astrologyService = require('../astrology/astrologyServiceWeb');
8
+
9
+ const SUPPORTED_OPTIONS = new Set(['c', 'rx', 'el', 's', 'status', 'people', 'setup', 'help', 'h']);
10
+ const CONFIG_PATH = '/home/astrocli/config.json';
11
+
12
+ function splitCommandArgs(commandString) {
13
+ const args = [];
14
+ let current = '';
15
+ let quoteChar = null;
16
+ let escaped = false;
17
+
18
+ for (let i = 0; i < commandString.length; i++) {
19
+ const char = commandString[i];
20
+
21
+ if (escaped) {
22
+ current += char;
23
+ escaped = false;
24
+ continue;
25
+ }
26
+
27
+ if (char === '\\') {
28
+ escaped = true;
29
+ continue;
30
+ }
31
+
32
+ if (quoteChar) {
33
+ if (char === quoteChar) {
34
+ quoteChar = null;
35
+ } else {
36
+ current += char;
37
+ }
38
+ continue;
39
+ }
40
+
41
+ if (char === '"' || char === "'") {
42
+ quoteChar = char;
43
+ continue;
44
+ }
45
+
46
+ if (/\s/.test(char)) {
47
+ if (current.length) {
48
+ args.push(current);
49
+ current = '';
50
+ }
51
+ continue;
52
+ }
53
+
54
+ current += char;
55
+ }
56
+
57
+ if (current.length) {
58
+ args.push(current);
59
+ }
60
+
61
+ return args;
62
+ }
63
+
64
+ class CLIServiceWeb {
65
+ constructor() {
66
+ this.astrologyService = astrologyService;
67
+ }
68
+
69
+ _loadConfig() {
70
+ try {
71
+ if (fs.existsSync(CONFIG_PATH)) {
72
+ const configData = fs.readFileSync(CONFIG_PATH, 'utf8');
73
+ return JSON.parse(configData);
74
+ }
75
+ } catch (error) {
76
+ console.error('Error loading configuration:', error.message);
77
+ }
78
+ return null;
79
+ }
80
+
81
+ _saveConfig(config) {
82
+ try {
83
+ const dir = path.posix.dirname(CONFIG_PATH);
84
+ if (!fs.existsSync(dir)) {
85
+ fs.mkdirSync(dir, { recursive: true });
86
+ }
87
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
88
+ return true;
89
+ } catch (error) {
90
+ console.error('Error saving configuration:', error.message);
91
+ return false;
92
+ }
93
+ }
94
+
95
+ _showConfigStatus() {
96
+ const config = this._loadConfig();
97
+
98
+ if (!config) {
99
+ console.log('Configuration status');
100
+ console.log('======================');
101
+ console.log('No configuration found.');
102
+ console.log('Use --setup in the GUI to configure your location and birth data.');
103
+ return { success: true, output: '' };
104
+ }
105
+
106
+ console.log('Configuration status');
107
+ console.log('======================\n');
108
+
109
+ if (config.currentLocation) {
110
+ console.log('Current location:');
111
+ console.log(`Name: ${config.currentLocation.name}, ${config.currentLocation.country}`);
112
+ console.log(`Coordinates: ${config.currentLocation.latitude.toFixed(4)}° latitude, ${config.currentLocation.longitude.toFixed(4)}° longitude`);
113
+ console.log(`Timezone: ${config.currentLocation.timezone}`);
114
+ } else {
115
+ console.log('Current location: Not configured');
116
+ }
117
+
118
+ console.log('\nBirth data:');
119
+ if (config.birthData) {
120
+ console.log(`Date: ${config.birthData.date}`);
121
+ console.log(`Time: ${config.birthData.time}`);
122
+
123
+ if (config.birthData.location) {
124
+ console.log(`Birth location: ${config.birthData.location.name}, ${config.birthData.location.country}`);
125
+ console.log(`Birth coordinates: ${config.birthData.location.latitude.toFixed(4)}° latitude, ${config.birthData.location.longitude.toFixed(4)}° longitude`);
126
+ } else {
127
+ console.log('Birth location: Not configured');
128
+ }
129
+ } else {
130
+ console.log('Birth data: Not configured');
131
+ }
132
+
133
+ if (config.people && Object.keys(config.people).length > 0) {
134
+ console.log('\nPerson data:');
135
+ console.log('================');
136
+ for (const [personId, personData] of Object.entries(config.people)) {
137
+ console.log(`\nPerson ${personId}:`);
138
+ console.log(` Date: ${personData.date}`);
139
+ console.log(` Time: ${personData.time}`);
140
+ if (personData.location) {
141
+ console.log(` Location: ${personData.location.name}, ${personData.location.country}`);
142
+ console.log(` Coordinates: ${personData.location.latitude.toFixed(4)}° latitude, ${personData.location.longitude.toFixed(4)}° longitude`);
143
+ }
144
+ }
145
+ }
146
+
147
+ console.log('\nSetup date:');
148
+ if (config.setupDate) {
149
+ console.log(`${new Date(config.setupDate).toLocaleString()}`);
150
+ } else {
151
+ console.log('Not available');
152
+ }
153
+
154
+ console.log('\nThis data is used for your personalized astrological calculations.');
155
+ return { success: true, output: '' };
156
+ }
157
+
158
+ _showPeople() {
159
+ const config = this._loadConfig();
160
+ if (!config || !config.people || Object.keys(config.people).length === 0) {
161
+ console.log('No people configured.');
162
+ return { success: true, output: '' };
163
+ }
164
+
165
+ console.log('Saved people:');
166
+ for (const personId of Object.keys(config.people)) {
167
+ console.log(`- ${personId}`);
168
+ }
169
+ return { success: true, output: '' };
170
+ }
171
+
172
+ // Main command handler
173
+ async handleCommand(planetArg, planet2Arg, options) {
174
+ // If planet2Arg is an object, it contains the options (commander behavior)
175
+ let planet2 = typeof planet2Arg === 'string' ? planet2Arg : null;
176
+ let actualOptions = typeof planet2Arg === 'object' ? planet2Arg : options;
177
+
178
+ // Ensure actualOptions is an object
179
+ if (!actualOptions) {
180
+ actualOptions = {};
181
+ }
182
+
183
+ // Capture output for GUI use
184
+ const output = [];
185
+ const originalConsoleLog = console.log;
186
+ const originalConsoleError = console.error;
187
+
188
+ // Override console methods to capture output
189
+ console.log = (...args) => {
190
+ output.push(args.join(' '));
191
+ };
192
+
193
+ console.error = (...args) => {
194
+ output.push(args.join(' '));
195
+ };
196
+
197
+ try {
198
+ if (actualOptions.help || actualOptions.h) {
199
+ console.log('Klio CLI (WebContainer)');
200
+ console.log('Usage: [planet] [options]');
201
+ console.log('');
202
+ console.log('Planets: sun, moon, mars, venus');
203
+ console.log('Options:');
204
+ console.log(' --help, -h Show this help');
205
+ console.log(' --c Critical degrees');
206
+ console.log(' --rx Retrograde planets');
207
+ console.log(' --el Element distribution');
208
+ console.log(' --s All planet positions');
209
+ console.log(' --status Show config');
210
+ console.log(' --people List people');
211
+ console.log(' --setup Setup (GUI only)');
212
+ return { success: true, output: output.join('\n') };
213
+ }
214
+
215
+ if (actualOptions.setup) {
216
+ console.log('Use --setup in the GUI to configure WebContainer settings.');
217
+ return { success: true, output: output.join('\n') };
218
+ }
219
+
220
+ if (actualOptions.status) {
221
+ this._showConfigStatus();
222
+ return { success: true, output: output.join('\n') };
223
+ }
224
+
225
+ if (actualOptions.people) {
226
+ this._showPeople();
227
+ return { success: true, output: output.join('\n') };
228
+ }
229
+
230
+ // Show critical planets if --c option is specified
231
+ if (actualOptions.c) {
232
+ const criticalPlanets = this.astrologyService.getCriticalPlanets();
233
+
234
+ if (criticalPlanets.length === 0) {
235
+ console.log('No planets on critical degrees found.');
236
+ return { success: true, output: output.join('\n') };
237
+ }
238
+
239
+ console.log('Critical degrees analysis:');
240
+ console.log('================================================================================');
241
+ console.log('| Planet | Sign | Degree | Type | Interpretation |');
242
+ console.log('================================================================================');
243
+
244
+ criticalPlanets.forEach(planet => {
245
+ const planetName = planet.name.charAt(0).toUpperCase() + planet.name.slice(1);
246
+ const sign = planet.sign.padEnd(10, ' ');
247
+ const degree = planet.degree.padEnd(5, ' ');
248
+ const criticalType = planet.criticalType.padEnd(20, ' ');
249
+ const interpretation = planet.interpretation.padEnd(30, ' ');
250
+ console.log(`| ${planetName.padEnd(8)} | ${sign} | ${degree} | ${criticalType} | ${interpretation} |`);
251
+ });
252
+
253
+ console.log('================================================================================');
254
+
255
+ return { success: true, output: output.join('\n') };
256
+ }
257
+
258
+ // Show retrograde planets if --rx option is specified
259
+ if (actualOptions.rx) {
260
+ const retrogradePlanets = this.astrologyService.getRetrogradePlanets();
261
+
262
+ if (retrogradePlanets.length === 0) {
263
+ console.log('No retrograde or stationary planets found.');
264
+ return { success: true, output: output.join('\n') };
265
+ }
266
+
267
+ console.log('Retrograde and stationary planets:');
268
+ console.log('================================================================================');
269
+ console.log('| Planet | Status |');
270
+ console.log('================================================================================');
271
+
272
+ retrogradePlanets.forEach(planet => {
273
+ const planetName = planet.name.charAt(0).toUpperCase() + planet.name.slice(1);
274
+ const status = planet.status.charAt(0).toUpperCase() + planet.status.slice(1);
275
+ console.log(`| ${planetName.padEnd(8)} | ${status.padEnd(10)} |`);
276
+ });
277
+
278
+ console.log('================================================================================');
279
+
280
+ return { success: true, output: output.join('\n') };
281
+ }
282
+
283
+ // Show element distribution if --el option is specified
284
+ if (actualOptions.el) {
285
+ const elementData = this.astrologyService.analyzeElementDistribution();
286
+
287
+ console.log('Element distribution:');
288
+ console.log('================================================================================');
289
+ console.log('| Element | Count |');
290
+ console.log('================================================================================');
291
+
292
+ for (const [element, count] of Object.entries(elementData.elementCounts)) {
293
+ console.log(`| ${element.padEnd(7)} | ${count.toString().padEnd(5)} |`);
294
+ }
295
+
296
+ console.log('================================================================================');
297
+
298
+ return { success: true, output: output.join('\n') };
299
+ }
300
+
301
+ // Show all planet positions if --s option is specified
302
+ if (actualOptions.s) {
303
+ const allPositions = this.astrologyService.getAllPlanetPositions();
304
+
305
+ console.log('Planet positions:');
306
+ console.log('================================================================================');
307
+ console.log('| Planet | Sign | Deg | Dignity | Element |');
308
+ console.log('================================================================================');
309
+
310
+ for (const [planetName, data] of Object.entries(allPositions)) {
311
+ const planetNameFormatted = planetName.charAt(0).toUpperCase() + planetName.slice(1);
312
+ const signFormatted = data.sign.padEnd(10, ' ');
313
+ const degreeFormatted = data.degreeInSign.padEnd(5, ' ');
314
+ const dignityFormatted = data.dignity.padEnd(12, ' ');
315
+ const elementFormatted = data.element.padEnd(9, ' ');
316
+
317
+ console.log(`| ${planetNameFormatted.padEnd(11)} | ${signFormatted} | ${degreeFormatted}° | ${dignityFormatted} | ${elementFormatted} |`);
318
+ }
319
+
320
+ console.log('================================================================================');
321
+
322
+ return { success: true, output: output.join('\n') };
323
+ }
324
+
325
+ // Show normal planet info as default
326
+ const planet = planetArg ? planetArg.toLowerCase() : 'moon';
327
+ const data = this.astrologyService.getAstrologicalData(planet);
328
+
329
+ console.log(`Astrological data for ${data.planet}:`);
330
+ console.log(`Sign: ${data.sign}`);
331
+ console.log(`Degree in sign: ${data.degreeInSign}°`);
332
+ console.log(`Dignity: ${data.dignity}`);
333
+ console.log(`Element: ${data.element}`);
334
+ console.log(`Decan: ${data.decan}`);
335
+ console.log(`Longitude: ${data.longitude}°`);
336
+
337
+ return { success: true, output: output.join('\n') };
338
+
339
+ } catch (error) {
340
+ console.error('Error executing command:', error.message);
341
+ return { success: false, output: output.join('\n') };
342
+ } finally {
343
+ // Restore original console methods
344
+ console.log = originalConsoleLog;
345
+ console.error = originalConsoleError;
346
+ }
347
+ }
348
+
349
+ // Method to execute a command programmatically
350
+ async executeCommand(commandString) {
351
+ // Parse the command string into arguments
352
+ const args = splitCommandArgs(commandString);
353
+
354
+ // Parse options manually
355
+ const options = {};
356
+ const positionalArgs = [];
357
+
358
+ for (let i = 0; i < args.length; i++) {
359
+ const arg = args[i];
360
+
361
+ if (arg.startsWith('--')) {
362
+ // Handle options
363
+ const optionName = arg.substring(2);
364
+ options[optionName] = true;
365
+ } else if (arg.startsWith('-')) {
366
+ // Handle short options
367
+ const optionName = arg.substring(1);
368
+ options[optionName] = true;
369
+ } else {
370
+ // Handle positional arguments
371
+ positionalArgs.push(arg);
372
+ }
373
+ }
374
+
375
+ const unsupportedOptions = Object.keys(options).filter((option) => !SUPPORTED_OPTIONS.has(option));
376
+ if (unsupportedOptions.length > 0) {
377
+ return {
378
+ success: false,
379
+ output: `Unsupported option(s) in WebContainer: ${unsupportedOptions.join(', ')}`
380
+ };
381
+ }
382
+
383
+ if (positionalArgs.length > 1) {
384
+ return {
385
+ success: false,
386
+ output: 'Multiple positional arguments are not supported in WebContainer.'
387
+ };
388
+ }
389
+
390
+ if (positionalArgs[0] && !planets[positionalArgs[0].toLowerCase()]) {
391
+ return {
392
+ success: false,
393
+ output: `Unsupported planet in WebContainer: ${positionalArgs[0]}`
394
+ };
395
+ }
396
+
397
+ // Create a new instance to handle the command
398
+ const tempService = new CLIServiceWeb();
399
+ const planetArg = positionalArgs[0] || null;
400
+ const planet2Arg = positionalArgs[1] || null;
401
+
402
+ return tempService.handleCommand(planetArg, planet2Arg, options);
403
+ }
404
+ }
405
+
406
+ module.exports = new CLIServiceWeb();
@@ -6,7 +6,10 @@ const readline = require('readline');
6
6
  const { parseAndFormatMarkdown } = require('../utils/markdownFormatter');
7
7
 
8
8
  // Function to determine cross-platform configuration path
9
- function getConfigPath() {
9
+ function getConfigPath(userId = null) {
10
+ if (process.env.KLIO_CONFIG_PATH) {
11
+ return process.env.KLIO_CONFIG_PATH;
12
+ }
10
13
  let configDir;
11
14
 
12
15
  // Determine configuration directory based on operating system
@@ -26,6 +29,15 @@ function getConfigPath() {
26
29
  fs.mkdirSync(configDir, { recursive: true });
27
30
  }
28
31
 
32
+ // For user-specific configs, create user directory
33
+ if (userId) {
34
+ const userDir = path.join(configDir, 'users', userId);
35
+ if (!fs.existsSync(userDir)) {
36
+ fs.mkdirSync(userDir, { recursive: true });
37
+ }
38
+ return path.join(userDir, 'config.json');
39
+ }
40
+
29
41
  return path.join(configDir, 'config.json');
30
42
  }
31
43
 
@@ -82,9 +94,9 @@ async function geocodeLocation(locationName) {
82
94
  }
83
95
 
84
96
  // Function to save configuration
85
- function saveConfig(config) {
97
+ function saveConfig(config, userId = null) {
86
98
  try {
87
- const configPath = getConfigPath();
99
+ const configPath = getConfigPath(userId);
88
100
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
89
101
  console.log('✓ Configuration successfully saved!');
90
102
  console.log(`📁 Location: ${configPath}`);
@@ -149,7 +161,7 @@ async function parsePersonData(personString) {
149
161
  }
150
162
 
151
163
  // Function to create/set any person
152
- async function setPerson(personId, personString) {
164
+ async function setPerson(personId, personString, userId = null) {
153
165
  const personData = await parsePersonData(personString);
154
166
 
155
167
  if (!personData) {
@@ -157,7 +169,7 @@ async function setPerson(personId, personString) {
157
169
  return false;
158
170
  }
159
171
 
160
- const config = loadConfig() || {
172
+ const config = loadConfig(userId) || {
161
173
  currentLocation: null,
162
174
  birthData: null,
163
175
  setupDate: new Date().toISOString()
@@ -172,12 +184,12 @@ async function setPerson(personId, personString) {
172
184
  config.people[personId] = personData;
173
185
 
174
186
  console.log(`✓ Person "${personId}" successfully created/updated!`);
175
- return saveConfig(config);
187
+ return saveConfig(config, userId);
176
188
  }
177
189
 
178
190
  // Function to load data for a specific person
179
- function getPersonData(personId) {
180
- const config = loadConfig();
191
+ function getPersonData(personId, userId = null) {
192
+ const config = loadConfig(userId);
181
193
 
182
194
  if (!config || !config.people || !config.people[personId]) {
183
195
  return null;
@@ -205,8 +217,8 @@ function getPersonData(personId) {
205
217
  }
206
218
 
207
219
  // Function to list all people
208
- function listPeople() {
209
- const config = loadConfig();
220
+ function listPeople(userId = null) {
221
+ const config = loadConfig(userId);
210
222
 
211
223
  if (!config || !config.people || Object.keys(config.people).length === 0) {
212
224
  console.log('No people found in configuration.');
@@ -232,8 +244,8 @@ function listPeople() {
232
244
  }
233
245
 
234
246
  // Function to delete a person
235
- function deletePerson(personId) {
236
- const config = loadConfig();
247
+ function deletePerson(personId, userId = null) {
248
+ const config = loadConfig(userId);
237
249
 
238
250
  if (!config || !config.people || !config.people[personId]) {
239
251
  console.error(`Person "${personId}" not found.`);
@@ -249,26 +261,36 @@ function deletePerson(personId) {
249
261
  }
250
262
 
251
263
  console.log(`✓ Person "${personId}" successfully deleted!`);
252
- return saveConfig(config);
264
+ return saveConfig(config, userId);
253
265
  }
254
266
 
255
267
  // Function to create/set person 1 (for backward compatibility)
256
- async function setPerson1(personString) {
257
- return await setPerson('p1', personString);
268
+ async function setPerson1(personString, userId = null) {
269
+ return await setPerson('p1', personString, userId);
258
270
  }
259
271
 
260
272
  // Function to create/set person 2 (for backward compatibility)
261
- async function setPerson2(personString) {
262
- return await setPerson('p2', personString);
273
+ async function setPerson2(personString, userId = null) {
274
+ return await setPerson('p2', personString, userId);
263
275
  }
264
276
 
265
277
  // Function to load configuration
266
- function loadConfig() {
278
+ function loadConfig(userId = null) {
267
279
  try {
268
- // Try to migrate old configuration first
269
- migrateOldConfig();
280
+ if (process.env.KLIO_CONFIG_JSON) {
281
+ try {
282
+ return JSON.parse(process.env.KLIO_CONFIG_JSON);
283
+ } catch (error) {
284
+ console.error('Error parsing KLIO_CONFIG_JSON:', error.message);
285
+ }
286
+ }
287
+
288
+ // Try to migrate old configuration first (only for global config)
289
+ if (!userId) {
290
+ migrateOldConfig();
291
+ }
270
292
 
271
- const configPath = getConfigPath();
293
+ const configPath = getConfigPath(userId);
272
294
  if (fs.existsSync(configPath)) {
273
295
  const configData = fs.readFileSync(configPath, 'utf8');
274
296
  return JSON.parse(configData);
@@ -280,8 +302,8 @@ function loadConfig() {
280
302
  }
281
303
 
282
304
  // Function to directly set the AI model
283
- function setAIModel(modelName) {
284
- const config = loadConfig() || {
305
+ function setAIModel(modelName, userId = null) {
306
+ const config = loadConfig(userId) || {
285
307
  currentLocation: null,
286
308
  birthData: null,
287
309
  setupDate: new Date().toISOString()
@@ -297,7 +319,7 @@ function setAIModel(modelName) {
297
319
  apiKey: null
298
320
  };
299
321
 
300
- if (saveConfig(config)) {
322
+ if (saveConfig(config, userId)) {
301
323
  console.log(`AI model successfully set: ${modelName}`);
302
324
  console.log(`Endpoint: ${endpoint}`);
303
325
  console.log(`Provider: lm-studio`);
@@ -307,8 +329,8 @@ function setAIModel(modelName) {
307
329
  }
308
330
 
309
331
  // Function to set a custom system prompt
310
- function setSystemPrompt(prompt) {
311
- const config = loadConfig() || {
332
+ function setSystemPrompt(prompt, userId = null) {
333
+ const config = loadConfig(userId) || {
312
334
  currentLocation: null,
313
335
  birthData: null,
314
336
  setupDate: new Date().toISOString()
@@ -327,7 +349,7 @@ function setSystemPrompt(prompt) {
327
349
  // Set the custom system prompt
328
350
  config.aiConfiguration.systemPrompt = prompt;
329
351
 
330
- if (saveConfig(config)) {
352
+ if (saveConfig(config, userId)) {
331
353
  console.log(`System prompt successfully set: "${prompt}"`);
332
354
  console.log('This prompt will be used for all future AI requests.');
333
355
  return true;
@@ -336,8 +358,8 @@ function setSystemPrompt(prompt) {
336
358
  }
337
359
 
338
360
  // Function to display saved configuration
339
- function showConfigStatus() {
340
- const config = loadConfig();
361
+ function showConfigStatus(userId = null) {
362
+ const config = loadConfig(userId);
341
363
 
342
364
  if (!config) {
343
365
  console.log('Configuration status');
@@ -410,7 +432,7 @@ function showConfigStatus() {
410
432
  }
411
433
 
412
434
  // Setup function for location and birth data
413
- async function performSetup() {
435
+ async function performSetup(userId = null) {
414
436
  console.log('AstroCLI Setup Assistant');
415
437
  console.log('=========================\n');
416
438
 
@@ -547,7 +569,7 @@ async function performSetup() {
547
569
  };
548
570
 
549
571
  // Save and confirmation
550
- if (saveConfig(config)) {
572
+ if (saveConfig(config, userId)) {
551
573
  console.log('\nPerfect! Your setup is complete!');
552
574
  console.log('\nYour configuration:');
553
575
  console.log(`- Your location: ${config.currentLocation.name}, ${config.currentLocation.country}`);
@@ -564,8 +586,8 @@ async function performSetup() {
564
586
  }
565
587
 
566
588
  // Function to send a question to the AI model
567
- async function askAIModel(prompt, planetData, houseInfo = null) {
568
- const config = loadConfig();
589
+ async function askAIModel(prompt, planetData, houseInfo = null, userId = null) {
590
+ const config = loadConfig(userId);
569
591
 
570
592
  if (!config || !config.aiConfiguration) {
571
593
  console.log('❌ No AI model configured. Use --setup -ai to configure an AI model.');
@@ -746,5 +768,7 @@ module.exports = {
746
768
  setPerson,
747
769
  getPersonData,
748
770
  listPeople,
749
- deletePerson
750
- };
771
+ deletePerson,
772
+ // Add helper functions for user-specific operations
773
+ getConfigPath
774
+ };