dsh-metaphysics 0.61.2 → 0.62.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +74 -1
  2. package/README.md +3 -3
  3. package/client.almanac.js +74 -69
  4. package/client.bazi.js +129 -124
  5. package/client.chenggu.js +75 -70
  6. package/client.convert.js +66 -61
  7. package/client.daliuren.js +129 -124
  8. package/client.fengshui.js +57 -52
  9. package/client.hepan.js +128 -123
  10. package/client.js +2 -2
  11. package/client.liuyao.js +127 -122
  12. package/client.mbti.js +66 -61
  13. package/client.name.js +73 -68
  14. package/client.numerology.js +72 -67
  15. package/client.qimen.js +125 -120
  16. package/client.star.js +72 -67
  17. package/client.tarot.js +123 -118
  18. package/client.trend.js +119 -114
  19. package/client.xiaoliuren.js +84 -79
  20. package/client.zeri.js +66 -61
  21. package/client.ziwei.js +129 -124
  22. package/engine/bazi-words.js +3 -0
  23. package/engine/bazi.js +29 -12
  24. package/engine/calendar.js +22 -1
  25. package/engine/chenggu.js +20 -6
  26. package/engine/convert.js +18 -9
  27. package/engine/daliuren.js +87 -18
  28. package/engine/datetime.js +73 -0
  29. package/engine/diff.js +1 -1
  30. package/engine/dst.js +0 -7
  31. package/engine/fengshui.js +19 -3
  32. package/engine/fingerprint.js +2 -1
  33. package/engine/followup.js +33 -3
  34. package/engine/geo.js +58 -12
  35. package/engine/handle.js +53 -1
  36. package/engine/index.js +135 -66
  37. package/engine/interpret-knowledge.js +3 -3
  38. package/engine/liuyao.js +27 -4
  39. package/engine/lunar.js +0 -7
  40. package/engine/mbti.js +1 -0
  41. package/engine/meihua.js +27 -16
  42. package/engine/name.js +15 -6
  43. package/engine/numerology.js +4 -3
  44. package/engine/qimen-interpret.js +28 -3
  45. package/engine/qimen-prompt.js +23 -17
  46. package/engine/qimen.js +32 -13
  47. package/engine/rectify.js +47 -6
  48. package/engine/star.js +5 -4
  49. package/engine/tarot.js +24 -6
  50. package/engine/tiaohou.js +17 -10
  51. package/engine/trend.js +14 -2
  52. package/engine/validate.js +30 -0
  53. package/engine/xiaoliuren.js +28 -7
  54. package/engine/yingqi.js +4 -9
  55. package/engine/zeri.js +19 -21
  56. package/engine/ziwei-hepan.js +3 -1
  57. package/engine/ziwei-words.js +2 -1
  58. package/engine/ziwei.js +15 -10
  59. package/index.js +99 -10
  60. package/package.json +1 -1
  61. package//345/221/275/347/220/206/346/216/222/347/233/230.html +11787 -11141
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Host 半:把排盘引擎注册成一个模型可调用的工具。
3
3
  * 纯计算;唯一的网络行为是按需把地址解析成经度(失败即退回内置坐标表)。
4
4
  */
5
- import { compute, renderText, geocodeAddress, caliberCompare, resolveHandle } from './engine/index.js';
5
+ import { compute, renderText, geocodeAddress, caliberCompare, resolveHandle, pruneForeignKeys, ownedKeys, normalizeLongitudes } from './engine/index.js';
6
6
  import { createLru } from './engine/lru.js';
7
7
  import { cacheMetrics } from './engine/calendar.js';
8
8
  import { parseRevision, displayValue } from './engine/revise.js';
@@ -78,13 +78,18 @@ export function apply(ctx) {
78
78
  } catch (e) {
79
79
  throw e && e.code ? e : bad(`handle 无法使用:${String((e && e.message) || e)}`);
80
80
  }
81
+ if (typeof next.system !== 'string' || !next.system.trim()) {
82
+ throw bad('需要 system(或带 system 的 handle):例如 { system: "bazi", datetime: "1990-05-12T14:30", gender: "male" }');
83
+ }
81
84
  // 一句话改参:先落到参数上,后面的地址解析、日期补全才按改后的体系走;
82
85
  // 改了什么同时留给对外回显(compute 里也会再算一遍,结果相同,这里以宿主这份为准)
83
86
  let revision = null;
87
+ let revisionChangedAddress = false;
84
88
  if (typeof next.revise === 'string' && next.revise.trim()) {
85
89
  const rev = parseRevision(next.revise);
86
90
  const before = { ...next };
87
- next = { ...next, ...rev.patch };
91
+ revisionChangedAddress = typeof rev.patch.address === 'string';
92
+ next = pruneForeignKeys({ ...next, ...rev.patch }, ownedKeys(args, rev.patch));
88
93
  revision = {
89
94
  text: rev.text,
90
95
  applied: rev.labels.map((l) => {
@@ -95,6 +100,19 @@ export function apply(ctx) {
95
100
  };
96
101
  }
97
102
  if (next.shadow === true && !next.shadowVersion) next.shadowVersion = PKG_VERSION;
103
+ // hour / date / minute 只有会读它们的体系放行:其它体系收了也不看,用户以为改的是时刻,
104
+ // 实际仍按 datetime(或 00:00)排。宁可直接报错,也不静默排成另一张盘。
105
+ // name 例外——它除 datetime 外还支持「年/月/日(+时/分)」分开给生辰(engine/index.js 的 name 分支真读)。
106
+ const HOUR_SYSTEMS = new Set(['chenggu', 'xiaoliuren', 'name']);
107
+ if (next.hour !== undefined && next.hour !== null && !HOUR_SYSTEMS.has(next.system)) {
108
+ throw bad(`hour 仅用于 chenggu / xiaoliuren / name;${next.system} 请把时刻写进 datetime,例如 1990-05-12T14:30`);
109
+ }
110
+ if (next.minute !== undefined && next.minute !== null && next.system !== 'name') {
111
+ throw bad(`minute 仅用于 name(配合 year / month / day);${next.system} 请把时刻写进 datetime,例如 1990-05-12T14:30`);
112
+ }
113
+ if (next.date !== undefined && next.date !== null && next.system !== 'xiaoliuren') {
114
+ throw bad(`date 仅用于 xiaoliuren 时间起课;${next.system} 请用 datetime`);
115
+ }
98
116
  // 自由文本长度上限:这些串会整段进句柄(handle 是参数的 base64url),
99
117
  // 不设限就等于允许一次调用把结果撑到几百 KB——摘要、句柄、影子盘四处都要重抄一遍。
100
118
  const MAX_LEN = { address: 80, question: 300, revise: 200, ask: 200, surname: 16, givenName: 16 };
@@ -103,9 +121,19 @@ export function apply(ctx) {
103
121
  throw bad(`${k} 最长 ${lim} 个字符(收到 ${next[k].length} 个);写要点即可,例如「成都」「今年是否适合换工作」`);
104
122
  }
105
123
  }
106
- if (next.partner && typeof next.partner === 'object' && typeof next.partner.address === 'string'
107
- && next.partner.address.length > MAX_LEN.address) {
108
- throw bad(`partner.address 最长 ${MAX_LEN.address} 个字符(收到 ${next.partner.address.length} 个)`);
124
+ for (const who of ['partner', 'partner2']) {
125
+ const p = next[who];
126
+ if (p && typeof p === 'object' && typeof p.address === 'string' && p.address.length > MAX_LEN.address) {
127
+ throw bad(`${who}.address 最长 ${MAX_LEN.address} 个字符(收到 ${p.address.length} 个)`);
128
+ }
129
+ }
130
+ // 经度先按引擎那把尺归一("87.6" → 87.6;非十进制 / 超范围当场报错):
131
+ // 下面的 resolveGeo 只认 typeof === 'number',不归一时数字串形式的显式经度会被联网结果顶掉,
132
+ // 用户明明写了喀什 87.6,盘却按解析出的 121.5 排。
133
+ try {
134
+ next = normalizeLongitudes(next);
135
+ } catch (e) {
136
+ throw e && e.code ? e : bad(`经度无法使用:${String((e && e.message) || e)}`);
109
137
  }
110
138
  // 默认离线:内置表覆盖到地级市,足够真太阳时用;联网是把出生地原文发给第三方,必须显式开启
111
139
  const wantsOnline = next.geocode === 'online';
@@ -117,12 +145,34 @@ export function apply(ctx) {
117
145
  const geo = await geocodeCached(person.address, { online: true });
118
146
  if (geo && typeof geo.lon === 'number') {
119
147
  person.longitude = geo.lon;
148
+ // 标出来源:下一轮换了地址/一句话改参时,派生经度要丢掉重解析,
149
+ // 否则新地址被旧经度顶掉,盘不变、不联网、也不提示。
150
+ person.longitudeFromGeo = true;
120
151
  person.geo = geo;
121
152
  }
122
153
  } catch (e) {
123
154
  // 联网失败不影响排盘,退回内置表
124
155
  }
125
156
  };
157
+ // 句柄里带着上一轮的经度(可能是联网派生的)。本次显式给了新地址,或一句话改了出生地,
158
+ // 就先把派生经度清掉,让 resolveGeo 按新地址重新解析;用户亲手给的 longitude 不动。
159
+ const dropGeo = (person) => {
160
+ if (!person) return person;
161
+ const out = { ...person };
162
+ delete out.longitude;
163
+ delete out.geo;
164
+ delete out.longitudeFromGeo;
165
+ return out;
166
+ };
167
+ const hadHandle = typeof args.handle === 'string' && args.handle.trim() !== '';
168
+ if (hadHandle) {
169
+ // 换了地址却没另给经度:句柄里的旧经度(可能是联网派生的)必须让位,否则盘不变。
170
+ if ((typeof args.address === 'string' || revisionChangedAddress) && args.longitude === undefined) next = dropGeo(next);
171
+ if (args.partner && typeof args.partner === 'object' && args.partner.longitude === undefined
172
+ && typeof args.partner.address === 'string') next.partner = dropGeo(next.partner);
173
+ if (args.partner2 && typeof args.partner2 === 'object' && args.partner2.longitude === undefined
174
+ && typeof args.partner2.address === 'string') next.partner2 = dropGeo(next.partner2);
175
+ }
126
176
  // fengshui 不在名单里:玄空/八宅只看 year/month/day/gender 与坐向,
127
177
  // 经度对盘面毫无影响,把出生地发给第三方就是纯外泄。
128
178
  if (next.system === 'bazi' || next.system === 'ziwei' || next.system === 'hepan'
@@ -133,6 +183,12 @@ export function apply(ctx) {
133
183
  next.partner = { ...next.partner }; // 不改调用方传进来的对象
134
184
  await resolveGeo(next.partner);
135
185
  }
186
+ // 第三方也按出生地修正:只给主方与对方解析,三方盘里的第三人就永远用离线表,
187
+ // 与另外两人的口径不一致。
188
+ if (next.system === 'hepan' && next.partner2 && typeof next.partner2 === 'object') {
189
+ next.partner2 = { ...next.partner2 };
190
+ await resolveGeo(next.partner2);
191
+ }
136
192
  // 宿主有时钟:日期类体系缺日期时补「今天」,免得模型自己猜日期;
137
193
  // 择日与此刻起课同理——补上后仍会把实际采用的日期写进结果,可回溯。
138
194
  const now = new Date();
@@ -193,7 +249,11 @@ export function apply(ctx) {
193
249
  },
194
250
  hour: {
195
251
  type: 'integer',
196
- description: '时辰小时(0–23)。chenggu 用它补时辰(datetime 不带时分时必填);xiaoliuren 时间起课用它代替 datetime 的时分。',
252
+ description: '时辰小时(0–23)。chenggu 用它补时辰(datetime 不带时分时必填);xiaoliuren 时间起课用它代替 datetime 的时分;name 用 year/month/day 给生辰时用它带出生时(也可用 datetime)。',
253
+ },
254
+ minute: {
255
+ type: 'integer',
256
+ description: '分钟(0–59),仅 name 使用:配合 year / month / day 给生辰,省略按整点。',
197
257
  },
198
258
  dst: {
199
259
  type: 'boolean',
@@ -223,7 +283,7 @@ export function apply(ctx) {
223
283
  lateZi: {
224
284
  type: 'boolean',
225
285
  description:
226
- '23:00 后是否算次日(晚子时换日)。八字/大六壬默认 true(换日);奇门默认 false(归当日),也可用 lateZiHourMode 指定。输出摘要会写明实际采用的口径。',
286
+ '23:00 后是否算次日(晚子时换日)。八字/大六壬/称骨/小六壬/梅花/紫微默认 true(换日);奇门默认 false(归当日),也可用 lateZiHourMode 指定。输出摘要会写明实际采用的口径。',
227
287
  },
228
288
  trueSolar: {
229
289
  type: 'boolean',
@@ -373,7 +433,17 @@ export function apply(ctx) {
373
433
  },
374
434
  required: ['datetime', 'gender'],
375
435
  },
376
- upper: { type: 'number', description: '梅花数字起卦:第一个数(定上卦)。' },
436
+ chart: {
437
+ type: 'string',
438
+ enum: ['bazi', 'ziwei'],
439
+ description: '仅 hepan 使用:合盘底层用哪套盘(bazi 四柱合参 / ziwei 紫微合盘),默认 bazi。',
440
+ },
441
+ rectifyHours: {
442
+ type: 'array',
443
+ items: { type: 'integer', minimum: 0, maximum: 23 },
444
+ description: '仅八字 rectify 使用:只在这些时辰(0–23)里候选;省略则用全部十二时辰。用于已知大致时段时缩小范围。',
445
+ },
446
+ upper: { type: 'number', description: '梅花数字起卦:第一个数(定上卦),与 lower 成对。' },
377
447
  lower: { type: 'number', description: '梅花数字起卦:第二个数(定下卦)。' },
378
448
  changing: { type: 'number', description: '梅花数字起卦:可手动指定动爻(1-6);省略则按两数之和取。' },
379
449
  count: {
@@ -417,7 +487,7 @@ export function apply(ctx) {
417
487
  },
418
488
  kuiyueSchool: {
419
489
  type: 'string',
420
- enum: ['qs1', 'qs2', 'zhong-yiming', 'network'],
490
+ enum: ['qs1', 'qs2', 'zhong-yiming'],
421
491
  description: '紫微斗数:天魁天钺安法,默认 qs1(全书理解一),差异集中在庚辛壬癸年。',
422
492
  },
423
493
  tianshiTianshang: {
@@ -491,13 +561,32 @@ export function apply(ctx) {
491
561
  surname: { type: 'string', description: '仅 name 使用:姓氏,如「李」(复姓整体写,如「欧阳」)。' },
492
562
  givenName: { type: 'string', description: '仅 name assess 使用:名(1–2 个汉字)。' },
493
563
  limit: { type: 'number', description: '仅 name generate 使用:候选个数(1–20,默认 10)。' },
564
+ charElements: {
565
+ type: 'object',
566
+ description: '仅 name 使用:给未收录字指定五行,如 {"琰":"火"}。字库里有该字时以字库为准。',
567
+ additionalProperties: { type: 'string', enum: ['木', '火', '土', '金', '水'] },
568
+ },
569
+ strokeOverrides: {
570
+ type: 'object',
571
+ description: '仅 name 使用:逐字覆盖康熙笔画,如 {"沐":8}。用于字库未收的字或按别派笔画计算。',
572
+ additionalProperties: { type: 'integer', minimum: 1 },
573
+ },
574
+ surnameStrokes: {
575
+ type: 'object',
576
+ description: '仅 name 使用:按姓氏逐字覆盖康熙笔画,如 {"欧":15,"阳":17};strokeOverrides 优先。',
577
+ additionalProperties: { type: 'integer', minimum: 1 },
578
+ },
579
+ year: { type: 'integer', description: '公历出生年(仅 name 的 assess/generate,配合 month/day/hour;也可直接用 datetime)。' },
580
+ month: { type: 'integer', description: '公历出生月 1–12(仅 name)。' },
581
+ day: { type: 'integer', description: '公历出生日 1–31(仅 name)。' },
494
582
  answers: {
495
583
  type: 'array',
496
584
  items: { type: ['integer', 'null'] },
497
585
  description: '仅 mbti 使用:按题目顺序的 24 个分数,1(完全不像)–5(很像我);跳过某题传 null。不给则返回问卷题目,答满 12 题即可出类型。',
498
586
  },
499
587
  },
500
- required: ['system'],
588
+ // 不写 required:只带 handle 的追问是文档主推用法,schema 层拦住会让宿主先拒绝。
589
+ // 真正的必填校验放到 run():system 或 handle 至少给一个。
501
590
  },
502
591
  output: {
503
592
  schema: { type: 'object' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-metaphysics",
3
- "version": "0.61.2",
3
+ "version": "0.62.1",
4
4
  "type": "module",
5
5
  "description": "命理排盘(玄学 xuanxue)for DeepSeek Harness: nineteen systems and eleven quick references (almanac, solar–lunar conversion, …), with climate adjustment (tiaohou), the 120 Qiongtong Baojian entries, 1,062-char name library with per-character pinyin, Kangxi stroke counts, five-grid (五格/三才) analysis and mechanical phonetics checks, a 78-card tarot library on the Rider-Waite-Smith base (per-card English name, image description, core meaning and astrological correspondence, cross-checked against three published sources; court cards carry no zodiac by design and the chart says so), classical quotes (Ziping Zhenquan / Di Tian Sui / San Ming Tong Hui), hexagram body relations (六冲/六合/伏吟/反吟/化进神/化退神) plus yongshen placement (用神落位) with month/day strength (月建日辰旺衰) and whole-hexagram per-line strength (全卦逐爻旺衰) and response-time clues (应期线索:逢值/逢冲/逢合 + 月建五态快慢) for liuyao, plus an MBTI-style 16-type questionnaire and exact dayun handover dates (交运日) with annual suiyun hints (岁运并临/伏吟反吟), palace conditions (击刑/入墓), month states (旺相休囚死) and stem–palace relations (天盘干与宫生克) for qimen, flying four-transformations with chained flights (四化飞星串连), three-way-four-corner composite flights (三方四正复合飞化), switchable leap-month policy (闰月按日切分/整月归次月/整月归本月) eleven school switches (亮度/庚干四化/天马/天空/截空旬空/魁钺/天伤天使/长生/晚子时/身主/杂曜全表) for ziwei, the last one opting in to a 61-star minor-star (杂曜) table where each of the twelve extra stars carries its placement verse and the palace it landed in (verses accepted only when at least two independent published editions give the same table, homonymous stars never merged), caliber diffs for bazi/ziwei/qimen (same inputs, one switch, root-field changes), shadow-chart IDs (same normalized params → same ID; version bumps auto-diff chart fields), step-by-step deduction traces (liuyao najia→liuqin→shiying→fushen→strength; bazi strength score and pattern/yongshen; ziwei pattern conditions), and a bundled offline single-file page (命理排盘.html), a panel-to-chat handoff that asks the Harness about a finished chart, per-tab lazy engine chunks so the first-screen client bundle stays under 100KB gzip, and a public verification report (128 classical hexagram cases, 312 Zi Wei charts × 34 patterns, 12 dayun handover cases) generated from the same source that gates CI.",
6
6
  "keywords": [