klio 1.5.0 → 1.5.2
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 -1
- package/compose.yaml +6 -0
- package/package.json +8 -3
- package/src/astrology/astrologyService.js +43 -159
- 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 +93 -147
- 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/.dockerignore
ADDED
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
20
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
FROM node:20-bookworm-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Build deps for native modules like swisseph
|
|
6
|
+
RUN apt-get update \
|
|
7
|
+
&& apt-get install -y --no-install-recommends python3 make g++ \
|
|
8
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
9
|
+
|
|
10
|
+
COPY package.json package-lock.json ./
|
|
11
|
+
RUN npm ci
|
|
12
|
+
|
|
13
|
+
COPY . .
|
|
14
|
+
|
|
15
|
+
ENV NODE_ENV=production
|
|
16
|
+
EXPOSE 37421
|
|
17
|
+
|
|
18
|
+
CMD ["node", "src/gui/server.js"]
|
package/README.md
CHANGED
|
@@ -56,7 +56,28 @@ This will start a web server on port 37421 (or a different port if specified) an
|
|
|
56
56
|
klio --gui --gui-port 8080
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
###
|
|
59
|
+
### Running with Docker
|
|
60
|
+
|
|
61
|
+
You can also run the GUI using Docker for easy deployment:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Build the Docker image
|
|
65
|
+
docker build -t astrocli-gui .
|
|
66
|
+
|
|
67
|
+
# Run the container
|
|
68
|
+
docker run -p 3006:37421 astrocli-gui
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This will start the GUI on `http://localhost:37421`.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
### Demo
|
|
75
|
+
|
|
76
|
+
Try a live demo of klio web here:
|
|
77
|
+
|
|
78
|
+
🌐 [https://klio.up.railway.app](https://klio.up.railway.app)
|
|
79
|
+
|
|
80
|
+
## Configuration
|
|
60
81
|
|
|
61
82
|
- **Show status**: `--status` - Shows the stored configuration data
|
|
62
83
|
- **Setup**: `--setup` - Setup for a default chart which can be accessed with `--i` for example `klio --a --i` for displaying the natal aspects. In this setup you can also set up a local AI model for LM-Studio.
|
package/compose.yaml
ADDED
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "klio",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "A CLI for astrological calculations",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"klio": "./src/main.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
+
"build:gui-css": "tailwindcss -i ./src/gui/public/tailwind.css -o ./src/gui/public/tailwind.generated.css --minify",
|
|
12
|
+
"watch:gui-css": "tailwindcss -i ./src/gui/public/tailwind.css -o ./src/gui/public/tailwind.generated.css --watch"
|
|
11
13
|
},
|
|
12
14
|
"keywords": [
|
|
13
15
|
"astrology",
|
|
@@ -19,15 +21,18 @@
|
|
|
19
21
|
"license": "ISC",
|
|
20
22
|
"type": "commonjs",
|
|
21
23
|
"dependencies": {
|
|
24
|
+
"@webcontainer/api": "^1.6.1",
|
|
22
25
|
"axios": "^1.13.4",
|
|
26
|
+
"bcryptjs": "^3.0.3",
|
|
23
27
|
"commander": "^14.0.3",
|
|
24
28
|
"csv": "^6.4.1",
|
|
25
29
|
"csv-parser": "^3.0.0",
|
|
26
30
|
"express": "^4.19.2",
|
|
31
|
+
"express-session": "^1.19.0",
|
|
27
32
|
"fast-xml-parser": "^5.3.4",
|
|
28
33
|
"moment-timezone": "^0.6.0",
|
|
29
34
|
"node-fetch": "^3.3.2",
|
|
30
|
-
"
|
|
35
|
+
"session-file-store": "^1.5.0",
|
|
31
36
|
"swisseph": "^0.5.17"
|
|
32
37
|
}
|
|
33
38
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const swisseph = require('
|
|
1
|
+
const swisseph = require('./swissephAdapter');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const moment = require('moment-timezone');
|
|
4
4
|
const csvParser = require('csv-parser');
|
|
@@ -50,11 +50,11 @@ function getCurrentTimeInTimezone() {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// Function to load birth data from configuration
|
|
53
|
-
function getBirthDataFromConfig() {
|
|
53
|
+
function getBirthDataFromConfig(userId = null) {
|
|
54
54
|
try {
|
|
55
55
|
// First try to load the new configuration
|
|
56
56
|
const { loadConfig } = require('../config/configService');
|
|
57
|
-
const config = loadConfig();
|
|
57
|
+
const config = loadConfig(userId);
|
|
58
58
|
|
|
59
59
|
if (config && config.birthData) {
|
|
60
60
|
// Parse birth date (Format: DD.MM.YYYY)
|
|
@@ -78,32 +78,34 @@ function getBirthDataFromConfig() {
|
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
// If the new configuration was not found, try the old file
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (oldConfig && oldConfig.birthData) {
|
|
88
|
-
// Parse birth date (Format: DD.MM.YYYY)
|
|
89
|
-
const dateParts = oldConfig.birthData.date.split('.');
|
|
90
|
-
const day = parseInt(dateParts[0]);
|
|
91
|
-
const month = parseInt(dateParts[1]);
|
|
92
|
-
const year = parseInt(dateParts[2]);
|
|
93
|
-
|
|
94
|
-
// Parse birth time (Format: HH:MM)
|
|
95
|
-
const timeParts = oldConfig.birthData.time.split(':');
|
|
96
|
-
const hour = parseInt(timeParts[0]);
|
|
97
|
-
const minute = parseInt(timeParts[1]);
|
|
81
|
+
// If the new configuration was not found, try the old file (only for global config)
|
|
82
|
+
if (!userId) {
|
|
83
|
+
const configPath = path.join(__dirname, '../../astrocli-config.json');
|
|
84
|
+
if (fs.existsSync(configPath)) {
|
|
85
|
+
const configData = fs.readFileSync(configPath, 'utf8');
|
|
86
|
+
const oldConfig = JSON.parse(configData);
|
|
98
87
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
day
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
88
|
+
if (oldConfig && oldConfig.birthData) {
|
|
89
|
+
// Parse birth date (Format: DD.MM.YYYY)
|
|
90
|
+
const dateParts = oldConfig.birthData.date.split('.');
|
|
91
|
+
const day = parseInt(dateParts[0]);
|
|
92
|
+
const month = parseInt(dateParts[1]);
|
|
93
|
+
const year = parseInt(dateParts[2]);
|
|
94
|
+
|
|
95
|
+
// Parse birth time (Format: HH:MM)
|
|
96
|
+
const timeParts = oldConfig.birthData.time.split(':');
|
|
97
|
+
const hour = parseInt(timeParts[0]);
|
|
98
|
+
const minute = parseInt(timeParts[1]);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
year: year,
|
|
102
|
+
month: month,
|
|
103
|
+
day: day,
|
|
104
|
+
hour: hour,
|
|
105
|
+
minute: minute,
|
|
106
|
+
location: oldConfig.birthData.location
|
|
107
|
+
};
|
|
108
|
+
}
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
} catch (error) {
|
|
@@ -114,14 +116,14 @@ function getBirthDataFromConfig() {
|
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
// Function to load person data (including birth data)
|
|
117
|
-
function getPersonDataFromConfig(personId = 'birth') {
|
|
119
|
+
function getPersonDataFromConfig(personId = 'birth', userId = null) {
|
|
118
120
|
try {
|
|
119
121
|
const { getPersonData } = require('../config/configService');
|
|
120
122
|
|
|
121
123
|
if (personId === 'birth') {
|
|
122
|
-
return getBirthDataFromConfig();
|
|
124
|
+
return getBirthDataFromConfig(userId);
|
|
123
125
|
} else {
|
|
124
|
-
return getPersonData(personId);
|
|
126
|
+
return getPersonData(personId, userId);
|
|
125
127
|
}
|
|
126
128
|
} catch (error) {
|
|
127
129
|
console.error(`Error loading data for ${personId}:`, error.message);
|
|
@@ -684,7 +686,7 @@ function showPlanetAspects(planetName, dateComponents, useBirthData = false, use
|
|
|
684
686
|
|
|
685
687
|
console.log(`Aspects for ${planetLabel}:`);
|
|
686
688
|
console.log('================================================================================');
|
|
687
|
-
console.log('| Aspect | Planet | Orb |
|
|
689
|
+
console.log('| Aspect | Planet | Orb |');
|
|
688
690
|
console.log('================================================================================');
|
|
689
691
|
|
|
690
692
|
if (aspects.length === 0) {
|
|
@@ -696,14 +698,8 @@ function showPlanetAspects(planetName, dateComponents, useBirthData = false, use
|
|
|
696
698
|
const planetFormatted = planetNameFormatted.padEnd(8, ' ');
|
|
697
699
|
const orb = aspect.orb.padEnd(4, ' ');
|
|
698
700
|
|
|
699
|
-
//
|
|
700
|
-
|
|
701
|
-
if (exactDateTime) {
|
|
702
|
-
const dateTimeStr = `${String(exactDateTime.day).padStart(2, '0')}.${String(exactDateTime.month).padStart(2, '0')}.${exactDateTime.year} ${String(exactDateTime.hour).padStart(2, '0')}:${String(exactDateTime.minute).padStart(2, '0')}`;
|
|
703
|
-
console.log(`| ${aspectName} | ${planetFormatted} | ${orb}° | ${dateTimeStr} |`);
|
|
704
|
-
} else {
|
|
705
|
-
console.log(`| ${aspectName} | ${planetFormatted} | ${orb}° | Calculation failed |`);
|
|
706
|
-
}
|
|
701
|
+
// Exact time calculation removed
|
|
702
|
+
console.log(`| ${aspectName} | ${planetFormatted} | ${orb}° |`);
|
|
707
703
|
});
|
|
708
704
|
}
|
|
709
705
|
|
|
@@ -724,7 +720,7 @@ function showPlanetComboAspects(planetNames, dateComponents, useBirthData = fals
|
|
|
724
720
|
|
|
725
721
|
console.log(`Aspects between ${planetLabels}:`);
|
|
726
722
|
console.log('================================================================================');
|
|
727
|
-
console.log('| Planet 1 | Planet 2 | Aspect | Orb |
|
|
723
|
+
console.log('| Planet 1 | Planet 2 | Aspect | Orb |');
|
|
728
724
|
console.log('================================================================================');
|
|
729
725
|
|
|
730
726
|
if (aspects.length === 0) {
|
|
@@ -738,14 +734,8 @@ function showPlanetComboAspects(planetNames, dateComponents, useBirthData = fals
|
|
|
738
734
|
const aspectName = aspect.type.padEnd(11, ' ');
|
|
739
735
|
const orb = aspect.orb.padEnd(4, ' ');
|
|
740
736
|
|
|
741
|
-
//
|
|
742
|
-
|
|
743
|
-
if (exactDateTime) {
|
|
744
|
-
const dateTimeStr = `${String(exactDateTime.day).padStart(2, '0')}.${String(exactDateTime.month).padStart(2, '0')}.${exactDateTime.year} ${String(exactDateTime.hour).padStart(2, '0')}:${String(exactDateTime.minute).padStart(2, '0')}`;
|
|
745
|
-
console.log(`| ${planet1Padded} | ${planet2Padded} | ${aspectName} | ${orb}° | ${dateTimeStr} |`);
|
|
746
|
-
} else {
|
|
747
|
-
console.log(`| ${planet1Padded} | ${planet2Padded} | ${aspectName} | ${orb}° | Calculation failed |`);
|
|
748
|
-
}
|
|
737
|
+
// Exact time calculation removed
|
|
738
|
+
console.log(`| ${planet1Padded} | ${planet2Padded} | ${aspectName} | ${orb}° |`);
|
|
749
739
|
});
|
|
750
740
|
}
|
|
751
741
|
|
|
@@ -758,107 +748,7 @@ function showPlanetComboAspects(planetNames, dateComponents, useBirthData = fals
|
|
|
758
748
|
}
|
|
759
749
|
}
|
|
760
750
|
|
|
761
|
-
// Helper function to calculate exact date and time for an exact aspect
|
|
762
|
-
function findExactAspectTime(planet1, planet2, aspectType, startDate) {
|
|
763
|
-
const targetAngle = getAspectAngle(aspectType);
|
|
764
|
-
|
|
765
|
-
if (targetAngle === null) {
|
|
766
|
-
return null;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
// We search in a reasonable time window (90 days in the future)
|
|
770
|
-
// to find the next exact aspect
|
|
771
|
-
// Especially important for slow planets like Saturn, Uranus, Neptune, Pluto
|
|
772
|
-
let bestMatch = null;
|
|
773
|
-
let bestDistance = Infinity;
|
|
774
|
-
|
|
775
|
-
// Search only in the future (0-90 days after start date)
|
|
776
|
-
const searchWindow = 90; // days
|
|
777
|
-
|
|
778
|
-
// We search in smaller steps (every 2 hours) to get more precise results
|
|
779
|
-
const stepsPerDay = 12; // 2-hour steps
|
|
780
|
-
|
|
781
|
-
for (let daysOffset = 0; daysOffset <= searchWindow; daysOffset++) {
|
|
782
|
-
for (let hourOffset = 0; hourOffset < 24; hourOffset++) { // Every hour
|
|
783
|
-
for (let minuteOffset = 0; minuteOffset < 60; minuteOffset += 5) { // Every 5 minutes
|
|
784
|
-
const testDate = addDays(startDate, daysOffset);
|
|
785
|
-
testDate.hour = hourOffset;
|
|
786
|
-
testDate.minute = minuteOffset;
|
|
787
|
-
|
|
788
|
-
// Calculate positions of both planets
|
|
789
|
-
const planet1Data = getAstrologicalData(planet1, testDate);
|
|
790
|
-
const planet2Data = getAstrologicalData(planet2, testDate);
|
|
791
|
-
|
|
792
|
-
// Calculate angle between planets
|
|
793
|
-
const angleDiff = Math.abs(planet1Data.longitude - planet2Data.longitude) % 360;
|
|
794
|
-
const normalizedAngle = Math.min(angleDiff, 360 - angleDiff);
|
|
795
|
-
const distanceToTarget = Math.abs(normalizedAngle - targetAngle);
|
|
796
|
-
|
|
797
|
-
// Store the best result
|
|
798
|
-
if (distanceToTarget < bestDistance) {
|
|
799
|
-
bestDistance = distanceToTarget;
|
|
800
|
-
bestMatch = {...testDate};
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
// If we found an aspect close enough to the target, return it
|
|
807
|
-
// Stricter tolerance of 0.5° for better accuracy
|
|
808
|
-
if (bestMatch && bestDistance <= 0.5) { // Tolerance of 0.5°
|
|
809
|
-
return bestMatch;
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
return null;
|
|
813
|
-
}
|
|
814
751
|
|
|
815
|
-
// Function to find the last exact aspect in the past (for separative phases)
|
|
816
|
-
function findLastExactAspectTime(planet1, planet2, aspectType, startDate) {
|
|
817
|
-
const targetAngle = getAspectAngle(aspectType);
|
|
818
|
-
|
|
819
|
-
if (targetAngle === null) {
|
|
820
|
-
return null;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
// Search in the past (up to 180 days before start date)
|
|
824
|
-
let bestMatch = null;
|
|
825
|
-
let bestDistance = Infinity;
|
|
826
|
-
|
|
827
|
-
// Search in a time window of 180 days before start date
|
|
828
|
-
const searchWindow = 180; // days
|
|
829
|
-
|
|
830
|
-
for (let daysOffset = -searchWindow; daysOffset <= 0; daysOffset++) {
|
|
831
|
-
for (let hourOffset = 0; hourOffset < 24; hourOffset++) { // Every hour
|
|
832
|
-
for (let minuteOffset = 0; minuteOffset < 60; minuteOffset += 5) { // Every 5 minutes
|
|
833
|
-
const testDate = addDays(startDate, daysOffset);
|
|
834
|
-
testDate.hour = hourOffset;
|
|
835
|
-
testDate.minute = minuteOffset;
|
|
836
|
-
|
|
837
|
-
// Calculate positions of both planets
|
|
838
|
-
const planet1Data = getAstrologicalData(planet1, testDate);
|
|
839
|
-
const planet2Data = getAstrologicalData(planet2, testDate);
|
|
840
|
-
|
|
841
|
-
// Calculate angle between planets
|
|
842
|
-
const angleDiff = Math.abs(planet1Data.longitude - planet2Data.longitude) % 360;
|
|
843
|
-
const normalizedAngle = Math.min(angleDiff, 360 - angleDiff);
|
|
844
|
-
const distanceToTarget = Math.abs(normalizedAngle - targetAngle);
|
|
845
|
-
|
|
846
|
-
// Store the best result
|
|
847
|
-
if (distanceToTarget < bestDistance) {
|
|
848
|
-
bestDistance = distanceToTarget;
|
|
849
|
-
bestMatch = {...testDate};
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// If we found an aspect close enough to the target, return it
|
|
856
|
-
if (bestMatch && bestDistance <= 0.5) { // Tolerance of 0.5°
|
|
857
|
-
return bestMatch;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
return null;
|
|
861
|
-
}
|
|
862
752
|
|
|
863
753
|
// Function to calculate all active aspects between all planets
|
|
864
754
|
function getAllActiveAspects(dateComponents) {
|
|
@@ -1775,7 +1665,7 @@ function showAllActiveAspects(dateComponents, useBirthData = false) {
|
|
|
1775
1665
|
|
|
1776
1666
|
console.log('Active Aspects (all planets) - Classified by Precision:');
|
|
1777
1667
|
console.log('═══════════════════════════════════════════════════════════════════════════');
|
|
1778
|
-
console.log('║ Planet 1 │ Planet 2 │ Orb │ Status
|
|
1668
|
+
console.log('║ Planet 1 │ Planet 2 │ Orb │ Status ║');
|
|
1779
1669
|
console.log('═══════════════════════════════════════════════════════════════════════════');
|
|
1780
1670
|
|
|
1781
1671
|
if (sortedAspects.length === 0) {
|
|
@@ -1826,14 +1716,10 @@ function showAllActiveAspects(dateComponents, useBirthData = false) {
|
|
|
1826
1716
|
);
|
|
1827
1717
|
const statusPadded = phase.padEnd(11, ' ');
|
|
1828
1718
|
|
|
1829
|
-
//
|
|
1830
|
-
const
|
|
1831
|
-
let dateTimeStr = '-';
|
|
1832
|
-
if (exactDateTime) {
|
|
1833
|
-
dateTimeStr = `${String(exactDateTime.day).padStart(2, '0')}.${String(exactDateTime.month).padStart(2, '0')}.${exactDateTime.year} ${String(exactDateTime.hour).padStart(2, '0')}:${String(exactDateTime.minute).padStart(2, '0')}`;
|
|
1834
|
-
}
|
|
1719
|
+
// Exact time calculation removed
|
|
1720
|
+
const dateTimeStr = '-';
|
|
1835
1721
|
|
|
1836
|
-
console.log(`║ ${planet1Padded} │ ${planet2Padded} │ ${orb}° │ ${statusPadded}
|
|
1722
|
+
console.log(`║ ${planet1Padded} │ ${planet2Padded} │ ${orb}° │ ${statusPadded} ║`);
|
|
1837
1723
|
});
|
|
1838
1724
|
}
|
|
1839
1725
|
});
|
|
@@ -2953,8 +2839,6 @@ module.exports = {
|
|
|
2953
2839
|
getPastAspects,
|
|
2954
2840
|
getAspectAngle,
|
|
2955
2841
|
getFutureAspects,
|
|
2956
|
-
findExactAspectTime,
|
|
2957
|
-
findLastExactAspectTime,
|
|
2958
2842
|
analyzeCSVWithDatetime,
|
|
2959
2843
|
detectAspectFigures,
|
|
2960
2844
|
showAspectFigures,
|