dsh-metaphysics 0.59.0 → 0.59.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/CHANGELOG.md +97 -5
- package/README.md +15 -0
- package/client.almanac.js +56 -53
- package/client.bazi.js +112 -109
- package/client.chenggu.js +56 -53
- package/client.convert.js +55 -52
- package/client.daliuren.js +119 -116
- package/client.fengshui.js +61 -58
- package/client.hepan.js +110 -107
- package/client.js +322 -290
- package/client.liuyao.js +119 -116
- package/client.mbti.js +57 -54
- package/client.name.js +69 -66
- package/client.numerology.js +64 -61
- package/client.qimen.js +119 -116
- package/client.star.js +57 -54
- package/client.tarot.js +118 -115
- package/client.trend.js +107 -104
- package/client.xiaoliuren.js +65 -62
- package/client.zeri.js +57 -54
- package/client.ziwei.js +112 -109
- package/engine/bazi.js +800 -740
- package/engine/diff.js +3 -0
- package/engine/geju.js +548 -547
- package/engine/index.js +69 -6
- package/engine/interpret.js +4 -1
- package/engine/limits.js +5 -0
- package/engine/plain-summary.js +604 -0
- package/engine/rectify.js +2 -1
- package/engine/trend.js +3 -0
- package/index.js +5 -0
- package/package.json +11 -3
- package//345/221/275/347/220/206/346/216/222/347/233/230.html +2168 -1316
package/engine/index.js
CHANGED
|
@@ -114,8 +114,11 @@ function baziText(r) {
|
|
|
114
114
|
+ ` 地势 ${p.dishi} 自坐 ${p.zizuo} 空亡 ${p.kongwang.join('')}`
|
|
115
115
|
+ (p.shensha && p.shensha.length ? ` 神煞 ${p.shensha.join('、')}` : '');
|
|
116
116
|
const wuxing = Object.entries(r.wuxing).map(([k, v]) => `${k}${v}`).join(' ');
|
|
117
|
+
// 命宫/身宫由「生月 + 生时支」定:时柱未知时它们是 null,不能拼字符串,也不能拿占位值顶上
|
|
117
118
|
const palaces = r.palaces
|
|
118
|
-
? `【胎元】${r.palaces.taiyuan.ganzhi} 胎息 ${r.palaces.taixi ? r.palaces.taixi.ganzhi : '—'}
|
|
119
|
+
? `【胎元】${r.palaces.taiyuan.ganzhi} 胎息 ${r.palaces.taixi ? r.palaces.taixi.ganzhi : '—'}`
|
|
120
|
+
+ (r.palaces.minggong ? ` 命宫 ${r.palaces.minggong.ganzhi}` : ' 命宫 时柱暂缺,不排')
|
|
121
|
+
+ (r.palaces.shengong ? ` 身宫 ${r.palaces.shengong.ganzhi}` : ' 身宫 时柱暂缺,不排')
|
|
119
122
|
: '';
|
|
120
123
|
const dayun = r.dayun.list.slice(0, 8)
|
|
121
124
|
.map((d) => `${d.startAge}岁 ${d.ganzhi}(${d.stemTenGod})${d.luck ? `·${d.luck.tier}` : ''}${d.relations && d.relations.length ? `[${d.relations.join('')}]` : ''}`).join(' ');
|
|
@@ -155,10 +158,12 @@ function baziText(r) {
|
|
|
155
158
|
(r.frame.yongshen.qt ? ` 穷通:${r.frame.yongshen.qt.month}${r.frame.yongshen.qt.stem}取${r.frame.yongshen.qt.yong.join('、')}(首取${r.frame.yongshen.qt.primary})` : ''),
|
|
156
159
|
`十神权重:${Object.entries(r.frame.godStats.groups).map(([k, v]) => `${k}${v}`).join(' ')}`,
|
|
157
160
|
] : [];
|
|
161
|
+
const hourMissing = r.hourUnknown === true || !P.hour;
|
|
158
162
|
return [
|
|
159
|
-
`【四柱】${P.year.ganzhi} ${P.month.ganzhi} ${P.day.ganzhi} ${P.hour.ganzhi}`,
|
|
163
|
+
`【四柱】${P.year.ganzhi} ${P.month.ganzhi} ${P.day.ganzhi}${P.hour ? ` ${P.hour.ganzhi}` : '(时柱暂缺)'}`,
|
|
160
164
|
`日主:${r.dayMaster.stem}${r.dayMaster.element}(${r.dayMaster.yinYang}) 空亡:${r.kongwang.join('')}`,
|
|
161
|
-
line('年柱', P.year), line('月柱', P.month), line('日柱', P.day),
|
|
165
|
+
line('年柱', P.year), line('月柱', P.month), line('日柱', P.day),
|
|
166
|
+
...(hourMissing ? [] : [line('时柱', P.hour)]),
|
|
162
167
|
`五行:${wuxing}`,
|
|
163
168
|
...(palaces ? [palaces] : []),
|
|
164
169
|
`日主强弱:${r.strength.level}(${r.strength.score}) ${r.strength.detail}`,
|
|
@@ -752,6 +757,20 @@ export function compute(input = {}) {
|
|
|
752
757
|
delete args.partner2;
|
|
753
758
|
}
|
|
754
759
|
const r = jsonSafe(computeRaw(args));
|
|
760
|
+
// 「所问之事」以前只有六爻和塔罗会落到结果里,梅花/六壬/奇门/小六壬都丢掉了:
|
|
761
|
+
// 于是第一屏想说「你问的是……」却拿不到那句话。这里统一补齐,已有的值不动。
|
|
762
|
+
if (r && !r.question && typeof args.question === 'string' && args.question.trim()
|
|
763
|
+
&& ['liuyao', 'meihua', 'daliuren', 'qimen', 'tarot', 'xiaoliuren'].includes(r.system)) {
|
|
764
|
+
r.question = args.question.trim();
|
|
765
|
+
}
|
|
766
|
+
// 顶部那段「人话总结」:命盘两类 + 问事五类 + 合盘 + 走势,都由 plainSummary 决定给不给。
|
|
767
|
+
// 数据不够时它返回 null,不动 r.brief;生成失败只记 briefError,绝不因此拖垮排盘。
|
|
768
|
+
if (r) {
|
|
769
|
+
try {
|
|
770
|
+
const brief = plainSummary(r);
|
|
771
|
+
if (brief) r.brief = brief;
|
|
772
|
+
} catch (e) { r.briefError = String((e && e.message) || e); }
|
|
773
|
+
}
|
|
755
774
|
if (revision) r.revision = revision;
|
|
756
775
|
if (r && r.system === 'bazi') {
|
|
757
776
|
r.classics = classicsFor(r);
|
|
@@ -774,6 +793,16 @@ export function compute(input = {}) {
|
|
|
774
793
|
if (r && (r.system === 'bazi' || r.system === 'ziwei') && r.input && r.input.longitude == null && r.summary) {
|
|
775
794
|
r.summary = `⚠ 未提供出生地或经度:按东八区平太阳时排盘,时柱可能与本命盘相差一个时辰;海外出生请先把当地时间换算成东八区标准时。\n${r.summary}`;
|
|
776
795
|
}
|
|
796
|
+
// 时辰未知:固定提示必须在第一行。放在所有警告之后 prepend,才不会被地警告/精度提示挤到第二行
|
|
797
|
+
if (r && r.system === 'bazi' && r.hourUnknown && r.summary) {
|
|
798
|
+
r.summary = `时柱按正午暂算,涉及时柱的结论仅供参考
|
|
799
|
+
${r.summary}`;
|
|
800
|
+
}
|
|
801
|
+
// 趋势是降级而不是阻断:起运按日估算,也要在首行说清
|
|
802
|
+
if (r && r.system === 'trend' && r.hourUnknown && r.summary) {
|
|
803
|
+
r.summary = `时柱暂缺:起运与大运按日估算,仅供参考
|
|
804
|
+
${r.summary}`;
|
|
805
|
+
}
|
|
777
806
|
// 显式要求时才附加解析模块,避免默认输出过长;「所问之事」与「询问方向」一并传进去
|
|
778
807
|
if (args.withInterpret) {
|
|
779
808
|
r.interpret = interpret(r, {
|
|
@@ -782,7 +811,10 @@ export function compute(input = {}) {
|
|
|
782
811
|
});
|
|
783
812
|
}
|
|
784
813
|
// 八字 × 紫微交叉验证:同一份出生信息再排另一套盘,并列两边对同一件事的说法
|
|
785
|
-
|
|
814
|
+
// 交叉验证要再排另一套盘:时辰未知时紫微排不出来,这条支路直接跳过,
|
|
815
|
+
// 不能拿正午排一张紫微盘来「交叉」,那是两张都不可信的盘互证。
|
|
816
|
+
if (args.cross === true && args.hourUnknown === true) r.crossSkipped = 'HOUR_UNKNOWN';
|
|
817
|
+
if (args.cross === true && args.hourUnknown !== true && (r.system === 'bazi' || r.system === 'ziwei')) {
|
|
786
818
|
const cdt = parseDateTime(args.datetime);
|
|
787
819
|
const common = {
|
|
788
820
|
...cdt,
|
|
@@ -1016,10 +1048,16 @@ function computeRaw(args = {}) {
|
|
|
1016
1048
|
dst: args.dst !== false,
|
|
1017
1049
|
// 流年表默认从「今年」起排;要复现历史结论就把基准年钉死
|
|
1018
1050
|
nowYear: asOf ? asOf.year : undefined,
|
|
1051
|
+
// 时辰未知:三柱模式(时柱不排、精度降到日、摘要首行固定提示)
|
|
1052
|
+
hourUnknown: args.hourUnknown === true,
|
|
1019
1053
|
});
|
|
1020
1054
|
return { ...result, ...(asOf ? { asOf } : {}), precision: dt.precision, summary: baziText(result) };
|
|
1021
1055
|
}
|
|
1022
1056
|
if (system === 'ziwei') {
|
|
1057
|
+
// 命宫由「生月 + 生时支」定:时支未知则整盘无处落脚,没有「大概的紫微盘」这种东西
|
|
1058
|
+
if (args.hourUnknown === true) {
|
|
1059
|
+
throw fail('HOUR_UNKNOWN', '紫微必须有出生时刻:命宫由生月与生时支定,时支未知则整盘不成立。请补上出生时刻,或改用八字三柱模式只看年/月/日。');
|
|
1060
|
+
}
|
|
1023
1061
|
const dt = parseDateTime(args.datetime);
|
|
1024
1062
|
const asOf = checkAsOf(args.asOf);
|
|
1025
1063
|
const result = buildZiwei({
|
|
@@ -1076,6 +1114,10 @@ function computeRaw(args = {}) {
|
|
|
1076
1114
|
return { ...result, summary: qimenText(result) };
|
|
1077
1115
|
}
|
|
1078
1116
|
if (system === 'hepan') {
|
|
1117
|
+
// 双方夫妻宫、大运都依赖时支:任一方时辰未知就不出盘,免得「差不多」的合盘误导决定
|
|
1118
|
+
if (args.hourUnknown === true || (args.partner && args.partner.hourUnknown === true)) {
|
|
1119
|
+
throw fail('HOUR_UNKNOWN', '合盘需要双方都有出生时刻:夫妻宫与大运都依赖时支。请补齐时刻后再看,或用八字三柱模式各自看年/月/日。');
|
|
1120
|
+
}
|
|
1079
1121
|
const partner = args.partner;
|
|
1080
1122
|
if (!partner || typeof partner !== 'object') {
|
|
1081
1123
|
throw fail('E_PARTNER', '合盘需要 partner:{ datetime, gender, address? },其中 partner.datetime 是对方的公历出生时间');
|
|
@@ -1288,8 +1330,12 @@ function computeRaw(args = {}) {
|
|
|
1288
1330
|
}
|
|
1289
1331
|
if (system === 'trend') {
|
|
1290
1332
|
const dt = parseDateTime(args.datetime);
|
|
1333
|
+
// 「现在在第几步」要有个当前年份;引擎不读挂钟,只认 asOf。
|
|
1334
|
+
// 不给 asOf 就不标当前位置(summary 会少一句,但结果可复现)。
|
|
1335
|
+
const nowParts = args.asOf ? checkAsOf(args.asOf) : undefined;
|
|
1291
1336
|
const result = buildTrend({
|
|
1292
1337
|
...dt,
|
|
1338
|
+
nowYear: nowParts ? nowParts.year : undefined,
|
|
1293
1339
|
gender: requireGender(args.gender),
|
|
1294
1340
|
toAge: args.toAge,
|
|
1295
1341
|
lateZi: args.lateZi !== false,
|
|
@@ -1298,8 +1344,15 @@ function computeRaw(args = {}) {
|
|
|
1298
1344
|
longitude: typeof args.longitude === 'number' ? args.longitude : undefined,
|
|
1299
1345
|
address: typeof args.address === 'string' ? args.address : undefined,
|
|
1300
1346
|
geo: args.geo && typeof args.geo === 'object' ? args.geo : undefined,
|
|
1347
|
+
hourUnknown: args.hourUnknown === true,
|
|
1301
1348
|
});
|
|
1302
|
-
|
|
1349
|
+
// 趋势没有独立的 dayun 字段(大运在 buildTrend 内部用),所以精度标记挂在结果顶层
|
|
1350
|
+
return {
|
|
1351
|
+
...result,
|
|
1352
|
+
...(args.hourUnknown === true ? { hourUnknown: true, hourPrecision: 'day' } : {}),
|
|
1353
|
+
precision: dt.precision,
|
|
1354
|
+
summary: trendText(result),
|
|
1355
|
+
};
|
|
1303
1356
|
}
|
|
1304
1357
|
if (system === 'fengshui') {
|
|
1305
1358
|
const action = args.action === 'advise' ? 'advise' : args.action === 'xuankong' ? 'xuankong' : 'natal';
|
|
@@ -1647,7 +1700,16 @@ function nameText(r) {
|
|
|
1647
1700
|
/** 给模型的紧凑文本(去掉大字段,保留摘要;带解析时附上解析) */
|
|
1648
1701
|
export function renderText(result) {
|
|
1649
1702
|
const base = result.summary ?? JSON.stringify(result, null, 2);
|
|
1650
|
-
|
|
1703
|
+
// 先给一段人话,再给盘面细节:模型照这段复述,就不至于一开口就是术语表
|
|
1704
|
+
const NL = String.fromCharCode(10);
|
|
1705
|
+
const brief = result.brief
|
|
1706
|
+
? ['【先说结论】' + result.brief.lead]
|
|
1707
|
+
.concat((result.brief.sections || []).map((x) => [' ', x.h, ':', x.t].join('')))
|
|
1708
|
+
.concat((result.brief.advice || []).length ? [' 可以试着做的:' + result.brief.advice.map((a) => String(a).replace(/。$/, '')).join(';') + '。'] : [])
|
|
1709
|
+
.concat(result.brief.caveat ? [' ※ ' + result.brief.caveat] : [])
|
|
1710
|
+
.join(NL) + NL
|
|
1711
|
+
: '';
|
|
1712
|
+
const parts = [brief + base];
|
|
1651
1713
|
if (result.shadow && result.shadow.id) {
|
|
1652
1714
|
const s = result.shadow;
|
|
1653
1715
|
parts.push(`【影子盘】${s.id}(同参数同 ID;版本 ${s.version})`
|
|
@@ -1724,6 +1786,7 @@ export { askLink, parseAskLink, formOf, restNote, askPackages };
|
|
|
1724
1786
|
// 问事解读提示:把一张盘打包成「先答他所问、不堆术语、回到现实核对」的提示,供面板一键复制
|
|
1725
1787
|
// (本仓 esbuild 不接受 `export {} from`,先静态 import 再 export)
|
|
1726
1788
|
import { buildEventPrompt, ASK_KINDS } from './event-prompt.js';
|
|
1789
|
+
import { plainSummary } from './plain-summary.js';
|
|
1727
1790
|
export { buildEventPrompt, ASK_KINDS };
|
|
1728
1791
|
|
|
1729
1792
|
|
package/engine/interpret.js
CHANGED
|
@@ -494,7 +494,10 @@ function baziInterpret(r) {
|
|
|
494
494
|
key: 'palaces', label: '胎命身',
|
|
495
495
|
text: `胎元 ${r.palaces.taiyuan.ganzhi}(月干进一、月支进三);`
|
|
496
496
|
+ `${r.palaces.taixi ? `胎息 ${r.palaces.taixi.ganzhi}(日干五合、日支六合);` : ''}`
|
|
497
|
-
+
|
|
497
|
+
+ (r.palaces.minggong && r.palaces.shengong
|
|
498
|
+
? `命宫 ${r.palaces.minggong.ganzhi}、身宫 ${r.palaces.shengong.ganzhi}(生月与生时两支合数定宫,天干随六十甲子)。`
|
|
499
|
+
// 时辰未知:这两宫由生时支定,没有时支就没有宫位——宁可空着,不能拿正午顶上
|
|
500
|
+
: '命宫与身宫由生月与生时支合数定宫:时柱暂缺,这两宫不排。')
|
|
498
501
|
+ '四宫是读盘的辅助坐标,不单独折吉凶。',
|
|
499
502
|
}] : []),
|
|
500
503
|
{
|
package/engine/limits.js
CHANGED
|
@@ -54,6 +54,11 @@ export const SYSTEM_LIMITS = {
|
|
|
54
54
|
},
|
|
55
55
|
],
|
|
56
56
|
bazi: [
|
|
57
|
+
{
|
|
58
|
+
id: 'bazi-hour-unknown',
|
|
59
|
+
title: '时辰未知时,只给三柱,不给紫微与合盘',
|
|
60
|
+
text: '不知道出生时辰时,本插件不排时柱,起运按日估算,并把「时柱按正午暂算」写在结果第一行。紫微(命宫由生月与生时支定)与合盘(夫妻宫、大运都依赖时支)一律不出盘——不是保守,是这两套离了时支就不成立,硬排出来只是看着像盘。',
|
|
61
|
+
},
|
|
57
62
|
{
|
|
58
63
|
id: 'bazi-no-event',
|
|
59
64
|
title: '不预测具体年份的事件',
|