klio 1.5.6 → 1.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/ephe/se00010s.se1 +0 -0
- package/package.json +1 -1
- package/src/astrology/astrologyConstants.js +29 -0
- package/src/astrology/astrologyService.js +216 -65
- package/src/cli/cli.js +75 -36
- package/src/cli/cliService.js +18 -1
- package/src/config/configService.js +50 -1
package/README.md
CHANGED
|
@@ -36,6 +36,25 @@ npm install -g klio
|
|
|
36
36
|
klio [options]
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
### Planet Selection
|
|
40
|
+
|
|
41
|
+
By default, Klio uses traditional planets plus Chiron. You can customize which planets and asteroids are active:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Show currently active planets
|
|
45
|
+
klio --active-planets
|
|
46
|
+
|
|
47
|
+
# Set custom active planets (comma-separated)
|
|
48
|
+
klio --planets "Sun,Moon,Mercury,Venus,Chiron,Hygiea"
|
|
49
|
+
|
|
50
|
+
klio --planets "Sun,Moon,Mercury,Venus,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Available planets: Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Chiron, Hygiea
|
|
54
|
+
|
|
55
|
+
> **Note:** By default, only Chiron is active among asteroids. Other asteroids like Hygiea must be explicitly enabled.
|
|
56
|
+
=======
|
|
57
|
+
|
|
39
58
|
### Web Interface
|
|
40
59
|
|
|
41
60
|
AstroCLI includes a web-based GUI that you can launch with the `--gui` flag:
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -13,6 +13,25 @@ const planets = {
|
|
|
13
13
|
chiron: 15
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
// Asteroid constants for Swiss Ephemeris
|
|
17
|
+
// Note: These use the SE_AST_OFFSET (10000) + asteroid number
|
|
18
|
+
const asteroidConstants = {
|
|
19
|
+
ceres: 10001, // Ceres = 10000 + 1
|
|
20
|
+
pallas: 10002, // Pallas = 10000 + 2
|
|
21
|
+
hygiea: 10010, // Hygiea = 10000 + 10
|
|
22
|
+
juno: 10003, // Juno = 10000 + 3
|
|
23
|
+
vesta: 10004 // Vesta = 10000 + 4
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Asteroid names for Swiss Ephemeris (used as string identifiers)
|
|
27
|
+
const asteroidNames = {
|
|
28
|
+
ceres: 'Ceres',
|
|
29
|
+
pallas: 'Pallas',
|
|
30
|
+
hygiea: 'Hygiea',
|
|
31
|
+
juno: 'Juno',
|
|
32
|
+
vesta: 'Vesta'
|
|
33
|
+
};
|
|
34
|
+
|
|
16
35
|
// Zodiac signs (English)
|
|
17
36
|
const signs = [
|
|
18
37
|
'Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo',
|
|
@@ -54,8 +73,18 @@ const dignities = {
|
|
|
54
73
|
15: { sign: 'Virgo', exaltation: 'Pisces', fall: 'Virgo', detriment: 'Pisces' } // Chiron
|
|
55
74
|
};
|
|
56
75
|
|
|
76
|
+
// Asteroid dignities (using asteroid constants as keys)
|
|
77
|
+
const asteroidDignities = {
|
|
78
|
+
10001: { sign: 'Taurus', exaltation: 'Virgo', fall: 'Scorpio', detriment: 'Scorpio' }, // Ceres
|
|
79
|
+
10002: { sign: 'Libra', exaltation: 'Aries', fall: 'Libra', detriment: 'Aries' }, // Pallas
|
|
80
|
+
10010: { sign: 'Virgo', exaltation: 'Pisces', fall: 'Virgo', detriment: 'Pisces' } // Hygiea
|
|
81
|
+
};
|
|
82
|
+
|
|
57
83
|
module.exports = {
|
|
58
84
|
planets,
|
|
85
|
+
asteroidNames,
|
|
86
|
+
asteroidConstants,
|
|
87
|
+
asteroidDignities,
|
|
59
88
|
signs,
|
|
60
89
|
elements,
|
|
61
90
|
signTypes,
|
|
@@ -3,7 +3,7 @@ const fs = require('fs');
|
|
|
3
3
|
const moment = require('moment-timezone');
|
|
4
4
|
const csvParser = require('csv-parser');
|
|
5
5
|
const { parse } = require('csv-parse/sync');
|
|
6
|
-
const { planets, signs, elements, signTypes, decans, dignities } = require('./astrologyConstants');
|
|
6
|
+
const { planets, asteroidNames, asteroidConstants, asteroidDignities, signs, elements, signTypes, decans, dignities } = require('./astrologyConstants');
|
|
7
7
|
const { loadConfig } = require('../config/configService');
|
|
8
8
|
const path = require('path');
|
|
9
9
|
|
|
@@ -49,6 +49,17 @@ function getCurrentTimeInTimezone() {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// Function to get active planets (filtered by configuration)
|
|
53
|
+
function getActivePlanets() {
|
|
54
|
+
try {
|
|
55
|
+
const { getActivePlanets } = require('../config/configService');
|
|
56
|
+
return getActivePlanets();
|
|
57
|
+
} catch (error) {
|
|
58
|
+
// Fallback to default active planets if config service is not available
|
|
59
|
+
return ['sun', 'moon', 'mercury', 'venus', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune', 'pluto', 'chiron'];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
52
63
|
// Function to load birth data from configuration
|
|
53
64
|
function getBirthDataFromConfig(userId = null) {
|
|
54
65
|
try {
|
|
@@ -377,7 +388,14 @@ function getPlanetHouse(planetLongitude, houseCusps) {
|
|
|
377
388
|
// Function to calculate astrological data
|
|
378
389
|
function getAstrologicalData(planetName, customDate = null) {
|
|
379
390
|
const planet = planets[planetName];
|
|
380
|
-
|
|
391
|
+
const isAsteroid = asteroidNames[planetName] !== undefined;
|
|
392
|
+
let asteroidId = null;
|
|
393
|
+
|
|
394
|
+
if (isAsteroid) {
|
|
395
|
+
asteroidId = asteroidConstants[planetName];
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (planet === undefined && !isAsteroid) {
|
|
381
399
|
console.error(`Invalid planet: ${planetName}. Available planets:`, Object.keys(planets).join(', '));
|
|
382
400
|
process.exit(1);
|
|
383
401
|
}
|
|
@@ -433,16 +451,53 @@ function getAstrologicalData(planetName, customDate = null) {
|
|
|
433
451
|
|
|
434
452
|
const julianDay = calculateJulianDayUTC({ year: calcYear, month: calcMonth, day: calcDay, hour: calcHour, minute: calcMinute }, timezoneOffsetMinutes);
|
|
435
453
|
const flag = swisseph.SEFLG_SWIEPH | swisseph.SEFLG_SPEED;
|
|
436
|
-
let result
|
|
454
|
+
let result;
|
|
437
455
|
|
|
438
|
-
if (
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
456
|
+
if (isAsteroid) {
|
|
457
|
+
// Handle asteroid calculations using proper Swiss Ephemeris constants
|
|
458
|
+
if (asteroidId) {
|
|
459
|
+
// Use the same swe_calc_ut function but with asteroid ID
|
|
460
|
+
result = swisseph.swe_calc_ut(julianDay, asteroidId, flag);
|
|
461
|
+
|
|
462
|
+
if (result.error && result.error.includes('not found')) {
|
|
463
|
+
// Fallback to Moshier if ephemeris files are missing
|
|
464
|
+
const moshierFlag = swisseph.SEFLG_MOSEPH | swisseph.SEFLG_SPEED;
|
|
465
|
+
result = swisseph.swe_calc_ut(julianDay, asteroidId, moshierFlag);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (result.error) {
|
|
469
|
+
console.warn(`⚠️ Could not calculate asteroid ${planetName}: ${result.error}`);
|
|
470
|
+
// Fallback to placeholder data if calculation fails
|
|
471
|
+
result = {
|
|
472
|
+
longitude: 0,
|
|
473
|
+
latitude: 0,
|
|
474
|
+
distance: 0,
|
|
475
|
+
longitudeSpeed: 0
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
} else {
|
|
479
|
+
console.warn(`⚠️ Unknown asteroid: ${planetName}`);
|
|
480
|
+
// Fallback to placeholder data for unknown asteroids
|
|
481
|
+
result = {
|
|
482
|
+
longitude: 0,
|
|
483
|
+
latitude: 0,
|
|
484
|
+
distance: 0,
|
|
485
|
+
longitudeSpeed: 0
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
// Handle regular planet calculations
|
|
490
|
+
result = swisseph.swe_calc_ut(julianDay, planet, flag);
|
|
491
|
+
|
|
492
|
+
if (result.error && result.error.includes('not found')) {
|
|
493
|
+
// Fallback to Moshier if ephemeris files are missing
|
|
494
|
+
const moshierFlag = swisseph.SEFLG_MOSEPH | swisseph.SEFLG_SPEED;
|
|
495
|
+
result = swisseph.swe_calc_ut(julianDay, planet, moshierFlag);
|
|
496
|
+
}
|
|
443
497
|
|
|
444
|
-
|
|
445
|
-
|
|
498
|
+
if (result.error) {
|
|
499
|
+
throw new Error(result.error);
|
|
500
|
+
}
|
|
446
501
|
}
|
|
447
502
|
|
|
448
503
|
const longitude = result.longitude;
|
|
@@ -452,16 +507,20 @@ function getAstrologicalData(planetName, customDate = null) {
|
|
|
452
507
|
const element = elements[signIndex];
|
|
453
508
|
const decan = decans[Math.floor((longitude % 30) / 10)];
|
|
454
509
|
|
|
455
|
-
|
|
510
|
+
// Get dignity info - use asteroid dignities for asteroids, regular dignities for planets
|
|
511
|
+
const dignityInfo = isAsteroid ? asteroidDignities[asteroidId] : dignities[planet];
|
|
456
512
|
let dignity = 'Neutral';
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
513
|
+
|
|
514
|
+
if (dignityInfo) {
|
|
515
|
+
if (sign === dignityInfo.sign) {
|
|
516
|
+
dignity = 'Ruler';
|
|
517
|
+
} else if (sign === dignityInfo.exaltation) {
|
|
518
|
+
dignity = 'Exaltation';
|
|
519
|
+
} else if (sign === dignityInfo.fall) {
|
|
520
|
+
dignity = 'Fall';
|
|
521
|
+
} else if (sign === dignityInfo.detriment) {
|
|
522
|
+
dignity = 'Detriment';
|
|
523
|
+
}
|
|
465
524
|
}
|
|
466
525
|
|
|
467
526
|
return {
|
|
@@ -510,8 +569,16 @@ function getCriticalPlanets(customDate = null) {
|
|
|
510
569
|
const offsetMinutes = timezone ? getTimezoneOffset(timeData, timezone) : -new Date().getTimezoneOffset();
|
|
511
570
|
const julianDay = calculateJulianDayUTC(timeData, offsetMinutes);
|
|
512
571
|
|
|
513
|
-
//
|
|
572
|
+
// Get active planets from configuration
|
|
573
|
+
const activePlanets = getActivePlanets();
|
|
574
|
+
|
|
575
|
+
// Calculate positions of active planets only
|
|
514
576
|
for (const [name, planetId] of Object.entries(planets)) {
|
|
577
|
+
// Skip planets that are not in the active list
|
|
578
|
+
if (!activePlanets.includes(name)) {
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
|
|
515
582
|
const flag = swisseph.SEFLG_SWIEPH | swisseph.SEFLG_SPEED;
|
|
516
583
|
let result = swisseph.swe_calc_ut(julianDay, planetId, flag);
|
|
517
584
|
|
|
@@ -772,12 +839,12 @@ function showPlanetComboAspects(planetNames, dateComponents, useBirthData = fals
|
|
|
772
839
|
// Function to calculate all active aspects between all planets
|
|
773
840
|
function getAllActiveAspects(dateComponents) {
|
|
774
841
|
const allAspects = [];
|
|
775
|
-
const
|
|
842
|
+
const activePlanets = getActivePlanets();
|
|
776
843
|
const seenAspects = new Set(); // To avoid duplicates
|
|
777
844
|
|
|
778
|
-
// Calculate aspects for
|
|
779
|
-
for (let i = 0; i <
|
|
780
|
-
const planet1 =
|
|
845
|
+
// Calculate aspects for active planet pairs only
|
|
846
|
+
for (let i = 0; i < activePlanets.length; i++) {
|
|
847
|
+
const planet1 = activePlanets[i];
|
|
781
848
|
const aspects = calculatePlanetAspects(planet1, dateComponents, true); // Always use Huber orbs
|
|
782
849
|
|
|
783
850
|
aspects.forEach(aspect => {
|
|
@@ -1349,16 +1416,25 @@ async function calculatePersonalTransits(transitDate = null, birthData = null) {
|
|
|
1349
1416
|
const birthJulianDay = calculateJulianDayUTC(birthData, getTimezoneOffset(birthData, birthData.location?.timezone || 'Europe/Zurich'));
|
|
1350
1417
|
const houses = await calculateHouses(birthJulianDay, 'K', true, birthData.location || null); // Always Koch house system for transits
|
|
1351
1418
|
|
|
1352
|
-
//
|
|
1419
|
+
// Get active planets from configuration
|
|
1420
|
+
const activePlanets = getActivePlanets();
|
|
1421
|
+
|
|
1422
|
+
// Calculate current planet positions (Transit) for active planets only
|
|
1353
1423
|
const transitPlanets = {};
|
|
1354
1424
|
for (const [name, planetId] of Object.entries(planets)) {
|
|
1425
|
+
if (!activePlanets.includes(name)) {
|
|
1426
|
+
continue;
|
|
1427
|
+
}
|
|
1355
1428
|
const data = getAstrologicalData(name, transitDate);
|
|
1356
1429
|
transitPlanets[name] = data;
|
|
1357
1430
|
}
|
|
1358
1431
|
|
|
1359
|
-
// Calculate birth planet positions (Radix)
|
|
1432
|
+
// Calculate birth planet positions (Radix) for active planets only
|
|
1360
1433
|
const birthPlanets = {};
|
|
1361
1434
|
for (const [name, planetId] of Object.entries(planets)) {
|
|
1435
|
+
if (!activePlanets.includes(name)) {
|
|
1436
|
+
continue;
|
|
1437
|
+
}
|
|
1362
1438
|
const data = getAstrologicalData(name, birthData);
|
|
1363
1439
|
birthPlanets[name] = data;
|
|
1364
1440
|
}
|
|
@@ -1385,7 +1461,7 @@ async function calculatePersonalTransits(transitDate = null, birthData = null) {
|
|
|
1385
1461
|
}
|
|
1386
1462
|
|
|
1387
1463
|
// Function to calculate personal transit aspects
|
|
1388
|
-
function calculatePersonalTransitAspects(transitDate = null, birthData = null, targetPlanet = null) {
|
|
1464
|
+
function calculatePersonalTransitAspects(transitDate = null, birthData = null, targetPlanet = null, useHuberOrbs = true) {
|
|
1389
1465
|
if (!birthData) {
|
|
1390
1466
|
birthData = getBirthDataFromConfig();
|
|
1391
1467
|
if (!birthData) {
|
|
@@ -1399,52 +1475,79 @@ function calculatePersonalTransitAspects(transitDate = null, birthData = null, t
|
|
|
1399
1475
|
transitDate = getCurrentTimeInTimezone();
|
|
1400
1476
|
}
|
|
1401
1477
|
|
|
1402
|
-
//
|
|
1478
|
+
// Get active planets from configuration
|
|
1479
|
+
const activePlanets = getActivePlanets();
|
|
1480
|
+
|
|
1481
|
+
// Calculate current planet positions (Transit) for active planets only
|
|
1403
1482
|
const transitPlanets = {};
|
|
1404
1483
|
for (const [name, planetId] of Object.entries(planets)) {
|
|
1484
|
+
if (!activePlanets.includes(name)) {
|
|
1485
|
+
continue;
|
|
1486
|
+
}
|
|
1405
1487
|
const data = getAstrologicalData(name, transitDate);
|
|
1406
1488
|
transitPlanets[name] = data.longitude;
|
|
1407
1489
|
}
|
|
1408
1490
|
|
|
1409
|
-
// Calculate birth planet positions (Radix)
|
|
1491
|
+
// Calculate birth planet positions (Radix) for active planets only
|
|
1410
1492
|
const birthPlanets = {};
|
|
1411
1493
|
for (const [name, planetId] of Object.entries(planets)) {
|
|
1494
|
+
if (!activePlanets.includes(name)) {
|
|
1495
|
+
continue;
|
|
1496
|
+
}
|
|
1412
1497
|
const data = getAstrologicalData(name, birthData);
|
|
1413
1498
|
birthPlanets[name] = data.longitude;
|
|
1414
1499
|
}
|
|
1415
1500
|
|
|
1416
1501
|
// Calculate aspects between transit and birth planets
|
|
1417
1502
|
const aspects = [];
|
|
1418
|
-
|
|
1503
|
+
// Use the same orb model as other aspect calculations; default to Huber orbs
|
|
1504
|
+
const aspectTypes = useHuberOrbs ? [
|
|
1419
1505
|
{ name: 'Conjunction', angle: 0, orb: 8 },
|
|
1420
1506
|
{ name: 'Opposition', angle: 180, orb: 8 },
|
|
1421
1507
|
{ name: 'Square', angle: 90, orb: 6 },
|
|
1422
1508
|
{ name: 'Trine', angle: 120, orb: 6 },
|
|
1423
|
-
{ name: 'Sextile', angle: 60, orb: 4 }
|
|
1509
|
+
{ name: 'Sextile', angle: 60, orb: 4 },
|
|
1510
|
+
{ name: 'Quincunx', angle: 150, orb: 3 }
|
|
1511
|
+
] : [
|
|
1512
|
+
{ name: 'Conjunction', angle: 0, orb: 10 },
|
|
1513
|
+
{ name: 'Opposition', angle: 180, orb: 10 },
|
|
1514
|
+
{ name: 'Square', angle: 90, orb: 8 },
|
|
1515
|
+
{ name: 'Trine', angle: 120, orb: 8 },
|
|
1516
|
+
{ name: 'Sextile', angle: 60, orb: 6 },
|
|
1517
|
+
{ name: 'Quincunx', angle: 150, orb: 4 }
|
|
1424
1518
|
];
|
|
1425
1519
|
|
|
1426
1520
|
// Compare each transit planet with each birth planet
|
|
1427
1521
|
for (const [transitName, transitLongitude] of Object.entries(transitPlanets)) {
|
|
1522
|
+
// If filtering by target planet, only calculate for that transit planet
|
|
1523
|
+
if (targetPlanet && transitName !== targetPlanet) continue;
|
|
1524
|
+
|
|
1428
1525
|
for (const [birthName, birthLongitude] of Object.entries(birthPlanets)) {
|
|
1429
1526
|
// Skip if it's the same planet
|
|
1430
1527
|
if (transitName === birthName) continue;
|
|
1431
1528
|
|
|
1432
|
-
// Calculate the angle
|
|
1433
|
-
|
|
1434
|
-
|
|
1529
|
+
// Calculate the angle between the two planets
|
|
1530
|
+
let rawDiff = transitLongitude - birthLongitude;
|
|
1531
|
+
let angleDiff = ((rawDiff % 360) + 360) % 360;
|
|
1435
1532
|
|
|
1436
|
-
// Check for aspects
|
|
1533
|
+
// Check for aspects using the actual angle
|
|
1437
1534
|
for (const aspect of aspectTypes) {
|
|
1438
|
-
|
|
1535
|
+
// Check both the angle and its complement (360 - angle)
|
|
1536
|
+
const diff1 = Math.abs(angleDiff - aspect.angle);
|
|
1537
|
+
const diff2 = Math.abs((360 - angleDiff) - aspect.angle);
|
|
1538
|
+
const diff = Math.min(diff1, diff2);
|
|
1539
|
+
|
|
1540
|
+
if (diff <= aspect.orb) {
|
|
1439
1541
|
aspects.push({
|
|
1440
1542
|
transitPlanet: transitName,
|
|
1441
1543
|
birthPlanet: birthName,
|
|
1442
1544
|
type: aspect.name,
|
|
1443
|
-
angle:
|
|
1444
|
-
orb:
|
|
1545
|
+
angle: angleDiff.toFixed(2),
|
|
1546
|
+
orb: diff.toFixed(2),
|
|
1445
1547
|
transitLongitude: transitLongitude,
|
|
1446
1548
|
birthLongitude: birthLongitude
|
|
1447
1549
|
});
|
|
1550
|
+
break; // Only match one aspect per planet pair
|
|
1448
1551
|
}
|
|
1449
1552
|
}
|
|
1450
1553
|
}
|
|
@@ -1463,8 +1566,8 @@ function calculatePersonalTransitAspects(transitDate = null, birthData = null, t
|
|
|
1463
1566
|
}
|
|
1464
1567
|
|
|
1465
1568
|
// Function to display personal transit aspects
|
|
1466
|
-
function showPersonalTransitAspects(transitDate = null, birthData = null, targetPlanet = null) {
|
|
1467
|
-
const aspectData = calculatePersonalTransitAspects(transitDate, birthData, targetPlanet);
|
|
1569
|
+
function showPersonalTransitAspects(transitDate = null, birthData = null, targetPlanet = null, useHuberOrbs = true) {
|
|
1570
|
+
const aspectData = calculatePersonalTransitAspects(transitDate, birthData, targetPlanet, useHuberOrbs);
|
|
1468
1571
|
|
|
1469
1572
|
if (!aspectData || aspectData.aspects.length === 0) {
|
|
1470
1573
|
console.log('No personal transit aspects found.');
|
|
@@ -1472,7 +1575,7 @@ function showPersonalTransitAspects(transitDate = null, birthData = null, target
|
|
|
1472
1575
|
}
|
|
1473
1576
|
|
|
1474
1577
|
const transitDateDisplay = transitDate || getCurrentTimeInTimezone();
|
|
1475
|
-
console.log(`Personal Transit Aspects (${transitDateDisplay.day}.${transitDateDisplay.month}.${transitDateDisplay.year}):`);
|
|
1578
|
+
console.log(`Personal Transit Aspects (${transitDateDisplay.day}.${transitDateDisplay.month}.${transitDateDisplay.year}) [${(aspectData && aspectData.aspects.length>0) ? (useHuberOrbs ? 'Huber orbs' : 'Standard orbs') : ''}]:`);
|
|
1476
1579
|
console.log('================================================================================');
|
|
1477
1580
|
console.log('| Transit | Birth | Aspect | Orb | Transit Pos | Birth Pos |');
|
|
1478
1581
|
console.log('================================================================================');
|
|
@@ -1606,15 +1709,23 @@ function analyzeElementDistribution(dateComponents, useBirthData = false) {
|
|
|
1606
1709
|
|
|
1607
1710
|
const planetElements = {};
|
|
1608
1711
|
|
|
1609
|
-
//
|
|
1712
|
+
// Get active planets from configuration
|
|
1713
|
+
const activePlanets = getActivePlanets();
|
|
1714
|
+
|
|
1715
|
+
// Calculate elements of active planets only
|
|
1610
1716
|
for (const [name, planetId] of Object.entries(planets)) {
|
|
1717
|
+
// Skip planets that are not in the active list
|
|
1718
|
+
if (!activePlanets.includes(name)) {
|
|
1719
|
+
continue;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1611
1722
|
const data = getAstrologicalData(name, dateComponents);
|
|
1612
1723
|
elementCounts[data.element]++;
|
|
1613
1724
|
planetElements[name] = data.element;
|
|
1614
1725
|
}
|
|
1615
1726
|
|
|
1616
|
-
// Calculate total number of planets
|
|
1617
|
-
const totalPlanets =
|
|
1727
|
+
// Calculate total number of active planets
|
|
1728
|
+
const totalPlanets = activePlanets.length;
|
|
1618
1729
|
|
|
1619
1730
|
// Calculate percentages
|
|
1620
1731
|
const elementPercentages = {};
|
|
@@ -1859,7 +1970,7 @@ function parseFilterCriteria(filterString) {
|
|
|
1859
1970
|
const filterConditions = filterString.split(',').map(cond => cond.trim());
|
|
1860
1971
|
|
|
1861
1972
|
const criteria = [];
|
|
1862
|
-
const operatorRegex = /^(.+?)\s*(
|
|
1973
|
+
const operatorRegex = /^(.+?)\s*(>=|<=|!=|=|>|<)\s*(.+)$/;
|
|
1863
1974
|
|
|
1864
1975
|
for (const condition of filterConditions) {
|
|
1865
1976
|
const operatorMatch = condition.match(operatorRegex);
|
|
@@ -1872,23 +1983,33 @@ function parseFilterCriteria(filterString) {
|
|
|
1872
1983
|
continue;
|
|
1873
1984
|
}
|
|
1874
1985
|
|
|
1875
|
-
// Check for
|
|
1876
|
-
const
|
|
1877
|
-
if (
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1986
|
+
// Check for wildcard patterns in the value (new enhanced syntax)
|
|
1987
|
+
const wildcardMatch = condition.match(/^([^:]+):(.+)$/);
|
|
1988
|
+
if (wildcardMatch) {
|
|
1989
|
+
const column = wildcardMatch[1].trim();
|
|
1990
|
+
const value = wildcardMatch[2].trim();
|
|
1991
|
+
|
|
1992
|
+
// Remove * and ! from the value for contains/excludes operations
|
|
1993
|
+
const cleanValue = value.replace(/^[*!]+/, '').replace(/[*!]+$/, '');
|
|
1994
|
+
|
|
1995
|
+
// Check if value contains * or starts with !
|
|
1996
|
+
if (value.includes('*') || value.startsWith('!')) {
|
|
1997
|
+
// Use * operator for contains matching (remove * from value)
|
|
1998
|
+
// Use ! operator for excludes matching (remove ! from value)
|
|
1999
|
+
const operator = value.startsWith('!') ? '!' : '*';
|
|
2000
|
+
criteria.push({
|
|
2001
|
+
column: column,
|
|
2002
|
+
operator: operator,
|
|
2003
|
+
value: cleanValue
|
|
2004
|
+
});
|
|
2005
|
+
continue;
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
// Regular equality match
|
|
1888
2009
|
criteria.push({
|
|
1889
|
-
column:
|
|
1890
|
-
operator: '
|
|
1891
|
-
value:
|
|
2010
|
+
column: column,
|
|
2011
|
+
operator: '=',
|
|
2012
|
+
value: value
|
|
1892
2013
|
});
|
|
1893
2014
|
continue;
|
|
1894
2015
|
}
|
|
@@ -2074,6 +2195,13 @@ function analyzeCSVWithDatetime(filePath, planetName = 'moon', houseSystem = 'ko
|
|
|
2074
2195
|
const trimmed = value != null ? value.toString().trim() : '';
|
|
2075
2196
|
return /^\d{2}\/\d{2}\/\d{4}(?:\s+\d{1,2}:\d{2}(?::\d{2})?)?$/.test(trimmed);
|
|
2076
2197
|
});
|
|
2198
|
+
|
|
2199
|
+
// Second pass alternative: look for DD.MM.YYYY format (optional time)
|
|
2200
|
+
const ddMmYyyyDotColumns = Object.keys(data).filter(key => {
|
|
2201
|
+
const value = data[key];
|
|
2202
|
+
const trimmed = value != null ? value.toString().trim() : '';
|
|
2203
|
+
return /^\d{2}\.\d{2}\.\d{4}(?:\s+\d{1,2}:\d{2}(?::\d{2})?)?$/.test(trimmed);
|
|
2204
|
+
});
|
|
2077
2205
|
|
|
2078
2206
|
// Third pass: look for Unix timestamps (10 or 13 digits)
|
|
2079
2207
|
const unixTimestampColumns = Object.keys(data).filter(key => {
|
|
@@ -2093,8 +2221,8 @@ function analyzeCSVWithDatetime(filePath, planetName = 'moon', houseSystem = 'ko
|
|
|
2093
2221
|
return moment(trimmed, moment.ISO_8601, true).isValid();
|
|
2094
2222
|
});
|
|
2095
2223
|
|
|
2096
|
-
// Prioritize columns: YYYY-MM-DD, DD/MM/YYYY, Unix timestamps, then ISO dates
|
|
2097
|
-
datetimeColumns.push(...yyyyMmDdColumns, ...ddMmYyyyColumns, ...unixTimestampColumns, ...isoDateColumns);
|
|
2224
|
+
// Prioritize columns: YYYY-MM-DD, DD/MM/YYYY, DD.MM.YYYY, Unix timestamps, then ISO dates
|
|
2225
|
+
datetimeColumns.push(...yyyyMmDdColumns, ...ddMmYyyyColumns, ...ddMmYyyyDotColumns, ...unixTimestampColumns, ...isoDateColumns);
|
|
2098
2226
|
|
|
2099
2227
|
if (datetimeColumns.length > 0) {
|
|
2100
2228
|
datetimeValue = data[datetimeColumns[0]];
|
|
@@ -2121,6 +2249,15 @@ function analyzeCSVWithDatetime(filePath, planetName = 'moon', houseSystem = 'ko
|
|
|
2121
2249
|
if (datetime.isValid() && !hasTime) {
|
|
2122
2250
|
datetime = datetime.hour(12).minute(0);
|
|
2123
2251
|
}
|
|
2252
|
+
} else if (/^\d{2}\.\d{2}\.\d{4}(?:\s+\d{1,2}:\d{2}(?::\d{2})?)?$/.test(datetimeValueTrimmed)) {
|
|
2253
|
+
// Handle as DD.MM.YYYY (optional time)
|
|
2254
|
+
const hasTime = /\d{1,2}:\d{2}/.test(datetimeValueTrimmed);
|
|
2255
|
+
// Replace dots with slashes to use the same parsing logic
|
|
2256
|
+
const datetimeValueWithSlashes = datetimeValueTrimmed.replace(/\./g, '/');
|
|
2257
|
+
datetime = moment(datetimeValueWithSlashes, ['DD/MM/YYYY HH:mm:ss', 'DD/MM/YYYY HH:mm', 'DD/MM/YYYY'], true);
|
|
2258
|
+
if (datetime.isValid() && !hasTime) {
|
|
2259
|
+
datetime = datetime.hour(12).minute(0);
|
|
2260
|
+
}
|
|
2124
2261
|
} else {
|
|
2125
2262
|
// Handle as ISO-8601 date
|
|
2126
2263
|
datetime = moment(datetimeValueTrimmed);
|
|
@@ -2324,9 +2461,11 @@ function analyzeCSVWithDatetime(filePath, planetName = 'moon', houseSystem = 'ko
|
|
|
2324
2461
|
const isYYYYMMDD = /^\d{4}-\d{2}-\d{2}$/.test(trimmed);
|
|
2325
2462
|
// Check for DD/MM/YYYY date format
|
|
2326
2463
|
const isDDMMYYYY = /^\d{2}\/\d{2}\/\d{4}(?:\s+\d{1,2}:\d{2}(?::\d{2})?)?$/.test(trimmed);
|
|
2464
|
+
// Check for DD.MM.YYYY date format
|
|
2465
|
+
const isDDMMYYYYDot = /^\d{2}\.\d{2}\.\d{4}(?:\s+\d{1,2}:\d{2}(?::\d{2})?)?$/.test(trimmed);
|
|
2327
2466
|
// Check for Unix-Timestamp (number with 10 or 13 digits)
|
|
2328
2467
|
const isUnixTimestamp = /^\d{10,13}$/.test(trimmed);
|
|
2329
|
-
return isISO || isYYYYMMDD || isDDMMYYYY || isUnixTimestamp;
|
|
2468
|
+
return isISO || isYYYYMMDD || isDDMMYYYY || isDDMMYYYYDot || isUnixTimestamp;
|
|
2330
2469
|
});
|
|
2331
2470
|
|
|
2332
2471
|
if (datetimeColumns.length > 0) {
|
|
@@ -2354,6 +2493,15 @@ function analyzeCSVWithDatetime(filePath, planetName = 'moon', houseSystem = 'ko
|
|
|
2354
2493
|
if (datetime.isValid() && !hasTime) {
|
|
2355
2494
|
datetime = datetime.hour(12).minute(0);
|
|
2356
2495
|
}
|
|
2496
|
+
} else if (/^\d{2}\.\d{2}\.\d{4}(?:\s+\d{1,2}:\d{2}(?::\d{2})?)?$/.test(datetimeValueTrimmed)) {
|
|
2497
|
+
// Handle as DD.MM.YYYY (optional time)
|
|
2498
|
+
const hasTime = /\d{1,2}:\d{2}/.test(datetimeValueTrimmed);
|
|
2499
|
+
// Replace dots with slashes to use the same parsing logic
|
|
2500
|
+
const datetimeValueWithSlashes = datetimeValueTrimmed.replace(/\./g, '/');
|
|
2501
|
+
datetime = moment(datetimeValueWithSlashes, ['DD/MM/YYYY HH:mm:ss', 'DD/MM/YYYY HH:mm', 'DD/MM/YYYY'], true);
|
|
2502
|
+
if (datetime.isValid() && !hasTime) {
|
|
2503
|
+
datetime = datetime.hour(12).minute(0);
|
|
2504
|
+
}
|
|
2357
2505
|
} else {
|
|
2358
2506
|
// Handle as ISO-8601 date
|
|
2359
2507
|
datetime = moment(datetimeValueTrimmed);
|
|
@@ -3053,7 +3201,10 @@ module.exports = {
|
|
|
3053
3201
|
calculateNextPlanetIngress,
|
|
3054
3202
|
calculateAstrologicalAngles,
|
|
3055
3203
|
longitudeToSignDegree,
|
|
3056
|
-
calculateTransitFrequency
|
|
3204
|
+
calculateTransitFrequency,
|
|
3205
|
+
// Filter functions for testing
|
|
3206
|
+
parseFilterCriteria,
|
|
3207
|
+
applyFilter
|
|
3057
3208
|
};
|
|
3058
3209
|
|
|
3059
3210
|
// Function to calculate how often a transit occurs between two planets
|
package/src/cli/cli.js
CHANGED
|
@@ -393,7 +393,7 @@ program
|
|
|
393
393
|
.option('--z <count>', 'Shows future aspects between two planets (Format: --z <count> planet1 aspectType planet2)')
|
|
394
394
|
.option('--csv <filepath>', 'Analyzes a CSV file with ISO-Datetime values or Unix timestamps')
|
|
395
395
|
.option('--date-col <column>', 'Specifies the column name containing date values (e.g., --date-col "Buchungsdatum")')
|
|
396
|
-
.option('--filter <column:value>', 'Filters CSV data by column:value (e.g., --filter "Item:coffee")')
|
|
396
|
+
.option('--filter <column:value>', 'Filters CSV data by column:value (e.g., --filter "Item:coffee", "Item:*coffee*", "Item:!bad*")')
|
|
397
397
|
.option('--title <title>', 'Title for the chart image (generates PNG image when provided)')
|
|
398
398
|
.option('--in [count]', 'Shows next planet ingress (entering new sign). Optional count for multiple ingresses')
|
|
399
399
|
.option('--wiki <occupation>', 'Fetches people from Wikidata by occupation and checks for specific aspects (Format: planet1 aspectType planet2 --wiki <occupation> [limit])')
|
|
@@ -401,6 +401,8 @@ program
|
|
|
401
401
|
.option('--gui-port <port>', 'Specify custom port for GUI server')
|
|
402
402
|
.option('--gui-verbose', 'Enable verbose logging for GUI server')
|
|
403
403
|
.option('--o', 'Shows how often this transit occurs in the sky (frequency calculation)')
|
|
404
|
+
.option('--planets <list>', 'Select active planets and asteroids (comma-separated, e.g., "Sun,Moon,Mercury,Venus,Hygiea")')
|
|
405
|
+
.option('--active-planets', 'Show the currently active planets')
|
|
404
406
|
.description('Shows astrological data for a planet')
|
|
405
407
|
.action(async (planetArg, planet2Arg, options) => {
|
|
406
408
|
// If planet2Arg is an object, it contains the options (commander behavior)
|
|
@@ -875,46 +877,68 @@ program
|
|
|
875
877
|
const houses = await calculateHouses(birthJulianDay, getHouseSystemCode(houseSystem), true);
|
|
876
878
|
const transitDateDisplay = transitDate || getCurrentTimeInTimezone();
|
|
877
879
|
|
|
878
|
-
//
|
|
880
|
+
// Collect all transit aspects first
|
|
881
|
+
const allAspects = [];
|
|
879
882
|
for (const [planetName] of Object.entries(planets)) {
|
|
880
883
|
const transitPlanetData = getAstrologicalData(planetName, transitDateDisplay);
|
|
881
884
|
const transitHouse = getPlanetHouse(parseFloat(transitPlanetData.degreeInSign) + (signs.indexOf(transitPlanetData.sign) * 30), houses.house);
|
|
882
|
-
const transitAspectsData = calculatePersonalTransitAspects(transitDateDisplay, birthData, planetName);
|
|
885
|
+
const transitAspectsData = calculatePersonalTransitAspects(transitDateDisplay, birthData, planetName, true);
|
|
883
886
|
const transitAspects = transitAspectsData ? transitAspectsData.aspects : [];
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
'Trine': 120,
|
|
895
|
-
'Sextile': 60
|
|
896
|
-
};
|
|
897
|
-
const targetAngle = aspectAngles[aspect.type];
|
|
898
|
-
const phase = determineAspectPhase(planetName, aspect.birthPlanet, transitDateDisplay, aspect.type, targetAngle);
|
|
899
|
-
|
|
900
|
-
// Exact time calculation removed
|
|
901
|
-
const dateTimeStr = '-';
|
|
902
|
-
|
|
903
|
-
const transitPlanetFormatted = planetName.charAt(0).toUpperCase() + planetName.slice(1);
|
|
904
|
-
const aspectTypeFormatted = aspect.type.padEnd(11);
|
|
905
|
-
const aspectPlanetFormatted = aspect.birthPlanet.charAt(0).toUpperCase() + aspect.birthPlanet.slice(1);
|
|
906
|
-
const orbFormatted = aspect.orb.padEnd(4);
|
|
907
|
-
const transitPosFormatted = `${transitPlanetData.sign} ${transitPlanetData.degreeInSign}°`;
|
|
908
|
-
const transitHouseFormatted = transitHouse.toString().padEnd(11);
|
|
909
|
-
const birthHouseFormatted = birthHouse.toString().padEnd(11);
|
|
910
|
-
const phaseFormatted = phase === 'separativ' ? 'separativ'.padEnd(11) : phase.padEnd(11);
|
|
911
|
-
const dateTimeFormatted = dateTimeStr.padEnd(22);
|
|
912
|
-
|
|
913
|
-
console.log(`| ${transitPlanetFormatted.padEnd(10)} | ${aspectTypeFormatted} | ${aspectPlanetFormatted.padEnd(10)} | ${orbFormatted}° | ${transitPosFormatted.padEnd(16)} | ${transitHouseFormatted} | ${birthHouseFormatted} | ${phaseFormatted} |`);
|
|
887
|
+
|
|
888
|
+
// Add planet data to each aspect for later display
|
|
889
|
+
transitAspects.forEach(aspect => {
|
|
890
|
+
allAspects.push({
|
|
891
|
+
planetName: planetName,
|
|
892
|
+
transitPlanetData: transitPlanetData,
|
|
893
|
+
transitHouse: transitHouse,
|
|
894
|
+
aspect: aspect,
|
|
895
|
+
birthPlanetData: getAstrologicalData(aspect.birthPlanet, birthData),
|
|
896
|
+
birthHouse: getPlanetHouse(parseFloat(getAstrologicalData(aspect.birthPlanet, birthData).degreeInSign) + (signs.indexOf(getAstrologicalData(aspect.birthPlanet, birthData).sign) * 30), houses.house)
|
|
914
897
|
});
|
|
915
|
-
}
|
|
898
|
+
});
|
|
916
899
|
}
|
|
917
900
|
|
|
901
|
+
// Sort all aspects by orb (smallest orb first)
|
|
902
|
+
allAspects.sort((a, b) => {
|
|
903
|
+
return parseFloat(a.aspect.orb) - parseFloat(b.aspect.orb);
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
// Display sorted aspects
|
|
907
|
+
allAspects.forEach(aspectData => {
|
|
908
|
+
const planetName = aspectData.planetName;
|
|
909
|
+
const transitPlanetData = aspectData.transitPlanetData;
|
|
910
|
+
const transitHouse = aspectData.transitHouse;
|
|
911
|
+
const aspect = aspectData.aspect;
|
|
912
|
+
const birthPlanetData = aspectData.birthPlanetData;
|
|
913
|
+
const birthHouse = aspectData.birthHouse;
|
|
914
|
+
|
|
915
|
+
// Determine aspect phase
|
|
916
|
+
const aspectAngles = {
|
|
917
|
+
'Conjunction': 0,
|
|
918
|
+
'Opposition': 180,
|
|
919
|
+
'Square': 90,
|
|
920
|
+
'Trine': 120,
|
|
921
|
+
'Sextile': 60
|
|
922
|
+
};
|
|
923
|
+
const targetAngle = aspectAngles[aspect.type];
|
|
924
|
+
const phase = determineAspectPhase(planetName, aspect.birthPlanet, transitDateDisplay, aspect.type, targetAngle);
|
|
925
|
+
|
|
926
|
+
// Exact time calculation removed
|
|
927
|
+
const dateTimeStr = '-';
|
|
928
|
+
|
|
929
|
+
const transitPlanetFormatted = planetName.charAt(0).toUpperCase() + planetName.slice(1);
|
|
930
|
+
const aspectTypeFormatted = aspect.type.padEnd(11);
|
|
931
|
+
const aspectPlanetFormatted = aspect.birthPlanet.charAt(0).toUpperCase() + aspect.birthPlanet.slice(1);
|
|
932
|
+
const orbFormatted = aspect.orb.padEnd(4);
|
|
933
|
+
const transitPosFormatted = `${transitPlanetData.sign} ${transitPlanetData.degreeInSign}°`;
|
|
934
|
+
const transitHouseFormatted = transitHouse.toString().padEnd(11);
|
|
935
|
+
const birthHouseFormatted = birthHouse.toString().padEnd(11);
|
|
936
|
+
const phaseFormatted = phase === 'separativ' ? 'separativ'.padEnd(11) : phase.padEnd(11);
|
|
937
|
+
const dateTimeFormatted = dateTimeStr.padEnd(22);
|
|
938
|
+
|
|
939
|
+
console.log(`| ${transitPlanetFormatted.padEnd(10)} | ${aspectTypeFormatted} | ${aspectPlanetFormatted.padEnd(10)} | ${orbFormatted}° | ${transitPosFormatted.padEnd(16)} | ${transitHouseFormatted} | ${birthHouseFormatted} | ${phaseFormatted} |`);
|
|
940
|
+
});
|
|
941
|
+
|
|
918
942
|
console.log('================================================================================');
|
|
919
943
|
console.log(`\nAnalyse-Datum: ${transitDateDisplay.day}.${transitDateDisplay.month}.${transitDateDisplay.year}`);
|
|
920
944
|
console.log(`Geburtsdatum: ${birthData.day}.${birthData.month}.${birthData.year} ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')}`);
|
|
@@ -975,13 +999,13 @@ program
|
|
|
975
999
|
// Show personal transit aspects
|
|
976
1000
|
// Use the specific planet if specified, or all planets
|
|
977
1001
|
const targetPlanet = planetArg ? planetArg.toLowerCase() : null;
|
|
978
|
-
showPersonalTransitAspects(transitDate, birthData, targetPlanet);
|
|
1002
|
+
showPersonalTransitAspects(transitDate, birthData, targetPlanet, true);
|
|
979
1003
|
|
|
980
1004
|
// Handle AI request for personal transit aspects
|
|
981
1005
|
if (options.p) {
|
|
982
1006
|
// Get the transit aspects data for AI
|
|
983
1007
|
const { calculatePersonalTransitAspects } = require('../astrology/astrologyService');
|
|
984
|
-
const transitAspectsData = calculatePersonalTransitAspects(transitDate, birthData, targetPlanet);
|
|
1008
|
+
const transitAspectsData = calculatePersonalTransitAspects(transitDate, birthData, targetPlanet, true);
|
|
985
1009
|
const transitAspects = transitAspectsData ? transitAspectsData.aspects : [];
|
|
986
1010
|
|
|
987
1011
|
const aiData = {
|
|
@@ -1207,6 +1231,21 @@ program
|
|
|
1207
1231
|
return;
|
|
1208
1232
|
}
|
|
1209
1233
|
|
|
1234
|
+
// Set active planets if --planets option is specified (no planet required)
|
|
1235
|
+
if (options.planets) {
|
|
1236
|
+
const { setActivePlanets } = require('../config/configService');
|
|
1237
|
+
setActivePlanets(options.planets);
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// Show active planets if --active-planets option is specified (no planet required)
|
|
1242
|
+
if (options.activePlanets) {
|
|
1243
|
+
const { getActivePlanets } = require('../config/configService');
|
|
1244
|
+
const activePlanets = getActivePlanets();
|
|
1245
|
+
console.log('Active planets:', activePlanets.join(', '));
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1210
1249
|
// Set AI model if --ai option is specified (no planet required)
|
|
1211
1250
|
if (options.ai) {
|
|
1212
1251
|
setAIModel(options.ai);
|
package/src/cli/cliService.js
CHANGED
|
@@ -160,13 +160,15 @@ class CLIService {
|
|
|
160
160
|
.option('--v <count>', 'Shows past aspects between two planets (Format: --v <count> planet1 aspectType planet2)')
|
|
161
161
|
.option('--z <count>', 'Shows future aspects between two planets (Format: --z <count> planet1 aspectType planet2)')
|
|
162
162
|
.option('--csv <filepath>', 'Analyzes a CSV file with ISO-Datetime values or Unix timestamps')
|
|
163
|
-
.option('--filter <column:value>', 'Filters CSV data by column:value (e.g., --filter "Item:coffee")')
|
|
163
|
+
.option('--filter <column:value>', 'Filters CSV data by column:value (e.g., --filter "Item:coffee", "Item:*coffee*", "Item:!bad*")')
|
|
164
164
|
.option('--title <title>', 'Title for the chart image (generates PNG image when provided)')
|
|
165
165
|
.option('--in [count]', 'Shows next planet ingress (entering new sign). Optional count for multiple ingresses')
|
|
166
166
|
.option('--wiki <occupation>', 'Fetches people from Wikidata by occupation and checks for specific aspects (Format: planet1 aspectType planet2 --wiki <occupation> [limit])')
|
|
167
167
|
.option('--gui', 'Launches the web interface for command history (port 37421)')
|
|
168
168
|
.option('--gui-port <port>', 'Specify custom port for GUI server')
|
|
169
169
|
.option('--o', 'Shows how often this transit occurs in the sky (frequency calculation)')
|
|
170
|
+
.option('--planets <list>', 'Select active planets and asteroids (comma-separated, e.g., "Sun,Moon,Mercury,Venus,Hygiea")')
|
|
171
|
+
.option('--active-planets', 'Show the currently active planets')
|
|
170
172
|
.description('Shows astrological data for a planet')
|
|
171
173
|
.action(this.handleCommand.bind(this));
|
|
172
174
|
}
|
|
@@ -652,6 +654,21 @@ class CLIService {
|
|
|
652
654
|
return { success: true, output: output.join('\n') };
|
|
653
655
|
}
|
|
654
656
|
|
|
657
|
+
// Set active planets if --planets option is specified (no planet required)
|
|
658
|
+
if (actualOptions.planets) {
|
|
659
|
+
const { setActivePlanets } = require('../config/configService');
|
|
660
|
+
setActivePlanets(actualOptions.planets, userId);
|
|
661
|
+
return { success: true, output: output.join('\n') };
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Show active planets if --active-planets option is specified (no planet required)
|
|
665
|
+
if (actualOptions.activePlanets) {
|
|
666
|
+
const { getActivePlanets } = require('../config/configService');
|
|
667
|
+
const activePlanets = getActivePlanets(userId);
|
|
668
|
+
console.log('Active planets:', activePlanets.join(', '));
|
|
669
|
+
return { success: true, output: output.join('\n') };
|
|
670
|
+
}
|
|
671
|
+
|
|
655
672
|
// Delete person if --delete-person option is specified (no planet required)
|
|
656
673
|
if (actualOptions.deletePerson) {
|
|
657
674
|
deletePerson(actualOptions.deletePerson, userId);
|
|
@@ -456,6 +456,52 @@ function setAIModel(modelName, userId = null) {
|
|
|
456
456
|
return false;
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
+
// Function to set active planets
|
|
460
|
+
function setActivePlanets(planetList, userId = null) {
|
|
461
|
+
const config = loadConfig(userId) || {
|
|
462
|
+
currentLocation: null,
|
|
463
|
+
birthData: null,
|
|
464
|
+
setupDate: new Date().toISOString()
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// Get available planets for validation
|
|
468
|
+
const { planets } = require('../astrology/astrologyConstants');
|
|
469
|
+
const availablePlanets = Object.keys(planets);
|
|
470
|
+
|
|
471
|
+
// Convert comma-separated string to array and normalize planet names
|
|
472
|
+
const planetsArray = planetList.split(',').map(p => p.trim().toLowerCase());
|
|
473
|
+
|
|
474
|
+
// Validate planet names
|
|
475
|
+
const invalidPlanets = planetsArray.filter(p => !availablePlanets.includes(p));
|
|
476
|
+
if (invalidPlanets.length > 0) {
|
|
477
|
+
console.error(`Error: Invalid planet names: ${invalidPlanets.join(', ')}`);
|
|
478
|
+
console.error(`Available planets: ${availablePlanets.join(', ')}`);
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
config.activePlanets = planetsArray;
|
|
483
|
+
|
|
484
|
+
if (saveConfig(config, userId)) {
|
|
485
|
+
console.log(`Active planets set: ${planetsArray.join(', ')}`);
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Function to get active planets (with defaults)
|
|
492
|
+
function getActivePlanets(userId = null) {
|
|
493
|
+
const config = loadConfig(userId);
|
|
494
|
+
|
|
495
|
+
// Default active planets (traditional planets + Chiron, excluding other asteroids)
|
|
496
|
+
const defaultActivePlanets = ['sun', 'moon', 'mercury', 'venus', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune', 'pluto', 'chiron'];
|
|
497
|
+
|
|
498
|
+
if (config && config.activePlanets && Array.isArray(config.activePlanets)) {
|
|
499
|
+
return config.activePlanets;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return defaultActivePlanets;
|
|
503
|
+
}
|
|
504
|
+
|
|
459
505
|
// Function to set a custom system prompt
|
|
460
506
|
function setSystemPrompt(prompt, userId = null) {
|
|
461
507
|
const config = loadConfig(userId) || {
|
|
@@ -903,5 +949,8 @@ module.exports = {
|
|
|
903
949
|
showNotes,
|
|
904
950
|
searchNotesBySituation,
|
|
905
951
|
// Add helper functions for user-specific operations
|
|
906
|
-
getConfigPath
|
|
952
|
+
getConfigPath,
|
|
953
|
+
// Planet selection functions
|
|
954
|
+
setActivePlanets,
|
|
955
|
+
getActivePlanets
|
|
907
956
|
};
|