klio 1.4.9 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +9 -0
- package/.nvmrc +1 -0
- package/Dockerfile +18 -0
- package/README.md +22 -0
- package/compose.yaml +6 -0
- package/package.json +8 -3
- package/src/astrology/astrologyConstants.js +7 -0
- package/src/astrology/astrologyService.js +327 -302
- package/src/astrology/astrologyServiceWeb.js +369 -0
- package/src/astrology/swephWasmLoader.js +106 -0
- package/src/astrology/swissephAdapter.js +279 -0
- package/src/cli/cli.js +148 -152
- package/src/cli/cliService.js +1197 -0
- package/src/cli/cliServiceWeb.js +406 -0
- package/src/config/configService.js +59 -35
- package/src/gui/public/index.html +839 -298
- package/src/gui/public/sweph/astro.data +0 -0
- package/src/gui/public/sweph/astro.js +3934 -0
- package/src/gui/public/sweph/astro.wasm +0 -0
- package/src/gui/public/tailwind.css +3 -0
- package/src/gui/public/tailwind.generated.css +1 -0
- package/src/gui/public/webcontainerService.js +435 -0
- package/src/gui/routes/api.js +64 -101
- package/src/gui/server.js +80 -31
- package/src/gui/webcontainerSetup.js +244 -0
- package/src/health/fileAnalysis.js +2 -2
- package/commands.db +0 -0
- package/src/gui/commandLogger.js +0 -67
- package/src/gui/database.js +0 -135
|
@@ -0,0 +1,1197 @@
|
|
|
1
|
+
// CLI Service - Modular version of CLI functionality that can be imported
|
|
2
|
+
// This allows the GUI to run commands without spawning child processes
|
|
3
|
+
|
|
4
|
+
const { Command } = require('commander');
|
|
5
|
+
const { planets, signs } = require('../astrology/astrologyConstants');
|
|
6
|
+
const { showRetrogradePlanets } = require('../astrology/retrogradeService');
|
|
7
|
+
const { getCurrentTimeInTimezone, showAspectFigures, analyzeElementDistribution, getTimezoneOffset, calculateJulianDayUTC, calculateHouses, getAstrologicalData, getPlanetHouse, showPlanetAspects, calculatePlanetAspects, getAllActiveAspects, showAllActiveAspects, getBirthDataFromConfig, getPersonDataFromConfig, detectAspectFigures, calculatePersonalTransits, showPersonalTransitAspects, showCombinedAnalysis, calculatePersonalTransitAspects, determineAspectPhase, getAspectAngle, getFutureAspects, getPastAspects, analyzeCSVWithDatetime, analyzeHouseDistributionSignificance, analyzeAspectDistributionSignificance, analyzeSignDistributionSignificance, calculateAspectStatistics, calculatePlanetComboAspects, showPlanetComboAspects, getCriticalPlanets, getHouseSystemCode, calculateNextPlanetIngress, calculateAstrologicalAngles, longitudeToSignDegree, calculateTransitFrequency } = require('../astrology/astrologyService');
|
|
8
|
+
const { performSetup, showConfigStatus, loadConfig, setAIModel, askAIModel, setPerson1, setPerson2, setPerson, getPersonData, listPeople, deletePerson } = require('../config/configService');
|
|
9
|
+
const { parseAppleHealthXML } = require('../health/healthService');
|
|
10
|
+
const { analyzeStepsByPlanetSign, analyzeStressByPlanetAspects, analyzePlanetAspectsForSleep, analyzeLateNightAspects, analyzeAllNighterAspects } = require('../health/healthAnalysis');
|
|
11
|
+
const { getFileCreationDate, parseDateToComponents } = require('../utils/fileUtils');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
|
|
15
|
+
// Import chart generation and downloads folder utilities
|
|
16
|
+
let chartGenerator = null;
|
|
17
|
+
let downloadsFolder = null;
|
|
18
|
+
try {
|
|
19
|
+
chartGenerator = require('../utils/chartGenerator');
|
|
20
|
+
downloadsFolder = require('../utils/downloadsFolder');
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.debug('Chart generation utilities not available');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Wikidata service import
|
|
26
|
+
let wikidataService = null;
|
|
27
|
+
try {
|
|
28
|
+
wikidataService = require('../wikidata/wikidataService');
|
|
29
|
+
} catch (error) {
|
|
30
|
+
// Wikidata service not available
|
|
31
|
+
console.debug('Wikidata service not available');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getAspectTypeFullName(aspectType) {
|
|
35
|
+
const aspectNames = {
|
|
36
|
+
c: 'Conjunctions',
|
|
37
|
+
conjunction: 'Conjunctions',
|
|
38
|
+
o: 'Oppositions',
|
|
39
|
+
opposition: 'Oppositions',
|
|
40
|
+
s: 'Squares',
|
|
41
|
+
square: 'Squares',
|
|
42
|
+
t: 'Trines',
|
|
43
|
+
trine: 'Trines',
|
|
44
|
+
se: 'Sextiles',
|
|
45
|
+
sextile: 'Sextiles'
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const key = aspectType ? aspectType.toLowerCase() : '';
|
|
49
|
+
return aspectNames[key] || aspectType;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function splitCommandArgs(commandString) {
|
|
53
|
+
const args = [];
|
|
54
|
+
let current = '';
|
|
55
|
+
let quoteChar = null;
|
|
56
|
+
let escaped = false;
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < commandString.length; i++) {
|
|
59
|
+
const char = commandString[i];
|
|
60
|
+
|
|
61
|
+
if (escaped) {
|
|
62
|
+
current += char;
|
|
63
|
+
escaped = false;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (char === '\\') {
|
|
68
|
+
escaped = true;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (quoteChar) {
|
|
73
|
+
if (char === quoteChar) {
|
|
74
|
+
quoteChar = null;
|
|
75
|
+
} else {
|
|
76
|
+
current += char;
|
|
77
|
+
}
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (char === '"' || char === "'") {
|
|
82
|
+
quoteChar = char;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (/\s/.test(char)) {
|
|
87
|
+
if (current.length) {
|
|
88
|
+
args.push(current);
|
|
89
|
+
current = '';
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
current += char;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (current.length) {
|
|
98
|
+
args.push(current);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return args;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class CLIService {
|
|
105
|
+
constructor() {
|
|
106
|
+
this.program = new Command();
|
|
107
|
+
this.setupProgram();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
setupProgram() {
|
|
111
|
+
this.program
|
|
112
|
+
.name('klio')
|
|
113
|
+
.description('A simple CLI for astrological data')
|
|
114
|
+
.version('1.0.0');
|
|
115
|
+
|
|
116
|
+
this.program
|
|
117
|
+
.argument('[planet]', 'First planet for astrological calculations')
|
|
118
|
+
.argument('[planet2]', 'Second planet for aspect calculations')
|
|
119
|
+
.allowExcessArguments(true)
|
|
120
|
+
.option('--hs <system>', 'House system (Placidus, Koch, Porphyry, Regiomontanus, Campanus, Equal, WholeSign, Gauquelin, Vehlow, Topocentric, Alcabitius, Morinus) - shows planet + house position')
|
|
121
|
+
.option('--hl <system>', 'House list (Placidus, Koch, Porphyry, Regiomontanus, Campanus, Equal, WholeSign, Gauquelin, Vehlow, Topocentric, Alcabitius, Morinus) - shows only house table')
|
|
122
|
+
.option('--d <date>', 'Use a specific date for calculations (Format: DD.MM.YYYY or "DD.MM.YYYY HH:MM")')
|
|
123
|
+
.option('--a', 'Shows all aspects of the planet')
|
|
124
|
+
.option('--k', 'Shows aspects between any planet combinations')
|
|
125
|
+
.option('--s', 'Shows all planet positions in a table with signs, degrees and houses')
|
|
126
|
+
.option('--apple <filepath>', 'Analyzes Apple Health Export XML file')
|
|
127
|
+
.option('--hypothesis <type>', 'Specifies the hypothesis to test (e.g. "sleep-moon", "steps-moon", "stress-moon")')
|
|
128
|
+
.option('--sleep', 'Analyzes sleep patterns with moon aspects')
|
|
129
|
+
.option('--steps', 'Analyzes step patterns with moon signs')
|
|
130
|
+
.option('--stress', 'Analyzes stress patterns with moon aspects based on HRV')
|
|
131
|
+
.option('--night', 'Analyzes late night sleep patterns (after 2am) and common aspects')
|
|
132
|
+
.option('--night-all', 'Analyzes all-nighter sleep patterns (04:00 and 06:00 starts)')
|
|
133
|
+
.option('--setup', 'Configures location and birth data for personalized calculations')
|
|
134
|
+
.option('--ai <model>', 'Sets a specific AI model (e.g. "google/gemma-3n-e4b")')
|
|
135
|
+
.option('--system <prompt>', 'Sets a custom system prompt for all AI requests')
|
|
136
|
+
.option('--rx', 'Shows all retrograde or stationary planets')
|
|
137
|
+
.option('--c', 'Shows all planets on critical degrees')
|
|
138
|
+
.option('--status', 'Shows the stored configuration data')
|
|
139
|
+
.option('--i', 'Uses the birth data from setup for calculations (short form)')
|
|
140
|
+
.option('--p1', 'Uses the data from person 1 for calculations')
|
|
141
|
+
.option('--p2', 'Uses the data from person 2 for calculations')
|
|
142
|
+
.option('--p3', 'Uses the data from person 3 for calculations')
|
|
143
|
+
.option('--p4', 'Uses the data from person 4 for calculations')
|
|
144
|
+
.option('--p5', 'Uses the data from person 5 for calculations')
|
|
145
|
+
.option('--p6', 'Uses the data from person 6 for calculations')
|
|
146
|
+
.option('--with-person <id>', 'Uses the data from a specific person ID for calculations')
|
|
147
|
+
.option('--wp <id>', 'Uses the data from a specific person ID for calculations (short form)')
|
|
148
|
+
.option('--up <id>', 'Uses the data from a specific person ID for calculations (short form)')
|
|
149
|
+
.option('--person1 <data>', 'Creates or updates person 1 (Format: "Location, Country, DD.MM.YYYY HH:MM")')
|
|
150
|
+
.option('--person2 <data>', 'Creates or updates person 2 (Format: "Location, Country, DD.MM.YYYY HH:MM")')
|
|
151
|
+
.option('--person <id> <data>', 'Creates or updates any person (Format: "Location, Country, DD.MM.YYYY HH:MM")')
|
|
152
|
+
.option('--people', 'Lists all saved persons')
|
|
153
|
+
.option('--delete-person <id>', 'Deletes a person')
|
|
154
|
+
.option('--t <days>', 'Time limit (e.g. 7d, 30d, 90d, 14d)')
|
|
155
|
+
.option('--p <prompt>', 'Asks a question to the AI model (e.g. "Which careers suit this position?")')
|
|
156
|
+
.option('--el', 'Shows the element distribution of planets in a horizontal chart')
|
|
157
|
+
.option('--af', 'Shows active aspect figures like T-squares, Grand Trines, etc.')
|
|
158
|
+
.option('--tra', 'Shows personal transits based on birth data')
|
|
159
|
+
.option('--tr', 'Shows personal transit aspects (Transit → Radix) (short form)')
|
|
160
|
+
.option('--v <count>', 'Shows past aspects between two planets (Format: --v <count> planet1 aspectType planet2)')
|
|
161
|
+
.option('--z <count>', 'Shows future aspects between two planets (Format: --z <count> planet1 aspectType planet2)')
|
|
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")')
|
|
164
|
+
.option('--title <title>', 'Title for the chart image (generates PNG image when provided)')
|
|
165
|
+
.option('--in [count]', 'Shows next planet ingress (entering new sign). Optional count for multiple ingresses')
|
|
166
|
+
.option('--wiki <occupation>', 'Fetches people from Wikidata by occupation and checks for specific aspects (Format: planet1 aspectType planet2 --wiki <occupation> [limit])')
|
|
167
|
+
.option('--gui', 'Launches the web interface for command history (port 37421)')
|
|
168
|
+
.option('--gui-port <port>', 'Specify custom port for GUI server')
|
|
169
|
+
.option('--o', 'Shows how often this transit occurs in the sky (frequency calculation)')
|
|
170
|
+
.description('Shows astrological data for a planet')
|
|
171
|
+
.action(this.handleCommand.bind(this));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Helper function to check if person data should be used (including new options)
|
|
175
|
+
shouldUsePersonData(options) {
|
|
176
|
+
return options.i || options.p1 || options.p2 || options.withPerson || options.wp || options.up;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Helper function to determine which person data should be used
|
|
180
|
+
getPersonDataToUse(options) {
|
|
181
|
+
const userId = options._userId;
|
|
182
|
+
|
|
183
|
+
// Check for direct person ID usage (e.g., --with-person john or --wp john or --up john)
|
|
184
|
+
if (options.withPerson) {
|
|
185
|
+
const personData = getPersonDataFromConfig(options.withPerson, userId);
|
|
186
|
+
if (!personData) {
|
|
187
|
+
throw new Error(`Person "${options.withPerson}" not found.`);
|
|
188
|
+
}
|
|
189
|
+
return { source: options.withPerson, data: personData };
|
|
190
|
+
}
|
|
191
|
+
if (options.wp) {
|
|
192
|
+
const personData = getPersonDataFromConfig(options.wp, userId);
|
|
193
|
+
if (!personData) {
|
|
194
|
+
throw new Error(`Person "${options.wp}" not found.`);
|
|
195
|
+
}
|
|
196
|
+
return { source: options.wp, data: personData };
|
|
197
|
+
}
|
|
198
|
+
if (options.up) {
|
|
199
|
+
const personData = getPersonDataFromConfig(options.up, userId);
|
|
200
|
+
if (!personData) {
|
|
201
|
+
throw new Error(`Person "${options.up}" not found.`);
|
|
202
|
+
}
|
|
203
|
+
return { source: options.up, data: personData };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Dynamic person names (e.g., --p3, --p4, etc.)
|
|
207
|
+
for (const [key, value] of Object.entries(options)) {
|
|
208
|
+
if (key.startsWith('p') && key.length > 1 && !isNaN(key.substring(1))) {
|
|
209
|
+
const personId = key.substring(1); // Extract the number
|
|
210
|
+
return { source: key, data: getPersonDataFromConfig(personId, userId) };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (options.p1) {
|
|
215
|
+
return { source: 'p1', data: getPersonDataFromConfig('p1', userId) };
|
|
216
|
+
} else if (options.p2) {
|
|
217
|
+
return { source: 'p2', data: getPersonDataFromConfig('p2', userId) };
|
|
218
|
+
} else if (options.p3) {
|
|
219
|
+
return { source: 'p3', data: getPersonDataFromConfig('p3', userId) };
|
|
220
|
+
} else if (options.p4) {
|
|
221
|
+
return { source: 'p4', data: getPersonDataFromConfig('p4', userId) };
|
|
222
|
+
} else if (options.p5) {
|
|
223
|
+
return { source: 'p5', data: getPersonDataFromConfig('p5', userId) };
|
|
224
|
+
} else if (options.p6) {
|
|
225
|
+
return { source: 'p6', data: getPersonDataFromConfig('p6', userId) };
|
|
226
|
+
} else if (options.i) {
|
|
227
|
+
return { source: 'birth', data: getPersonDataFromConfig('birth', userId) };
|
|
228
|
+
}
|
|
229
|
+
return { source: null, data: null };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Helper function to check if birth data should be used (for backward compatibility only)
|
|
233
|
+
shouldUseBirthData(options) {
|
|
234
|
+
return options.i;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Function to calculate the Julian Day in UTC
|
|
238
|
+
getJulianDay(customDate = null, useBirthData = false) {
|
|
239
|
+
let timezoneOffsetMinutes = 0;
|
|
240
|
+
const config = loadConfig();
|
|
241
|
+
|
|
242
|
+
if (customDate) {
|
|
243
|
+
// Calculate the timezone offset
|
|
244
|
+
if (useBirthData && config && config.birthData && config.birthData.location) {
|
|
245
|
+
timezoneOffsetMinutes = getTimezoneOffset(customDate, config.birthData.location.timezone || 'Europe/Zurich');
|
|
246
|
+
} else if (config && config.currentLocation && config.currentLocation.timezone) {
|
|
247
|
+
timezoneOffsetMinutes = getTimezoneOffset(customDate, config.currentLocation.timezone);
|
|
248
|
+
} else {
|
|
249
|
+
timezoneOffsetMinutes = -new Date().getTimezoneOffset();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return calculateJulianDayUTC(customDate, timezoneOffsetMinutes);
|
|
253
|
+
} else {
|
|
254
|
+
// Use the current time in the configured timezone
|
|
255
|
+
let now;
|
|
256
|
+
let timezone = 'Europe/Zurich'; // Default
|
|
257
|
+
|
|
258
|
+
if (config && config.currentLocation && config.currentLocation.timezone) {
|
|
259
|
+
timezone = config.currentLocation.timezone;
|
|
260
|
+
try {
|
|
261
|
+
const moment = require('moment-timezone');
|
|
262
|
+
now = moment().tz(timezone);
|
|
263
|
+
console.log(`Using timezone: ${timezone}`);
|
|
264
|
+
} catch (error) {
|
|
265
|
+
console.log('Could not load moment-timezone, using local time');
|
|
266
|
+
now = require('moment')();
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
now = require('moment')();
|
|
270
|
+
console.log('No timezone configuration found, using local system time');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
timezoneOffsetMinutes = now.utcOffset();
|
|
274
|
+
return calculateJulianDayUTC({
|
|
275
|
+
year: now.year(),
|
|
276
|
+
month: now.month() + 1,
|
|
277
|
+
day: now.date(),
|
|
278
|
+
hour: now.hours(),
|
|
279
|
+
minute: now.minutes()
|
|
280
|
+
}, timezoneOffsetMinutes);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Main command handler
|
|
285
|
+
async handleCommand(planetArg, planet2Arg, options, userId = null) {
|
|
286
|
+
// If planet2Arg is an object, it contains the options (commander behavior)
|
|
287
|
+
let planet2 = typeof planet2Arg === 'string' ? planet2Arg : null;
|
|
288
|
+
// Fix: Check for null explicitly since typeof null === 'object' in JavaScript
|
|
289
|
+
let actualOptions = (typeof planet2Arg === 'object' && planet2Arg !== null) ? planet2Arg : options;
|
|
290
|
+
|
|
291
|
+
// Ensure actualOptions is an object
|
|
292
|
+
if (!actualOptions) {
|
|
293
|
+
actualOptions = {};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Store userId in the options for use in config functions
|
|
297
|
+
actualOptions._userId = userId;
|
|
298
|
+
|
|
299
|
+
// Capture output for GUI use
|
|
300
|
+
const output = [];
|
|
301
|
+
const originalConsoleLog = console.log;
|
|
302
|
+
const originalConsoleError = console.error;
|
|
303
|
+
|
|
304
|
+
// Override console methods to capture output
|
|
305
|
+
const captureOnly = process.env.KLIO_CAPTURE_ONLY === '1';
|
|
306
|
+
|
|
307
|
+
console.log = (...args) => {
|
|
308
|
+
if (!captureOnly) {
|
|
309
|
+
originalConsoleLog(...args);
|
|
310
|
+
}
|
|
311
|
+
output.push(args.join(' '));
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
console.error = (...args) => {
|
|
315
|
+
if (!captureOnly) {
|
|
316
|
+
originalConsoleError(...args);
|
|
317
|
+
}
|
|
318
|
+
output.push(args.join(' '));
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
// Handle GUI launch if --gui flag is specified
|
|
323
|
+
if (options.gui) {
|
|
324
|
+
throw new Error('GUI mode cannot be launched through CLI service');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Show critical planets if --c option is specified (no planet required)
|
|
328
|
+
if (actualOptions.c) {
|
|
329
|
+
// Use person data if --p1, --p2 or --i option is specified
|
|
330
|
+
let customDate = null;
|
|
331
|
+
let personSource = null;
|
|
332
|
+
const personDataToUse = this.getPersonDataToUse(actualOptions);
|
|
333
|
+
|
|
334
|
+
if (personDataToUse.data) {
|
|
335
|
+
customDate = personDataToUse.data;
|
|
336
|
+
personSource = personDataToUse.source;
|
|
337
|
+
|
|
338
|
+
if (personSource === 'p1') {
|
|
339
|
+
console.log(`Using Person 1 data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
340
|
+
} else if (personSource === 'p2') {
|
|
341
|
+
console.log(`Using Person 2 data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
342
|
+
} else if (personSource.startsWith('p')) {
|
|
343
|
+
const personId = personSource.substring(1);
|
|
344
|
+
console.log(`Using Person ${personId} data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
345
|
+
} else {
|
|
346
|
+
console.log(`Using birth data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (customDate.location) {
|
|
350
|
+
console.log(`Location: ${customDate.location.name}, ${customDate.location.country}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Use custom date if specified (overrides person data)
|
|
355
|
+
if (actualOptions.d) {
|
|
356
|
+
// Try to parse the date as DD.MM.YYYY or DD.MM.YYYY HH:MM
|
|
357
|
+
const dateRegex = /^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/;
|
|
358
|
+
const match = actualOptions.d.match(dateRegex);
|
|
359
|
+
|
|
360
|
+
if (match) {
|
|
361
|
+
const day = parseInt(match[1], 10);
|
|
362
|
+
const month = parseInt(match[2], 10);
|
|
363
|
+
const year = parseInt(match[3], 10);
|
|
364
|
+
const hour = match[4] ? parseInt(match[4], 10) : 12; // Default: 12 o'clock
|
|
365
|
+
const minute = match[5] ? parseInt(match[5], 10) : 0; // Default: 0 minutes
|
|
366
|
+
|
|
367
|
+
// Check if the date is valid
|
|
368
|
+
const date = new Date(year, month - 1, day, hour, minute);
|
|
369
|
+
if (date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day) {
|
|
370
|
+
customDate = {
|
|
371
|
+
day: day,
|
|
372
|
+
month: month,
|
|
373
|
+
year: year,
|
|
374
|
+
hour: hour,
|
|
375
|
+
minute: minute
|
|
376
|
+
};
|
|
377
|
+
console.log(`Using custom date: ${day}.${month}.${year} ${hour}:${minute.toString().padStart(2, '0')}`);
|
|
378
|
+
} else {
|
|
379
|
+
console.error('Invalid date:', actualOptions.d);
|
|
380
|
+
console.error('💡 Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
381
|
+
return { success: false, output: output.join('\n') };
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
console.error('Invalid date:', actualOptions.d);
|
|
385
|
+
console.error('💡 Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
386
|
+
return { success: false, output: output.join('\n') };
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// If no custom date is specified, use current date
|
|
391
|
+
if (!customDate) {
|
|
392
|
+
const currentTime = getCurrentTimeInTimezone();
|
|
393
|
+
customDate = {
|
|
394
|
+
day: currentTime.day,
|
|
395
|
+
month: currentTime.month,
|
|
396
|
+
year: currentTime.year,
|
|
397
|
+
hour: currentTime.hour,
|
|
398
|
+
minute: currentTime.minute
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const criticalPlanets = getCriticalPlanets(customDate);
|
|
403
|
+
|
|
404
|
+
if (criticalPlanets.length === 0) {
|
|
405
|
+
console.log('No planets on critical degrees found.');
|
|
406
|
+
return { success: true, output: output.join('\n') };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
console.log('================================================================================================================');
|
|
410
|
+
console.log('| Planet | Sign | Degree | Type | Interpretation |');
|
|
411
|
+
console.log('================================================================================================================');
|
|
412
|
+
|
|
413
|
+
criticalPlanets.forEach(planet => {
|
|
414
|
+
const planetName = planet.name.charAt(0).toUpperCase() + planet.name.slice(1);
|
|
415
|
+
const sign = planet.sign.padEnd(10, ' ');
|
|
416
|
+
const degree = planet.degree.padEnd(5, ' ');
|
|
417
|
+
const criticalType = planet.criticalType.padEnd(20, ' ');
|
|
418
|
+
const interpretation = planet.interpretation.padEnd(46, ' ');
|
|
419
|
+
console.log(`| ${planetName.padEnd(8)} | ${sign} | ${degree} | ${criticalType} | ${interpretation} |`);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
console.log('================================================================================================================');
|
|
423
|
+
if (this.shouldUseBirthData(actualOptions)) {
|
|
424
|
+
console.log('\nThis analysis is based on your birth chart.');
|
|
425
|
+
} else {
|
|
426
|
+
console.log('\nThis analysis is based on the current planet position.');
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return { success: true, output: output.join('\n') };
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Show retrograde planets if --rx option is specified (no planet required)
|
|
433
|
+
if (actualOptions.rx) {
|
|
434
|
+
await showRetrogradePlanets(this.shouldUseBirthData(actualOptions));
|
|
435
|
+
return { success: true, output: output.join('\n') };
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Show element distribution if --el option is specified (no planet required)
|
|
439
|
+
if (actualOptions.el) {
|
|
440
|
+
// Use person data if --p1, --p2 or --i option is specified
|
|
441
|
+
let customDate = null;
|
|
442
|
+
let useBirthData = false;
|
|
443
|
+
let personSource = null;
|
|
444
|
+
|
|
445
|
+
const personDataToUse = this.getPersonDataToUse(actualOptions);
|
|
446
|
+
if (personDataToUse.data) {
|
|
447
|
+
customDate = personDataToUse.data;
|
|
448
|
+
personSource = personDataToUse.source;
|
|
449
|
+
useBirthData = personSource === 'birth'; // Only true for birth data
|
|
450
|
+
|
|
451
|
+
if (personSource === 'p1') {
|
|
452
|
+
console.log(`Using Person 1 data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
453
|
+
} else if (personSource === 'p2') {
|
|
454
|
+
console.log(`Using Person 2 data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
455
|
+
} else if (personSource.startsWith('p')) {
|
|
456
|
+
const personId = personSource.substring(1);
|
|
457
|
+
console.log(`Using Person ${personId} data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
458
|
+
} else {
|
|
459
|
+
console.log(`Using birth data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (customDate.location) {
|
|
463
|
+
console.log(`Location: ${customDate.location.name}, ${customDate.location.country}`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
// Use custom date if specified (overrides --p1, --p2 and --i options)
|
|
467
|
+
if (actualOptions.d) {
|
|
468
|
+
// Try to parse the date as DD.MM.YYYY or DD.MM.YYYY HH:MM
|
|
469
|
+
const dateRegex = /^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/;
|
|
470
|
+
const match = actualOptions.d.match(dateRegex);
|
|
471
|
+
|
|
472
|
+
if (match) {
|
|
473
|
+
const day = parseInt(match[1], 10);
|
|
474
|
+
const month = parseInt(match[2], 10);
|
|
475
|
+
const year = parseInt(match[3], 10);
|
|
476
|
+
const hour = match[4] ? parseInt(match[4], 10) : 12; // Default: 12 o'clock
|
|
477
|
+
const minute = match[5] ? parseInt(match[5], 10) : 0; // Default: 0 minutes
|
|
478
|
+
|
|
479
|
+
// Check if the date is valid
|
|
480
|
+
const date = new Date(year, month - 1, day, hour, minute);
|
|
481
|
+
if (date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day) {
|
|
482
|
+
customDate = {
|
|
483
|
+
day: day,
|
|
484
|
+
month: month,
|
|
485
|
+
year: year,
|
|
486
|
+
hour: hour,
|
|
487
|
+
minute: minute
|
|
488
|
+
};
|
|
489
|
+
console.log(`Using custom date: ${day}.${month}.${year} ${hour}:${minute.toString().padStart(2, '0')}`);
|
|
490
|
+
} else {
|
|
491
|
+
console.error('Invalid date:', actualOptions.d);
|
|
492
|
+
console.error('💡 Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
493
|
+
return { success: false, output: output.join('\n') };
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
console.error('Invalid date:', actualOptions.d);
|
|
497
|
+
console.error('💡 Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
498
|
+
return { success: false, output: output.join('\n') };
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// If no custom date is specified, use current date
|
|
503
|
+
if (!customDate) {
|
|
504
|
+
const currentTime = getCurrentTimeInTimezone();
|
|
505
|
+
customDate = {
|
|
506
|
+
day: currentTime.day,
|
|
507
|
+
month: currentTime.month,
|
|
508
|
+
year: currentTime.year,
|
|
509
|
+
hour: currentTime.hour,
|
|
510
|
+
minute: currentTime.minute
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Analyze and show element distribution
|
|
515
|
+
analyzeElementDistribution(customDate, useBirthData);
|
|
516
|
+
|
|
517
|
+
return { success: true, output: output.join('\n') };
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Show personal transits if --tra option is specified (no planet required)
|
|
521
|
+
if (actualOptions.tra) {
|
|
522
|
+
// Person data is required for transits
|
|
523
|
+
const personDataToUse = this.getPersonDataToUse(actualOptions);
|
|
524
|
+
if (!personDataToUse.data) {
|
|
525
|
+
console.error('Error: Personal transits require person data. Please run --setup or create a person with --person1/--person2.');
|
|
526
|
+
return { success: false, output: output.join('\n') };
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const birthData = personDataToUse.data;
|
|
530
|
+
|
|
531
|
+
// Use custom date if specified
|
|
532
|
+
let transitDate = null;
|
|
533
|
+
if (actualOptions.d) {
|
|
534
|
+
// Try to parse the date as DD.MM.YYYY or DD.MM.YYYY HH:MM
|
|
535
|
+
const dateRegex = /^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/;
|
|
536
|
+
const match = actualOptions.d.match(dateRegex);
|
|
537
|
+
|
|
538
|
+
if (match) {
|
|
539
|
+
const day = parseInt(match[1], 10);
|
|
540
|
+
const month = parseInt(match[2], 10);
|
|
541
|
+
const year = parseInt(match[3], 10);
|
|
542
|
+
const hour = match[4] ? parseInt(match[4], 10) : 12; // Default: 12 o'clock
|
|
543
|
+
const minute = match[5] ? parseInt(match[5], 10) : 0; // Default: 0 minutes
|
|
544
|
+
|
|
545
|
+
// Check if the date is valid
|
|
546
|
+
const date = new Date(year, month - 1, day, hour, minute);
|
|
547
|
+
if (date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day) {
|
|
548
|
+
transitDate = {
|
|
549
|
+
day: day,
|
|
550
|
+
month: month,
|
|
551
|
+
year: year,
|
|
552
|
+
hour: hour,
|
|
553
|
+
minute: minute
|
|
554
|
+
};
|
|
555
|
+
console.log(`Using transit date: ${day}.${month}.${year} ${hour}:${minute.toString().padStart(2, '0')}`);
|
|
556
|
+
} else {
|
|
557
|
+
console.error('Invalid date:', actualOptions.d);
|
|
558
|
+
console.error('💡 Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
559
|
+
return { success: false, output: output.join('\n') };
|
|
560
|
+
}
|
|
561
|
+
} else {
|
|
562
|
+
console.error('Invalid date:', actualOptions.d);
|
|
563
|
+
console.error('💡 Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
564
|
+
return { success: false, output: output.join('\n') };
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Calculate personal transits
|
|
569
|
+
const transitData = await calculatePersonalTransits(transitDate, birthData);
|
|
570
|
+
|
|
571
|
+
if (!transitData) {
|
|
572
|
+
console.error('Error calculating personal transits.');
|
|
573
|
+
return { success: false, output: output.join('\n') };
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Show person data
|
|
577
|
+
if (personDataToUse.source === 'p1') {
|
|
578
|
+
console.log(`Person 1 data: ${birthData.day}.${birthData.month}.${birthData.year} ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')}`);
|
|
579
|
+
} else if (personDataToUse.source === 'p2') {
|
|
580
|
+
console.log(`Person 2 data: ${birthData.day}.${birthData.month}.${birthData.year} ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')}`);
|
|
581
|
+
} else {
|
|
582
|
+
console.log(`Birth data: ${birthData.day}.${birthData.month}.${birthData.year} ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')}`);
|
|
583
|
+
}
|
|
584
|
+
if (birthData.location) {
|
|
585
|
+
console.log(`Location: ${birthData.location.name}, ${birthData.location.country}`);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Show transit date
|
|
589
|
+
const transitDateDisplay = transitDate || getCurrentTimeInTimezone();
|
|
590
|
+
console.log(`Transit date: ${transitDateDisplay.day}.${transitDateDisplay.month}.${transitDateDisplay.year} ${transitDateDisplay.hour}:${transitDateDisplay.minute.toString().padStart(2, '0')}`);
|
|
591
|
+
|
|
592
|
+
console.log('\nPersonal transits (House System: Koch, based on birth ASC):');
|
|
593
|
+
console.log('================================================================================');
|
|
594
|
+
console.log('| Planet | Transit Position | Birth Position | Transit House |');
|
|
595
|
+
console.log('================================================================================');
|
|
596
|
+
|
|
597
|
+
// Show all planets with their transit and birth positions
|
|
598
|
+
for (const [name, data] of Object.entries(transitData.planets)) {
|
|
599
|
+
const planetNameFormatted = name.charAt(0).toUpperCase() + name.slice(1);
|
|
600
|
+
const transitPos = `${data.sign} ${data.degreeInSign}°`;
|
|
601
|
+
const birthPos = `${data.birthPosition.sign} ${data.birthPosition.degreeInSign}°`;
|
|
602
|
+
const transitHouse = data.transitHouse;
|
|
603
|
+
|
|
604
|
+
console.log(`| ${planetNameFormatted.padEnd(11)} | ${transitPos.padEnd(22)} | ${birthPos.padEnd(22)} | ${transitHouse.toString().padEnd(11)} |`);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
console.log('================================================================================');
|
|
608
|
+
|
|
609
|
+
return { success: true, output: output.join('\n') };
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Show configuration status if --status option is specified (no planet required)
|
|
613
|
+
if (actualOptions.status) {
|
|
614
|
+
showConfigStatus(userId);
|
|
615
|
+
return { success: true, output: output.join('\n') };
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Perform setup if --setup option is specified (no planet required)
|
|
619
|
+
if (actualOptions.setup) {
|
|
620
|
+
await performSetup(userId);
|
|
621
|
+
return { success: true, output: output.join('\n') };
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Create/update person 1 if --person1 option is specified (no planet required)
|
|
625
|
+
if (actualOptions.person1) {
|
|
626
|
+
await setPerson1(actualOptions.person1, userId);
|
|
627
|
+
return { success: true, output: output.join('\n') };
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Create/update person 2 if --person2 option is specified (no planet required)
|
|
631
|
+
if (actualOptions.person2) {
|
|
632
|
+
await setPerson2(actualOptions.person2, userId);
|
|
633
|
+
return { success: true, output: output.join('\n') };
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// List people if --people option is specified (no planet required)
|
|
637
|
+
if (actualOptions.people) {
|
|
638
|
+
listPeople(userId);
|
|
639
|
+
return { success: true, output: output.join('\n') };
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Set AI model if --ai option is specified (no planet required)
|
|
643
|
+
if (actualOptions.ai) {
|
|
644
|
+
setAIModel(actualOptions.ai, userId);
|
|
645
|
+
return { success: true, output: output.join('\n') };
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Set system prompt if --system option is specified (no planet required)
|
|
649
|
+
if (actualOptions.system) {
|
|
650
|
+
const { setSystemPrompt } = require('../config/configService');
|
|
651
|
+
setSystemPrompt(actualOptions.system, userId);
|
|
652
|
+
return { success: true, output: output.join('\n') };
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Delete person if --delete-person option is specified (no planet required)
|
|
656
|
+
if (actualOptions.deletePerson) {
|
|
657
|
+
deletePerson(actualOptions.deletePerson, userId);
|
|
658
|
+
return { success: true, output: output.join('\n') };
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Create/update any person if --person option is specified (no planet required)
|
|
662
|
+
if (actualOptions.person) {
|
|
663
|
+
// The person option expects both id and data, but we need to handle the parsing
|
|
664
|
+
// For now, we'll assume the format is "id data" and split on first space
|
|
665
|
+
const personParts = actualOptions.person.split(' ');
|
|
666
|
+
const personId = personParts[0];
|
|
667
|
+
const personData = personParts.slice(1).join(' ');
|
|
668
|
+
|
|
669
|
+
if (personId && personData) {
|
|
670
|
+
const { setPerson } = require('../config/configService');
|
|
671
|
+
await setPerson(personId, personData, userId);
|
|
672
|
+
} else {
|
|
673
|
+
console.error('Invalid format for --person option. Expected: --person <id> "Location, Country, DD.MM.YYYY HH:MM"');
|
|
674
|
+
}
|
|
675
|
+
return { success: true, output: output.join('\n') };
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Show aspects if --a option is specified
|
|
679
|
+
if (actualOptions.a) {
|
|
680
|
+
// Use person data if --p1, --p2 or --i option is specified
|
|
681
|
+
let customDate = null;
|
|
682
|
+
let useBirthData = false;
|
|
683
|
+
let personSource = null;
|
|
684
|
+
|
|
685
|
+
const personDataToUse = this.getPersonDataToUse(actualOptions);
|
|
686
|
+
if (personDataToUse.data) {
|
|
687
|
+
customDate = personDataToUse.data;
|
|
688
|
+
personSource = personDataToUse.source;
|
|
689
|
+
useBirthData = personSource === 'birth'; // Only true for birth data
|
|
690
|
+
|
|
691
|
+
if (personSource === 'p1') {
|
|
692
|
+
console.log(`Using Person 1 data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
693
|
+
} else if (personSource === 'p2') {
|
|
694
|
+
console.log(`Using Person 2 data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
695
|
+
} else if (personSource.startsWith('p')) {
|
|
696
|
+
const personId = personSource.substring(1);
|
|
697
|
+
console.log(`Using Person ${personId} data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
698
|
+
} else {
|
|
699
|
+
console.log(`Using birth data: ${customDate.day}.${customDate.month}.${customDate.year} ${customDate.hour}:${customDate.minute.toString().padStart(2, '0')}`);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (customDate.location) {
|
|
703
|
+
console.log(`Location: ${customDate.location.name}, ${customDate.location.country}`);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
// Use custom date if specified (overrides --p1, --p2 and --i options)
|
|
707
|
+
if (actualOptions.d) {
|
|
708
|
+
// Try to parse the date as DD.MM.YYYY or DD.MM.YYYY HH:MM
|
|
709
|
+
const dateRegex = /^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/;
|
|
710
|
+
const match = actualOptions.d.match(dateRegex);
|
|
711
|
+
|
|
712
|
+
if (match) {
|
|
713
|
+
const day = parseInt(match[1], 10);
|
|
714
|
+
const month = parseInt(match[2], 10);
|
|
715
|
+
const year = parseInt(match[3], 10);
|
|
716
|
+
const hour = match[4] ? parseInt(match[4], 10) : 12; // Default: 12 o'clock
|
|
717
|
+
const minute = match[5] ? parseInt(match[5], 10) : 0; // Default: 0 minutes
|
|
718
|
+
|
|
719
|
+
// Check if the date is valid
|
|
720
|
+
const date = new Date(year, month - 1, day, hour, minute);
|
|
721
|
+
if (date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day) {
|
|
722
|
+
customDate = {
|
|
723
|
+
day: day,
|
|
724
|
+
month: month,
|
|
725
|
+
year: year,
|
|
726
|
+
hour: hour,
|
|
727
|
+
minute: minute
|
|
728
|
+
};
|
|
729
|
+
console.log(`Using custom date: ${day}.${month}.${year} ${hour}:${minute.toString().padStart(2, '0')}`);
|
|
730
|
+
} else {
|
|
731
|
+
console.error('Invalid date:', actualOptions.d);
|
|
732
|
+
console.error('💡 Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
733
|
+
return { success: false, output: output.join('\n') };
|
|
734
|
+
}
|
|
735
|
+
} else {
|
|
736
|
+
console.error('Invalid date:', actualOptions.d);
|
|
737
|
+
console.error('💡 Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
738
|
+
return { success: false, output: output.join('\n') };
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// If no custom date is specified, use current date
|
|
743
|
+
if (!customDate) {
|
|
744
|
+
const currentTime = getCurrentTimeInTimezone();
|
|
745
|
+
customDate = {
|
|
746
|
+
day: currentTime.day,
|
|
747
|
+
month: currentTime.month,
|
|
748
|
+
year: currentTime.year,
|
|
749
|
+
hour: currentTime.hour,
|
|
750
|
+
minute: currentTime.minute
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// If no planet is specified, show all active aspects for all planets
|
|
755
|
+
if (!planetArg) {
|
|
756
|
+
showAllActiveAspects(customDate, useBirthData);
|
|
757
|
+
|
|
758
|
+
return { success: true, output: output.join('\n') };
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Show aspects for the specific planet (with Huber orbs)
|
|
762
|
+
showPlanetAspects(planetArg, customDate, useBirthData, true);
|
|
763
|
+
|
|
764
|
+
return { success: true, output: output.join('\n') };
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// Show table view of all planet positions if --s option is specified
|
|
768
|
+
if (actualOptions.s) {
|
|
769
|
+
// Use person data for house calculation if --p1, --p2 or --i option is specified
|
|
770
|
+
let customDate = null;
|
|
771
|
+
let birthData = null;
|
|
772
|
+
let houseSystem = actualOptions.hs ? actualOptions.hs.toLowerCase() : 'koch';
|
|
773
|
+
let personSource = null;
|
|
774
|
+
let useNatalPositions = false;
|
|
775
|
+
|
|
776
|
+
// Check if person data should be used for house calculation
|
|
777
|
+
const personDataToUse = this.getPersonDataToUse(actualOptions);
|
|
778
|
+
if (personDataToUse.data) {
|
|
779
|
+
birthData = personDataToUse.data;
|
|
780
|
+
personSource = personDataToUse.source;
|
|
781
|
+
|
|
782
|
+
if (personSource === 'p1') {
|
|
783
|
+
console.log(`Using Person 1 data for house calculation: ${birthData.day}.${birthData.month}.${birthData.year} ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')}`);
|
|
784
|
+
} else if (personSource === 'p2') {
|
|
785
|
+
console.log(`Using Person 2 data for house calculation: ${birthData.day}.${birthData.month}.${birthData.year} ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')}`);
|
|
786
|
+
} else if (personSource.startsWith('p')) {
|
|
787
|
+
const personId = personSource.substring(1);
|
|
788
|
+
console.log(`Using Person ${personId} data for house calculation: ${birthData.day}.${birthData.month}.${birthData.year} ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')}`);
|
|
789
|
+
} else {
|
|
790
|
+
console.log(`Using birth data for house calculation: ${birthData.day}.${birthData.month}.${birthData.year} ${birthData.hour}:${birthData.minute.toString().padStart(2, '0')}`);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if (birthData.location) {
|
|
794
|
+
console.log(`Location: ${birthData.location.name}, ${birthData.location.country}`);
|
|
795
|
+
}
|
|
796
|
+
} else {
|
|
797
|
+
console.log('No person data found. Using current location for house calculation.');
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// Use custom date if specified (for planet positions)
|
|
801
|
+
if (actualOptions.d) {
|
|
802
|
+
// Try to parse the date as DD.MM.YYYY or DD.MM.YYYY HH:MM
|
|
803
|
+
const dateRegex = /^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/;
|
|
804
|
+
const match = actualOptions.d.match(dateRegex);
|
|
805
|
+
|
|
806
|
+
if (match) {
|
|
807
|
+
const day = parseInt(match[1], 10);
|
|
808
|
+
const month = parseInt(match[2], 10);
|
|
809
|
+
const year = parseInt(match[3], 10);
|
|
810
|
+
const hour = match[4] ? parseInt(match[4], 10) : 12; // Default: 12 o'clock
|
|
811
|
+
const minute = match[5] ? parseInt(match[5], 10) : 0; // Default: 0 minutes
|
|
812
|
+
|
|
813
|
+
// Check if the date is valid
|
|
814
|
+
const date = new Date(year, month - 1, day, hour, minute);
|
|
815
|
+
if (date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day) {
|
|
816
|
+
customDate = {
|
|
817
|
+
day: day,
|
|
818
|
+
month: month,
|
|
819
|
+
year: year,
|
|
820
|
+
hour: hour,
|
|
821
|
+
minute: minute
|
|
822
|
+
};
|
|
823
|
+
console.log(`Using custom date: ${day}.${month}.${year} ${hour}:${minute.toString().padStart(2, '0')}`);
|
|
824
|
+
} else {
|
|
825
|
+
console.error('Invalid date:', actualOptions.d);
|
|
826
|
+
console.error('💡 Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
827
|
+
return { success: false, output: output.join('\n') };
|
|
828
|
+
}
|
|
829
|
+
} else {
|
|
830
|
+
console.error('Invalid date:', actualOptions.d);
|
|
831
|
+
console.error('Please use the format: DD.MM.YYYY or "DD.MM.YYYY HH:MM" (with quotes for date with time)');
|
|
832
|
+
return { success: false, output: output.join('\n') };
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// If no custom date is specified, use current date
|
|
837
|
+
if (!customDate) {
|
|
838
|
+
const currentTime = getCurrentTimeInTimezone();
|
|
839
|
+
customDate = {
|
|
840
|
+
day: currentTime.day,
|
|
841
|
+
month: currentTime.month,
|
|
842
|
+
year: currentTime.year,
|
|
843
|
+
hour: currentTime.hour,
|
|
844
|
+
minute: currentTime.minute
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// Check if we should use natal positions (when --i is used without custom date)
|
|
849
|
+
if (this.shouldUseBirthData(actualOptions) && !actualOptions.d) {
|
|
850
|
+
useNatalPositions = true;
|
|
851
|
+
// customDate is already set to birth data from personDataToUse
|
|
852
|
+
console.log('Using natal positions from birth data.');
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
const config = loadConfig();
|
|
856
|
+
|
|
857
|
+
// Function to calculate Julian Day in UTC for planet positions
|
|
858
|
+
const getJulianDayUTCForPlanets = () => {
|
|
859
|
+
let timezoneOffsetMinutes = 0;
|
|
860
|
+
|
|
861
|
+
if (config && config.currentLocation && config.currentLocation.timezone) {
|
|
862
|
+
timezoneOffsetMinutes = getTimezoneOffset(customDate, config.currentLocation.timezone);
|
|
863
|
+
} else {
|
|
864
|
+
timezoneOffsetMinutes = -new Date().getTimezoneOffset();
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
return calculateJulianDayUTC(customDate, timezoneOffsetMinutes);
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
// Function to calculate Julian Day in UTC for house calculation
|
|
871
|
+
const getJulianDayUTCForHouses = () => {
|
|
872
|
+
let timezoneOffsetMinutes = 0;
|
|
873
|
+
|
|
874
|
+
if (birthData && birthData.location) {
|
|
875
|
+
timezoneOffsetMinutes = getTimezoneOffset(birthData, birthData.location.timezone || 'Europe/Zurich');
|
|
876
|
+
} else if (config && config.currentLocation && config.currentLocation.timezone) {
|
|
877
|
+
timezoneOffsetMinutes = getTimezoneOffset(customDate, config.currentLocation.timezone);
|
|
878
|
+
} else {
|
|
879
|
+
timezoneOffsetMinutes = -new Date().getTimezoneOffset();
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
return calculateJulianDayUTC(birthData ? birthData : customDate, timezoneOffsetMinutes);
|
|
883
|
+
};
|
|
884
|
+
|
|
885
|
+
// Calculate houses if house system is specified
|
|
886
|
+
let houses = null;
|
|
887
|
+
if (houseSystem) {
|
|
888
|
+
try {
|
|
889
|
+
const julianDayForHouses = getJulianDayUTCForHouses();
|
|
890
|
+
const useBirthLocation = birthData !== null;
|
|
891
|
+
|
|
892
|
+
houses = await calculateHouses(julianDayForHouses, getHouseSystemCode(houseSystem), useBirthLocation);
|
|
893
|
+
} catch (error) {
|
|
894
|
+
console.error('Error calculating houses:', error);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Show table view of all planet positions
|
|
899
|
+
console.log('Planet positions:');
|
|
900
|
+
console.log('================================================================================');
|
|
901
|
+
console.log('| Planet | Sign | Deg | House | Dignity | Element |');
|
|
902
|
+
console.log('================================================================================');
|
|
903
|
+
|
|
904
|
+
// Calculate positions of all planets
|
|
905
|
+
for (const [name, planetId] of Object.entries(planets)) {
|
|
906
|
+
const data = getAstrologicalData(name, customDate);
|
|
907
|
+
|
|
908
|
+
// Determine the house if houses were calculated
|
|
909
|
+
let house = 'N/A';
|
|
910
|
+
if (houses) {
|
|
911
|
+
const planetLongitude = parseFloat(data.degreeInSign) + (signs.indexOf(data.sign) * 30);
|
|
912
|
+
house = getPlanetHouse(planetLongitude, houses.house);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
const planetNameFormatted = name.charAt(0).toUpperCase() + name.slice(1);
|
|
916
|
+
const signFormatted = data.sign.padEnd(10, ' ');
|
|
917
|
+
const degreeFormatted = data.degreeInSign.padEnd(5, ' ');
|
|
918
|
+
const houseFormatted = house.toString().padEnd(4, ' ');
|
|
919
|
+
const dignityFormatted = data.dignity.padEnd(12, ' ');
|
|
920
|
+
const elementFormatted = data.element.padEnd(9, ' ');
|
|
921
|
+
|
|
922
|
+
console.log(`| ${planetNameFormatted.padEnd(11)} | ${signFormatted} | ${degreeFormatted}° | ${houseFormatted} | ${dignityFormatted} | ${elementFormatted} |`);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
console.log('================================================================================');
|
|
926
|
+
|
|
927
|
+
// Show astrological angles if houses were calculated
|
|
928
|
+
if (houses) {
|
|
929
|
+
console.log('\nAstrological Angles:');
|
|
930
|
+
console.log('================================================================================');
|
|
931
|
+
console.log('| Angle | Sign | Degree |');
|
|
932
|
+
console.log('================================================================================');
|
|
933
|
+
|
|
934
|
+
const angles = calculateAstrologicalAngles(houses.house);
|
|
935
|
+
|
|
936
|
+
// ASC (Ascendant)
|
|
937
|
+
const ascData = longitudeToSignDegree(angles.asc);
|
|
938
|
+
console.log(`| ASC | ${ascData.sign.padEnd(10)} | ${ascData.degree.padEnd(6)}° |`);
|
|
939
|
+
|
|
940
|
+
// MC (Medium Coeli/Midheaven)
|
|
941
|
+
const mcData = longitudeToSignDegree(angles.mc);
|
|
942
|
+
console.log(`| MC | ${mcData.sign.padEnd(10)} | ${mcData.degree.padEnd(6)}° |`);
|
|
943
|
+
|
|
944
|
+
// DESC (Descendant)
|
|
945
|
+
const descData = longitudeToSignDegree(angles.desc);
|
|
946
|
+
console.log(`| DESC | ${descData.sign.padEnd(10)} | ${descData.degree.padEnd(6)}° |`);
|
|
947
|
+
|
|
948
|
+
// IC (Imum Coeli)
|
|
949
|
+
const icData = longitudeToSignDegree(angles.ic);
|
|
950
|
+
console.log(`| IC | ${icData.sign.padEnd(10)} | ${icData.degree.padEnd(6)}° |`);
|
|
951
|
+
|
|
952
|
+
console.log('================================================================================');
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
if (birthData) {
|
|
956
|
+
if (useNatalPositions) {
|
|
957
|
+
console.log('\nThis analysis shows your natal planet positions.');
|
|
958
|
+
console.log('The houses are based on your birth ASC (natal chart).');
|
|
959
|
+
} else {
|
|
960
|
+
console.log('\nThis analysis shows current planet positions in your birth houses.');
|
|
961
|
+
console.log('The houses are based on your birth ASC (personal transits).');
|
|
962
|
+
}
|
|
963
|
+
} else {
|
|
964
|
+
console.log('\nThis analysis is based on the current planet position.');
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
if (houses) {
|
|
968
|
+
console.log(`House system: ${houseSystem.charAt(0).toUpperCase() + houseSystem.slice(1)}`);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
return { success: true, output: output.join('\n') };
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
if (actualOptions.v || actualOptions.z) {
|
|
975
|
+
const positionalArgs = actualOptions._positionalArgs || [];
|
|
976
|
+
let planet1 = positionalArgs[0] ? positionalArgs[0].toLowerCase() : null;
|
|
977
|
+
let aspectType = positionalArgs[1] ? positionalArgs[1].toLowerCase() : null;
|
|
978
|
+
let planet2 = positionalArgs[2] ? positionalArgs[2].toLowerCase() : null;
|
|
979
|
+
const countValue = actualOptions.v || actualOptions.z;
|
|
980
|
+
const count = parseInt(countValue, 10);
|
|
981
|
+
|
|
982
|
+
if (!planet1 || !aspectType || !planet2) {
|
|
983
|
+
const rawArgs = actualOptions._args || [];
|
|
984
|
+
const optionFlag = actualOptions.v ? '--v' : '--z';
|
|
985
|
+
const optionIndex = rawArgs.findIndex(arg => arg === optionFlag);
|
|
986
|
+
if (optionIndex !== -1 && rawArgs.length >= optionIndex + 5) {
|
|
987
|
+
planet1 = rawArgs[optionIndex + 2] ? rawArgs[optionIndex + 2].toLowerCase() : null;
|
|
988
|
+
aspectType = rawArgs[optionIndex + 3] ? rawArgs[optionIndex + 3].toLowerCase() : null;
|
|
989
|
+
planet2 = rawArgs[optionIndex + 4] ? rawArgs[optionIndex + 4].toLowerCase() : null;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
if (!planet1 || !aspectType || !planet2) {
|
|
994
|
+
console.error('Error: Past/future aspects require two planets and an aspect type.');
|
|
995
|
+
console.error('Format: --v <count> planet1 aspectType planet2');
|
|
996
|
+
console.error('Example: --v 3 saturn c neptune');
|
|
997
|
+
return { success: false, output: output.join('\n') };
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
if (!Number.isFinite(count) || count <= 0) {
|
|
1001
|
+
console.error('Error: Please provide a valid count for --v/--z.');
|
|
1002
|
+
return { success: false, output: output.join('\n') };
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
if (!planets[planet1] || !planets[planet2]) {
|
|
1006
|
+
console.error('Error: Invalid planets.');
|
|
1007
|
+
console.error('Available planets:', Object.keys(planets).join(', '));
|
|
1008
|
+
return { success: false, output: output.join('\n') };
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
const targetAngle = getAspectAngle(aspectType);
|
|
1012
|
+
if (targetAngle === null) {
|
|
1013
|
+
console.error('Error: Invalid aspect type.');
|
|
1014
|
+
console.error('Available aspect types: c, o, s, t, se (conjunction, opposition, square, trine, sextile)');
|
|
1015
|
+
return { success: false, output: output.join('\n') };
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
const aspectTypeFull = getAspectTypeFullName(aspectType);
|
|
1019
|
+
const aspects = actualOptions.v
|
|
1020
|
+
? getPastAspects(planet1, planet2, aspectType, count)
|
|
1021
|
+
: getFutureAspects(planet1, planet2, aspectType, count);
|
|
1022
|
+
const heading = actualOptions.v ? 'Last' : 'Next';
|
|
1023
|
+
|
|
1024
|
+
console.log(`${heading} ${count} ${aspectTypeFull} between ${planet1} and ${planet2}:`);
|
|
1025
|
+
console.log('================================================================================');
|
|
1026
|
+
console.log('| Date | Planet 1 Position | Planet 2 Position |');
|
|
1027
|
+
console.log('================================================================================');
|
|
1028
|
+
|
|
1029
|
+
if (aspects.length === 0) {
|
|
1030
|
+
console.log(actualOptions.v ? 'No past aspects found.' : 'No future aspects found.');
|
|
1031
|
+
} else {
|
|
1032
|
+
aspects.forEach(aspect => {
|
|
1033
|
+
const dateStr = `${String(aspect.date.day).padStart(2, '0')}.${String(aspect.date.month).padStart(2, '0')}.${aspect.date.year}`;
|
|
1034
|
+
const planet1Pos = `${aspect.planet1Position.padEnd(22, ' ')}`;
|
|
1035
|
+
const planet2Pos = `${aspect.planet2Position.padEnd(22, ' ')}`;
|
|
1036
|
+
console.log(`| ${dateStr.padEnd(18)} | ${planet1Pos} | ${planet2Pos} |`);
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
console.log('================================================================================');
|
|
1041
|
+
console.log(`Found: ${aspects.length} exact ${aspectTypeFull}`);
|
|
1042
|
+
return { success: true, output: output.join('\n') };
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// Show normal planet info as default
|
|
1046
|
+
const planet = planetArg ? planetArg.toLowerCase() : 'moon';
|
|
1047
|
+
const data = getAstrologicalData(planet, null);
|
|
1048
|
+
|
|
1049
|
+
console.log(`\nAstrological data for ${data.planet}:`);
|
|
1050
|
+
console.log(`Sign: ${data.sign}`);
|
|
1051
|
+
console.log(`Degree in sign: ${data.degreeInSign}°`);
|
|
1052
|
+
console.log(`Dignity: ${data.dignity}`);
|
|
1053
|
+
console.log(`Element: ${data.element}`);
|
|
1054
|
+
console.log(`Decan: ${data.decan}\n`);
|
|
1055
|
+
|
|
1056
|
+
return { success: true, output: output.join('\n') };
|
|
1057
|
+
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
console.error('Error executing command:', error.message);
|
|
1060
|
+
return { success: false, output: output.join('\n') };
|
|
1061
|
+
} finally {
|
|
1062
|
+
// Restore original console methods
|
|
1063
|
+
console.log = originalConsoleLog;
|
|
1064
|
+
console.error = originalConsoleError;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// Method to execute a command programmatically
|
|
1069
|
+
async executeCommand(commandString, userId = null) {
|
|
1070
|
+
// Parse the command string into arguments
|
|
1071
|
+
const args = splitCommandArgs(commandString);
|
|
1072
|
+
|
|
1073
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
1074
|
+
const helpText = [
|
|
1075
|
+
'Usage: [planet] [options]',
|
|
1076
|
+
'',
|
|
1077
|
+
'Options:',
|
|
1078
|
+
' --help, -h Show this help',
|
|
1079
|
+
' --c Critical degrees',
|
|
1080
|
+
' --rx Retrograde planets',
|
|
1081
|
+
' --el Element distribution',
|
|
1082
|
+
' --s All planet positions',
|
|
1083
|
+
' --status Show config',
|
|
1084
|
+
' --people List people',
|
|
1085
|
+
' --setup Configure via GUI',
|
|
1086
|
+
' --i Use birth data',
|
|
1087
|
+
' --hs <system> House system',
|
|
1088
|
+
' --hl <system> House list',
|
|
1089
|
+
' --d <date> Specific date (DD.MM.YYYY or "DD.MM.YYYY HH:MM")',
|
|
1090
|
+
' --a Show aspects for planet',
|
|
1091
|
+
' --k Show aspects between planets',
|
|
1092
|
+
' --af Show active aspect figures',
|
|
1093
|
+
' --tr Show personal transit aspects',
|
|
1094
|
+
' --v <count> Past aspects (planet1 aspectType planet2)',
|
|
1095
|
+
' --z <count> Future aspects (planet1 aspectType planet2)',
|
|
1096
|
+
' --in [count] Next planet ingress',
|
|
1097
|
+
' --o Transit frequency'
|
|
1098
|
+
].join('\n');
|
|
1099
|
+
return { success: true, output: helpText };
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// Parse options manually since Commander.js doesn't easily allow capturing output
|
|
1103
|
+
const options = {};
|
|
1104
|
+
const positionalArgs = [];
|
|
1105
|
+
const supportedOptions = new Set();
|
|
1106
|
+
for (const option of this.program.options) {
|
|
1107
|
+
if (option.long) {
|
|
1108
|
+
supportedOptions.add(option.long.replace(/^--/, ''));
|
|
1109
|
+
}
|
|
1110
|
+
if (option.short) {
|
|
1111
|
+
supportedOptions.add(option.short.replace(/^-/, ''));
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
for (let i = 0; i < args.length; i++) {
|
|
1116
|
+
const arg = args[i];
|
|
1117
|
+
|
|
1118
|
+
if (arg.startsWith('--')) {
|
|
1119
|
+
// Handle options
|
|
1120
|
+
const optionName = arg.substring(2);
|
|
1121
|
+
|
|
1122
|
+
if (!supportedOptions.has(optionName)) {
|
|
1123
|
+
return { success: false, output: `Unknown option: --${optionName}` };
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// Special handling for --v option which requires multiple arguments
|
|
1127
|
+
if (optionName === 'v') {
|
|
1128
|
+
// --v requires: count, planet1, aspectType, planet2
|
|
1129
|
+
if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
|
|
1130
|
+
options[optionName] = args[i + 1]; // This is the count
|
|
1131
|
+
|
|
1132
|
+
// Now we need to extract the next 3 arguments: planet1, aspectType, planet2
|
|
1133
|
+
if (i + 2 < args.length && !args[i + 2].startsWith('--')) {
|
|
1134
|
+
options['vPlanet1'] = args[i + 2];
|
|
1135
|
+
}
|
|
1136
|
+
if (i + 3 < args.length && !args[i + 3].startsWith('--')) {
|
|
1137
|
+
options['vAspectType'] = args[i + 3];
|
|
1138
|
+
}
|
|
1139
|
+
if (i + 4 < args.length && !args[i + 4].startsWith('--')) {
|
|
1140
|
+
options['vPlanet2'] = args[i + 4];
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
i += 4; // Skip the next 4 arguments (count + 3 planets/aspects)
|
|
1144
|
+
} else {
|
|
1145
|
+
options[optionName] = true;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
// Special handling for --z option which requires multiple arguments
|
|
1149
|
+
else if (optionName === 'z') {
|
|
1150
|
+
// --z requires: count, planet1, aspectType, planet2
|
|
1151
|
+
if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
|
|
1152
|
+
options[optionName] = args[i + 1]; // This is the count
|
|
1153
|
+
|
|
1154
|
+
// Now we need to extract the next 3 arguments: planet1, aspectType, planet2
|
|
1155
|
+
if (i + 2 < args.length && !args[i + 2].startsWith('--')) {
|
|
1156
|
+
options['zPlanet1'] = args[i + 2];
|
|
1157
|
+
}
|
|
1158
|
+
if (i + 3 < args.length && !args[i + 3].startsWith('--')) {
|
|
1159
|
+
options['zAspectType'] = args[i + 3];
|
|
1160
|
+
}
|
|
1161
|
+
if (i + 4 < args.length && !args[i + 4].startsWith('--')) {
|
|
1162
|
+
options['zPlanet2'] = args[i + 4];
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
i += 4; // Skip the next 4 arguments (count + 3 planets/aspects)
|
|
1166
|
+
} else {
|
|
1167
|
+
options[optionName] = true;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
// Handle other options with values
|
|
1171
|
+
else if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
|
|
1172
|
+
options[optionName] = args[i + 1];
|
|
1173
|
+
i++; // Skip the next argument as it's the value
|
|
1174
|
+
} else {
|
|
1175
|
+
options[optionName] = true;
|
|
1176
|
+
}
|
|
1177
|
+
} else if (arg.startsWith('-')) {
|
|
1178
|
+
// Handle short options (we'll skip these for simplicity)
|
|
1179
|
+
// This is a simplified parser for the GUI use case
|
|
1180
|
+
} else {
|
|
1181
|
+
// Handle positional arguments
|
|
1182
|
+
positionalArgs.push(arg);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// Create a new instance to handle the command
|
|
1187
|
+
const tempService = new CLIService();
|
|
1188
|
+
const planetArg = positionalArgs[0] || null;
|
|
1189
|
+
const planet2Arg = positionalArgs[1] || null;
|
|
1190
|
+
options._positionalArgs = positionalArgs;
|
|
1191
|
+
options._args = args;
|
|
1192
|
+
|
|
1193
|
+
return tempService.handleCommand(planetArg, planet2Arg, options, userId);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
module.exports = new CLIService();
|