guanwei 1.3.1 → 1.3.4

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 (53) hide show
  1. package/README.md +28 -8
  2. package/dist/assets/index-DVd66cjI.js +102 -0
  3. package/dist/index.html +1 -1
  4. package/package.json +13 -7
  5. package/packages/guanwei-api/package.json +26 -0
  6. package/packages/guanwei-api/src/mcp-http.ts +89 -0
  7. package/packages/guanwei-api/src/mcp.ts +108 -0
  8. package/packages/guanwei-api/src/routes/arts.ts +23 -0
  9. package/packages/guanwei-api/src/routes/chart.ts +24 -0
  10. package/packages/guanwei-api/src/server.ts +73 -0
  11. package/scripts/check-boundary.mjs +80 -0
  12. package/scripts/check-package.mjs +71 -0
  13. package/scripts/credentials.mjs +52 -0
  14. package/scripts/github-history-cleanup.mjs +140 -0
  15. package/scripts/npm-release.mjs +79 -0
  16. package/scripts/preflight-release.sh +29 -0
  17. package/scripts/release.sh +8 -3
  18. package/server/package.json +24 -0
  19. package/server/src/index.ts +58 -16
  20. package/server/src/routes/ai.ts +11 -17
  21. package/server/src/routes/divine.ts +36 -29
  22. package/server/src/routes/hour.ts +4 -0
  23. package/server/src/routes/tarot.ts +30 -7
  24. package/server/src/routes/users.ts +99 -48
  25. package/server/src/services/auth.ts +53 -0
  26. package/server/src/services/chartBrief.ts +10 -0
  27. package/server/src/services/dataDir.ts +80 -0
  28. package/server/src/services/divineStore.ts +5 -2
  29. package/server/src/services/duanyu.ts +3 -3
  30. package/server/src/services/llmProvider.ts +4 -3
  31. package/server/src/services/usersDb.ts +45 -0
  32. package/shared/core/data/gua64.ts +5 -5
  33. package/shared/core/data/qimen.ts +11 -9
  34. package/shared/core/data/ziwei.ts +20 -15
  35. package/shared/core/engine/bazi.ts +64 -13
  36. package/shared/core/engine/calendar.ts +18 -5
  37. package/shared/core/engine/chart.ts +3 -1
  38. package/shared/core/engine/liuren.ts +32 -13
  39. package/shared/core/engine/liuyao.ts +6 -1
  40. package/shared/core/engine/qimen.ts +102 -60
  41. package/shared/core/engine/trueSolarTime.ts +9 -6
  42. package/shared/core/engine/xiaoliuren.ts +6 -2
  43. package/shared/core/engine/ziwei.ts +55 -1
  44. package/shared/core/types.ts +10 -6
  45. package/src/components/arts/XiaoliurenArt.tsx +1 -1
  46. package/src/pages/DemoPage.tsx +1 -1
  47. package/src/services/api.ts +18 -1
  48. package/src/utils/userStore.ts +6 -2
  49. package/vite.config.ts +7 -3
  50. package/dist/assets/index-O2O9peYE.js +0 -102
  51. package/dist/assets/index-O2O9peYE.js.map +0 -1
  52. package/server/src/data/db.json +0 -7016
  53. package/server/src/data/guanwei.db +0 -0
@@ -2,6 +2,7 @@
2
2
  import { NAYIN, ganZhiIndex, mod } from '../data/ganzhi';
3
3
  import { GAN } from '../data/ganzhi';
4
4
  import { trueSolarTime } from './trueSolarTime';
5
+ import { daysSince } from './calendar';
5
6
  import type { ZiweiInput, ZiweiResult } from '../types';
6
7
 
7
8
  import { PALACE_NAMES, ZW_SIHUA, ZW_BRIGHTNESS, LU_CUN, KUI_YUE, TIAN_MA, HUO_START, LING_START, ZW_GEJU } from '../data/ziwei';
@@ -124,6 +125,21 @@ export function ziweiCalc(input: ZiweiInput): ZiweiResult {
124
125
  fuStars['天哭'] = mod(yearZhiIdx + 6, 12); // 午起子年顺行
125
126
  fuStars['天虚'] = mod(fuStars['天哭'] + 6, 12);
126
127
 
128
+ /* 1.6 杂曜(修正反馈:辅星/杂曜覆盖有限,2026-09-05 补常用)
129
+ 三台/八座:日支起子时顺/逆数至生时(日支由公历日期推,同八字历元) */
130
+ const dayZhiIdx = input.solarDate
131
+ ? mod(daysSince(input.solarDate[0], input.solarDate[1], input.solarDate[2]) + 55, 60) % 12
132
+ : 0;
133
+ fuStars['三台'] = mod(dayZhiIdx + correctedHour, 12);
134
+ fuStars['八座'] = mod(dayZhiIdx - correctedHour, 12);
135
+ // 天才/天寿(年干禄存、年支起法)
136
+ const luIdx2 = LU_CUN[gan] ? zhiIdx(LU_CUN[gan]) : 0;
137
+ fuStars['天才'] = mod(luIdx2 + correctedHour, 12);
138
+ fuStars['天寿'] = mod(yearZhiIdx + 5, 12); // 子年起未宫顺行(近似,无古籍精确法时按口诀)
139
+ // 恩光/天贵(年干禄存宫起,恩光顺行、天贵逆行)
140
+ fuStars['恩光'] = mod(luIdx2 + correctedHour, 12);
141
+ fuStars['天贵'] = mod(luIdx2 + 6 - correctedHour, 12);
142
+
127
143
  /* 2. 生年四化 */
128
144
  const sh = ZW_SIHUA[gan] || { lu: '', quan: '', ke: '', ji: '' };
129
145
  const sihua = { lu: sh.lu, quan: sh.quan, ke: sh.ke, ji: sh.ji };
@@ -140,6 +156,20 @@ export function ziweiCalc(input: ZiweiInput): ZiweiResult {
140
156
  brightness[s] = (ZW_BRIGHTNESS[s] || [])[pos] || '平';
141
157
  });
142
158
 
159
+ /* 3.1 昼夜调整(修正反馈:太阳喜昼、太阴喜夜,brightness 只按宫位未计生时昼夜)
160
+ 规则:昼生(卯~申时支 3..8,时辰序 0=子)太阳+1 级、太阴-1 级;夜生反之;庙/陷为界不越
161
+ (2026-09 修正:原 2..7 实为寅~未,把凌晨寅时误判为昼、下午申时误判为夜;昼夜分界按卯~申对齐) */
162
+ const LV: Record<string, number> = { 不: 1, 陷: 1, 平: 2, 利: 3, 得: 4, 旺: 5, 庙: 6 };
163
+ const LV_NAME = ['', '陷', '平', '利', '得', '旺', '庙'];
164
+ const dayNight: 'day' | 'night' = correctedHour >= 3 && correctedHour <= 8 ? 'day' : 'night';
165
+ const effBrightness: Record<string, string> = {};
166
+ for (const s of Object.keys(brightness)) {
167
+ let lv = LV[brightness[s]] || 2;
168
+ if (s === '太阳') lv += dayNight === 'day' ? 1 : -1;
169
+ if (s === '太阴') lv += dayNight === 'night' ? 1 : -1;
170
+ effBrightness[s] = LV_NAME[Math.max(1, Math.min(6, lv))];
171
+ }
172
+
143
173
  /* 4. 格局识别 */
144
174
  const geju: ZiweiResult['geju'] = [];
145
175
  const pushGeju = (key: string, why: string) => {
@@ -191,6 +221,30 @@ export function ziweiCalc(input: ZiweiInput): ZiweiResult {
191
221
  if (zwStars['廉贞'] === zwStars['七杀']) pushGeju('廉贞七杀', '廉贞七杀同守 ' + DIZHI[zwStars['廉贞']] + ' 宫');
192
222
  // 命无正曜
193
223
  if (mingStars.length === 0) pushGeju('命无正曜', '命宫无十四主星,借对宫 ' + [...new Set(duiStars)].join('、') + ' 为用');
224
+ // 水澄桂萼:太阴在子庙且坐命/命三方(传统格须太阴得地于命局关键位)
225
+ const moonPos = zwStars['太阴'];
226
+ const moonInKey = moonPos !== undefined && (moonPos === ming || sanhe.includes(moonPos));
227
+ if (moonInKey && effBrightness['太阴'] === '庙' && DIZHI[moonPos] === '子') pushGeju('水澄桂萼', '太阴' + DIZHI[moonPos] + '宫庙旺' + (moonPos === ming ? '坐命' : '照命三方'));
228
+ // 明珠出海:太阴在未(位置 5)庙(同上:坐命/三方)
229
+ if (moonInKey && effBrightness['太阴'] === '庙' && DIZHI[moonPos] === '未') pushGeju('明珠出海', '太阴' + DIZHI[moonPos] + '宫庙旺' + (moonPos === ming ? '坐命' : '照命三方'));
230
+ // 日照雷门:太阳在卯(位置 1)庙旺 + 昼生
231
+ const sunPos = zwStars['太阳'];
232
+ if (sunPos !== undefined && DIZHI[sunPos] === '卯' && effBrightness['太阳'] !== '陷' && effBrightness['太阳'] !== '平') pushGeju('日照雷门', '太阳坐卯宫' + effBrightness['太阳'] + ',' + (dayNight === 'day' ? '昼生' : '夜生') + '人');
233
+ // 日月并明:太阳庙旺于卯辰巳 且 太阴庙旺于酉戌亥
234
+ const sunGood = sunPos !== undefined && ['卯', '辰', '巳'].includes(DIZHI[sunPos]) && effBrightness['太阳'] !== '陷' && effBrightness['太阳'] !== '平';
235
+ const moonGood = moonPos !== undefined && ['酉', '戌', '亥'].includes(DIZHI[moonPos]) && effBrightness['太阴'] !== '陷' && effBrightness['太阴'] !== '平';
236
+ if (sunGood && moonGood) pushGeju('日月并明', '太阳' + DIZHI[sunPos] + effBrightness['太阳'] + '、太阴' + DIZHI[moonPos] + effBrightness['太阴'] + '皆得地');
237
+
238
+ /* 4.1 空宫借对宫(修正反馈:空宫须借对宫主星看)——十二宫逐一输出所借主星 */
239
+ const borrowedStars: Record<number, string[]> = {};
240
+ for (let i = 0; i < 12; i++) {
241
+ const p = palaces[i];
242
+ const own = Object.keys(zwStars).filter(s => zwStars[s] === p);
243
+ if (own.length === 0) {
244
+ const opp = mod(p + 6, 12);
245
+ borrowedStars[i] = Object.keys(zwStars).filter(s => zwStars[s] === opp);
246
+ }
247
+ }
194
248
 
195
249
  /* ===== 大限与流年(原逻辑保留) ===== */
196
250
  const JU_AGE: Record<string, number> = { 二: 2, 三: 3, 四: 4, 五: 5, 六: 6 };
@@ -214,5 +268,5 @@ export function ziweiCalc(input: ZiweiInput): ZiweiResult {
214
268
  const liunianPalaceName = PALACE_NAMES[Object.keys(palaces).find(k => palaces[Number(k)] === liunianIdx) ? Number(Object.keys(palaces).find(k => palaces[Number(k)] === liunianIdx)) : 0];
215
269
  const liunianStars = Object.keys(zwStars).filter(s => zwStars[s] === liunianIdx);
216
270
 
217
- return { ming, shen, zwPos, zwStars, fuStars, palaces, juName, mingGZ, nayin, correctedHour, dayun, curDayunIdx, nominalAge, liunianIdx, liunianPalaceName, liunianStars, startAge, forward, sihua, sihuaPos, brightness, geju };
271
+ return { ming, shen, zwPos, zwStars, fuStars, palaces, juName, mingGZ, nayin, correctedHour, dayun, curDayunIdx, nominalAge, liunianIdx, liunianPalaceName, liunianStars, startAge, forward, sihua, sihuaPos, brightness, geju, dayNight, effBrightness, borrowedStars };
218
272
  }
@@ -87,8 +87,12 @@ export interface ZiweiResult {
87
87
  fuStars: Record<string, number>; // 辅星位置(0=寅起)
88
88
  sihua: { lu: string; quan: string; ke: string; ji: string }; // 生年四化星名
89
89
  sihuaPos: { lu?: number; quan?: number; ke?: number; ji?: number }; // 四化落宫位
90
- brightness: Record<string, string>; // 十四主星亮度('庙'|'旺'|'得'|'利'|'平'|'陷')
90
+ brightness: Record<string, string>; // 十四主星亮度('庙'|'旺'|'得'|'利'|'平'|'陷'|'不'(不得地))
91
91
  geju: { key: string; name: string; ji: '吉' | '凶' | '平'; desc: string; why: string }[]; // 格局
92
+ // ─── 修正层(2026-09-05 算法边界反馈)───
93
+ dayNight?: 'day' | 'night'; // 昼生/夜生(按生时:卯~申为昼,酉~寅为夜;时辰序 0=子)
94
+ effBrightness?: Record<string, string>; // 昼夜调整后有效亮度(太阳喜昼、太阴喜夜)
95
+ borrowedStars?: Record<number, string[]>; // 空宫借对宫主星(十二宫位 → 所借主星)
92
96
  }
93
97
 
94
98
  // 奇门输入输出
@@ -97,12 +101,12 @@ export interface QimenResult {
97
101
  yin: boolean; ju: number; jqName: string;
98
102
  dayGZ: string; hourGZ: string;
99
103
  xunShou: string; xunshouName: string;
100
- zfStar: string; zsMen: string; zfPalace: number;
104
+ zfStar: string; zsMen: string; zfPalace: number; // 值符星/值使门/值符转后落宫(入中寄坤=2)
101
105
  pan: Record<number, { yi: string; men: string; star: string }>;
102
- // ─── 补齐层(2026-08-20)───
103
- zsPalace: number; // 值使门落宫(时干加临)
104
- tianYi: Record<number, string>; // 天盘奇仪(暗干)
105
- shen: Record<number, string>; // 八神布宫
106
+ // ─── 补齐层(2026-08-20;2026-09 起转盘五层对齐拆补权威口径)───
107
+ zsPalace: number; // 值使门飞落宫(入中寄坤=2)
108
+ tianYi: Record<number, string>; // 天盘奇仪(地盘旋转,符首→时干宫;中宫原地盘干)
109
+ shen: Record<number, string>; // 八神布宫(阳顺阴逆,值符神落时干宫;阳用勾陈朱雀、阴用白虎玄武)
106
110
  }
107
111
 
108
112
  // 梅花输入输出
@@ -40,7 +40,7 @@ export function XiaoliurenPanel({ onDivine }: PanelProps) {
40
40
  </div>
41
41
  )}
42
42
  <div className="field"><label htmlFor="xlr-h">时(时辰序)</label>
43
- <SongSelect id="xlr-h" value={String(h)} options={['子(1)', '丑(2)', '寅(3)', '卯(4)', '辰(5)', '巳(6)', '午(7)', '未(8)', '申(9)', '酉(10)', '戌(11)', '亥(12)'].map((t, i) => ({ value: String(i + 1), label: t }))} onChange={v => setH(Number(v))} />
43
+ <SongSelect id="xlr-h" value={String(h)} options={['子(0)', '丑(1)', '寅(2)', '卯(3)', '辰(4)', '巳(5)', '午(6)', '未(7)', '申(8)', '酉(9)', '戌(10)', '亥(11)'].map((t, i) => ({ value: String(i), label: t }))} onChange={v => setH(Number(v))} />
44
44
  </div>
45
45
  {mode === 'num' && (
46
46
  <div className="field"><label htmlFor="xlr-n3">三数</label><input className="input-line" type="number" id="xlr-n3" min={1} max={12} value={n3} onChange={e => setN3(Number(e.target.value))} /></div>
@@ -84,7 +84,7 @@ export default function DemoPage() {
84
84
  case 'meihua': return () => meihuaCalc({ mode: 'number', n1, n2, n3, now: new Date() } as any);
85
85
  case 'liuyao': return () => liuyaoCalc(undefined, { y, m, d });
86
86
  case 'liuren': return () => liurenCalc(new Date(y, m - 1, d, hour, 0));
87
- case 'xiaoliuren': return () => xiaoliurenCalc('time', m, d, Math.floor((hour % 24) / 2), n1, n2, n3);
87
+ case 'xiaoliuren': return () => xiaoliurenCalc('time', m, d, Math.floor(((hour % 24) + 1) % 24 / 2), n1, n2, n3);
88
88
  case 'tarot': return () => {
89
89
  const spread = allTarotSpreads().find((x: any) => x.id === 'three') || allTarotSpreads()[0];
90
90
  return { spread, cards: tarotDraw(3) };
@@ -264,7 +264,24 @@ export async function apiDivine(username: string, artId: string, inputs: unknown
264
264
  try { const e = await res.json(); msg = e.message || e.error || msg; } catch { /* ignore */ }
265
265
  throw new Error(msg);
266
266
  }
267
- return res.json();
267
+ const data = await res.json();
268
+ // L-NEW1 闭环:后端在「本次新建占位账号」时下发 claimToken(仅此一次),
269
+ // 保存到本地,供之后在 app 内注册时升级该占位账号(否则注册接口拒绝、拿不到云同步 token)
270
+ if (data?.claimToken) saveClaimToken(username, String(data.claimToken));
271
+ return data;
272
+ }
273
+
274
+ const CLAIM_KEY = (name: string) => 'guanwei_claim_' + name;
275
+
276
+ /** 保存/读取占位账号认领凭据(仅本地持有,随注册一次性使用) */
277
+ export function saveClaimToken(username: string, token: string): void {
278
+ try { localStorage.setItem(CLAIM_KEY(username), token); } catch { /* ignore */ }
279
+ }
280
+ export function claimTokenOf(username: string): string {
281
+ try { return localStorage.getItem(CLAIM_KEY(username)) || ''; } catch { return ''; }
282
+ }
283
+ export function clearClaimToken(username: string): void {
284
+ try { localStorage.removeItem(CLAIM_KEY(username)); } catch { /* ignore */ }
268
285
  }
269
286
 
270
287
  export interface DivineHistoryItem {
@@ -1,6 +1,7 @@
1
1
  // 本地用户体系(演示级):注册/登录/会话/出生档案
2
2
  // 注意:仅本地存储(localStorage),密码为演示级哈希,不适用于生产环境
3
3
  import type { GeoLocation } from '@core/types';
4
+ import { claimTokenOf, clearClaimToken } from '../services/api';
4
5
 
5
6
  export interface UserProfile {
6
7
  birthDate: string; // YYYY-MM-DD(公历)
@@ -142,13 +143,16 @@ async function fetchServerToken(username: string, password: string): Promise<voi
142
143
  body: JSON.stringify({ username, password }),
143
144
  });
144
145
  if (!res.ok) {
145
- // 后端无此用户 → 注册(自动建档即可;注册接口对已有用户返回 400)
146
+ // 后端无此用户 → 注册;若该名号已被「起占自动建档」占位,须携带建档时下发的 claimToken 方可升级
147
+ // (claimToken 由起占响应下发并由 services/api 落地本地,见 L-NEW1 闭环)
148
+ const claimToken = claimTokenOf(username);
146
149
  res = await fetch(API + '/register', {
147
150
  method: 'POST',
148
151
  headers: { 'Content-Type': 'application/json' },
149
- body: JSON.stringify({ username, password }),
152
+ body: JSON.stringify({ username, password, ...(claimToken ? { claimToken } : {}) }),
150
153
  });
151
154
  if (!res.ok) return;
155
+ clearClaimToken(username); // 一次性凭据,成功升级后即弃
152
156
  }
153
157
  const data = await res.json();
154
158
  if (data.token) {
package/vite.config.ts CHANGED
@@ -3,17 +3,21 @@ import react from '@vitejs/plugin-react'
3
3
  import tsconfigPaths from "vite-tsconfig-paths";
4
4
 
5
5
  // https://vite.dev/config/
6
+ // 后端地址:跟随 CLI 的 PORT/API_PORT(`guanwei start --api 4000` 时前端代理需同步)
7
+ const API_TARGET = `http://localhost:${process.env.API_PORT || process.env.PORT || 3018}`;
8
+
6
9
  export default defineConfig({
7
10
  test: {
8
11
  environment: 'jsdom',
9
12
  },
10
13
  build: {
11
- sourcemap: 'hidden',
14
+ // 生产不产出 sourcemap(原 'hidden' 仍生成 .map 文件,随 npm 包分发且可被公开访问 → 源码泄漏)
15
+ sourcemap: false,
12
16
  },
13
17
  server: {
14
18
  proxy: {
15
19
  '/api': {
16
- target: 'http://localhost:3018',
20
+ target: API_TARGET,
17
21
  changeOrigin: true,
18
22
  },
19
23
  },
@@ -22,7 +26,7 @@ export default defineConfig({
22
26
  preview: {
23
27
  proxy: {
24
28
  '/api': {
25
- target: 'http://localhost:3018',
29
+ target: API_TARGET,
26
30
  changeOrigin: true,
27
31
  },
28
32
  },