skill-calorie 0.2.6 → 0.3.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 (50) hide show
  1. package/SKILL.md +17 -13
  2. package/dist/cli/config.d.ts +24 -1
  3. package/dist/cli/config.d.ts.map +1 -1
  4. package/dist/cli/config.js +20 -7
  5. package/dist/cli/config.js.map +1 -1
  6. package/dist/config.d.ts +10 -3
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +60 -7
  9. package/dist/config.js.map +1 -1
  10. package/dist/fetch/audit.d.ts +2 -2
  11. package/dist/health.d.ts.map +1 -1
  12. package/dist/health.js +46 -43
  13. package/dist/health.js.map +1 -1
  14. package/dist/output.d.ts +4 -3
  15. package/dist/output.d.ts.map +1 -1
  16. package/dist/output.js +7 -11
  17. package/dist/output.js.map +1 -1
  18. package/dist/paths.d.ts +23 -1
  19. package/dist/paths.d.ts.map +1 -1
  20. package/dist/paths.js +44 -6
  21. package/dist/paths.js.map +1 -1
  22. package/dist/photo/dir.d.ts +1 -1
  23. package/dist/photo/dir.d.ts.map +1 -1
  24. package/dist/photo/dir.js +1 -1
  25. package/dist/photo/dir.js.map +1 -1
  26. package/dist/photo/photos.d.ts +3 -2
  27. package/dist/photo/photos.d.ts.map +1 -1
  28. package/dist/photo/photos.js +7 -8
  29. package/dist/photo/photos.js.map +1 -1
  30. package/dist/workout/landBatch.js +2 -2
  31. package/dist/workout/landRunner.d.ts +1 -1
  32. package/dist/workout/landRunner.d.ts.map +1 -1
  33. package/dist/workout/landRunner.js +15 -9
  34. package/dist/workout/landRunner.js.map +1 -1
  35. package/dist/workout/xunjiRunner.js +3 -3
  36. package/dist/xunji/catalog.d.ts +2 -4
  37. package/dist/xunji/catalog.d.ts.map +1 -1
  38. package/dist/xunji/catalog.js +7 -11
  39. package/dist/xunji/catalog.js.map +1 -1
  40. package/dist/xunji/index.d.ts +1 -1
  41. package/dist/xunji/index.js +1 -1
  42. package/dist/xunji/rateLimit.d.ts +4 -4
  43. package/dist/xunji/rateLimit.d.ts.map +1 -1
  44. package/dist/xunji/rateLimit.js +6 -6
  45. package/dist/xunji/rateLimit.js.map +1 -1
  46. package/dist/xunji/run-sync.d.ts +1 -1
  47. package/dist/xunji/run-sync.js +1 -1
  48. package/dist/xunji/run.d.ts +1 -1
  49. package/dist/xunji/run.d.ts.map +1 -1
  50. package/package.json +1 -1
package/SKILL.md CHANGED
@@ -25,7 +25,7 @@ calorie-cmd-read calorie.help.lookup --params '{"q":"看今日主页"}'
25
25
 
26
26
  ## 唯一出口(T11)
27
27
 
28
- - 二进制:`packages/skill-calorie/dist/cli/cmd_read.js`(bin `calorie-cmd-read`),纯 CLI 单轨,无面板/定时/外联动。
28
+ - 入口=本包 `bin` 声明的那条(`calorie-cmd-read`);**怎么调见下面「唯一出口:怎么跑」节**。纯 CLI 单轨,无面板/定时/外联动。
29
29
  - 运维定位(C1 #43):`skill-calorie-fetch`(`dist/fetch/cli.js`)仅运维(import/validate/dedupe/export/history/audit/catalog-verify),不承载业务读写;业务读写唯一出口仍为 `calorie-cmd-read`。
30
30
  - 契约:P9 冻结 argv+JSON+exit;缺 key exit 2、未知 key exit 3、取数/缺失 exit 4、envelope/渲染/落盘 exit 5、预检 exit 1。
31
31
  - stdout 纯净:成功只打 envelope JSON 一行;进度与错误一律 stderr;HTML 默认落 `<库目录>/<产物目录>/<中文command>_<YYYYMMDD>_<HHMMSS>[_N].html`(同秒冲突自动加 `_2`/`_3`)——`<库目录>`=配置文件 `~/.ilife/calorie.yaml` 的 `db.dir`,空串=数据目录 `~/.ilife/data/`;`<产物目录>`=同文件的 `html.dir`(默认 `calorie_html`)。落点回传在 envelope `data.output`(**恒绝对路径**,相对库目录/`--html` 亦按 cwd 归一后回传);`--html <路径>` 显式覆盖任意路径(**唯一落点参数**:老技能的 `--output` 别名已按 #245 删除,给了即 exit 2,与其余五家同形)。
@@ -546,17 +546,21 @@ calorie-cmd-read calorie.help.lookup --params '{"q":"看今日主页"}'
546
546
 
547
547
  ## 环境与出 scope
548
548
 
549
- - 路径类取值一律读配置文件 `~/.ilife/calorie.yaml`(**配置文件是唯一真相,环境变量不参与配置**):库目录=`db.dir`(空串=数据目录 `~/.ilife/data/`,首次读时自动建)、库文件名=`db.name`(默认 `calorie_data.db`)、产物目录=`html.dir`(默认 `calorie_html`)、照片目录=`photos.dir`(**空串=这一项还没配**:取数侧跳过「照片文件在不在」的校验、写侧直接阻断,不猜路径)、GIF 子目录=`photos.gifs`(默认 `gifs`)、训记五项=`xunji.*`、落地三项=`land.*`;`ILIFE_CONFIG_DIR` 设定且非空即整体接管配置目录。真实 DB 禁迁,测试 tmp 隔离;老家只读对照。取值面与环境项见 docs/env.md。
549
+ - 路径类取值一律读配置文件 `~/.ilife/calorie.yaml`(**配置文件是唯一真相,环境变量不参与配置**):库目录=`db.dir`(空串=数据目录 `~/.ilife/data/`,首次读时自动建)、库文件名=`db.name`(默认 `calorie_data.db`)、产物目录=`html.dir`(默认 `calorie_html`)、照片目录=`photos.dir`(**空串=这一项还没配**:取数侧跳过「照片文件在不在」的校验、写侧直接阻断,不猜路径)、GIF 子目录=`photos.gifs`(默认 `gifs`)、训记五项=`xunji.*`、落地三项=`land.*`。真实 DB 禁迁,测试 tmp 隔离;老家只读对照。取值面与环境项见 docs/env.md。
550
550
  - 出 scope(一期外):面板(二期单 MAP)、定时任务、本技能外联动(router+作息/备忘/训记仅只读对照,不落本包)。
551
551
 
552
- ## 公共安装器运行时(skills-cli 装完必读,#47)
553
-
554
- - 本仓库 `dist/` 不进 git:skills-cli 只把本目录(含本文件)装进 agent,不带可执行文件;“不走 npm”的只是 skill 发现这一步,运行时走 npm(`skill-calorie@0.2.6` 已发布)。
555
- - 取运行时二选一:`npm install -g skill-calorie@0.2.6`(一劳永逸),或免安装 `npx -p skill-calorie@0.2.6 calorie-cmd-read …`(每次现拉)。若 npm 报 EUNSUPPORTEDPROTOCOL(workspace:),说明已发布包待重发(发版流修,见 docs/public-installer-47.md「已发布包阻塞」),先用本仓构建产物验证链路。
556
- - HELP 现找→cmd_read→envelope→HTML 验证(隔离靠把**配置目录**指到临时目录:`ILIFE_CONFIG_DIR` 设定且非空即整体接管配置目录,配置里的 `db.dir` 再决定库与产物落哪;node>=22.13;完整口径见 docs/public-installer-47.md):
557
- ```sh
558
- cfg="$(mktemp -d)" && mkdir -p "$cfg/db" && printf 'db:\n dir: %s\n' "$cfg/db" > "$cfg/calorie.yaml"
559
- ILIFE_CONFIG_DIR="$cfg" calorie-cmd-read calorie.help.lookup --params '{"q":"看今日主页"}'
560
- ```
561
- - stdout/落盘契约与「唯一出口(T11)」节同源(成功只一行 envelope JSON;HTML 落点见 `data.output`)。
562
- - 版本钉死登记:本节版本硬编码现为 `@0.2.4`(已随 #44 发版流同步;历史登记见 docs/public-installer-47.md「版本钉死登记」)。
552
+ <!-- CALL-FORM-START -->
553
+
554
+ ## 唯一出口:怎么跑
555
+
556
+ 入口=本技能包 `package.json` `bin` 声明的那条:`dist/cli/cmd_read.js`。
557
+ `<技能基目录>`=加载本技能时给出的 `Base directory for this skill: <路径>` 那一行。
558
+
559
+ 1. 命令名解析得到时:`calorie-cmd-read <key> [--params '<json>']`。
560
+ 2. 解析不到时(`not recognized`/`command not found`)= PATH 上没有这条命令,下面这行照样跑得起来:
561
+ `node <技能基目录>/dist/cli/cmd_read.js <key> [--params '<json>']`
562
+ 3. 本目录里没有编译产物时:`npx -p skill-calorie calorie-cmd-read <key> [--params '<json>']`(上面第 2 行就够,不必再取一份)。
563
+
564
+ 换走法的信号只有一个:命令名解析不到。其余报错照 stderr 的报文原样交给用户。
565
+ <!-- CALL-FORM-END -->
566
+
@@ -9,8 +9,31 @@ export declare function isConfigKey(key: string): boolean;
9
9
  /**
10
10
  * 跑一个配置 key,返回整行 envelope JSON。
11
11
  *
12
- * 三个 key 的载荷:读 → `{path, dataDir, created, values}`;写 → 入参 `{values}`,回执 `{path, values}`;
12
+ * 三个 key 的载荷:读 → `{path, dataDir, created, values, resolved}`;写 → 入参 `{values}`,回执 `{path, values}`;
13
13
  * 重置 → `{path, backupPath}`(`backupPath` 为 null 表示本来就没有配置文件)。
14
+ *
15
+ * `resolved`(#757,照记账 #749 样板)=一组**解析后的绝对路径**,给设置页的只读行显示用:
16
+ * 生效数据目录/库文件/产物目录/照片目录/GIF 子目录/训记状态目录/动作库
17
+ * (算式唯一定义地=`src/paths.ts` 纯函数一族 + `src/xunji/catalog.ts` 的预置件,面板不自己拼路径)。
18
+ * 只算路径、不建目录、不写盘(设置页与体检都能随时调)。
14
19
  */
20
+ export interface CalorieResolvedPaths {
21
+ /** 生效数据目录(`db.dir` 非空用它,空=默认落点)——面板上可改项之一的生效值。 */
22
+ readonly dbDir: string;
23
+ /** 库文件绝对路径。 */
24
+ readonly dbFile: string;
25
+ /** HTML 产物目录绝对路径。 */
26
+ readonly htmlDir: string;
27
+ /** 照片目录绝对路径(空串按默认落点,不再是「未配置」)。 */
28
+ readonly photosDir: string;
29
+ /** 照片 GIF 子目录绝对路径。 */
30
+ readonly gifsDir: string;
31
+ /** 训记状态目录绝对路径(空串按默认落点,不再 `~/.mavis`)。 */
32
+ readonly stateDir: string;
33
+ /** 训记动作库绝对路径(空串=包内预置那份)。 */
34
+ readonly catalog: string;
35
+ }
36
+ /** 算那一组解析后的绝对路径。**只算路径、不建目录、不写盘**。 */
37
+ export declare function resolvedCaloriePaths(): CalorieResolvedPaths;
15
38
  export declare function runConfigKey(key: string, params: Record<string, unknown>): string;
16
39
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAiBA,wEAAwE;AACxE,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAEX,yBAAyB;AACzB,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAiCD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAejF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAmBA,wEAAwE;AACxE,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAEX,yBAAyB;AACzB,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAiCD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,oBAAoB;IACnC,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,eAAe;IACf,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qBAAqB;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kCAAkC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sBAAsB;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4BAA4B;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,sCAAsC;AACtC,wBAAgB,oBAAoB,IAAI,oBAAoB,CAc3D;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAgBjF"}
@@ -1,5 +1,7 @@
1
1
  import { CALORIE_SKILL, ENVELOPE_VERSION } from './keys.js';
2
2
  import { loadCalorieConfig, resetCalorieConfig, saveCalorieConfig } from '../config.js';
3
+ import { dbFileOf, gifsDirOf, htmlDirOf, photosDirOf, resolveDbDir, resolveDbFileName, stateDirOf } from '../paths.js';
4
+ import { XUNJI_CATALOG } from '../xunji/index.js';
3
5
  import { fail } from '../shared/params.js';
4
6
  /** 三个 key 的唯一定义地(插件侧镜像同值,见 `packages/plugin-calorie/src/bridge.ts`)。 */
5
7
  export const CONFIG_KEYS = {
@@ -42,16 +44,27 @@ function withHumanError(run) {
42
44
  fail(1, e instanceof Error ? e.message : String(e));
43
45
  }
44
46
  }
45
- /**
46
- * 跑一个配置 key,返回整行 envelope JSON。
47
- *
48
- * 三个 key 的载荷:读 → `{path, dataDir, created, values}`;写 → 入参 `{values}`,回执 `{path, values}`;
49
- * 重置 → `{path, backupPath}`(`backupPath` 为 null 表示本来就没有配置文件)。
50
- */
47
+ /** 算那一组解析后的绝对路径。**只算路径、不建目录、不写盘**。 */
48
+ export function resolvedCaloriePaths() {
49
+ const c = loadCalorieConfig();
50
+ const dbDir = resolveDbDir();
51
+ const photosDir = photosDirOf(dbDir, c.values.photos.dir);
52
+ const catalogConfigured = c.values.xunji.catalog;
53
+ return {
54
+ dbDir,
55
+ dbFile: dbFileOf(dbDir, resolveDbFileName()),
56
+ htmlDir: htmlDirOf(dbDir, c.values.html.dir),
57
+ photosDir,
58
+ gifsDir: gifsDirOf(photosDir, c.values.photos.gifs),
59
+ stateDir: stateDirOf(dbDir, c.values.xunji.stateDir),
60
+ catalog: catalogConfigured !== '' ? catalogConfigured : XUNJI_CATALOG.preset,
61
+ };
62
+ }
51
63
  export function runConfigKey(key, params) {
52
64
  if (key === CONFIG_KEYS.read) {
53
65
  const c = withHumanError(() => loadCalorieConfig());
54
- return envelope(key, 'detail', { path: c.path, dataDir: c.dataDir, created: c.created, values: c.values });
66
+ const resolved = withHumanError(() => resolvedCaloriePaths());
67
+ return envelope(key, 'detail', { path: c.path, dataDir: c.dataDir, created: c.created, values: c.values, resolved });
55
68
  }
56
69
  if (key === CONFIG_KEYS.write) {
57
70
  const raw = params['values'];
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -26,12 +26,16 @@ export declare const CALORIE_CONFIG_DEFAULTS: {
26
26
  backfillDays: number;
27
27
  };
28
28
  land: {
29
- scheduleCli: string;
30
- memoCli: string;
31
29
  xunjiSeconds: number;
32
30
  landSeconds: number;
33
31
  };
34
32
  };
33
+ /** **已退休键**(#762 过渡件 + #757 真删):我们自己删过、老配置文件里必然还留着的键的叶子全路径。命中的键跳过校验、
34
+ * 不进取值、也不会被写回(任何一次保存/重置天然抹掉它)。名单**只许写已经删掉的键**——键还在默认值表里
35
+ * 就写进清单=在护栏上开洞,`base-link-core` 收到清单时当场拒。
36
+ * `land.scheduleCli`/`land.memoCli`(#748 定稿)删于本票 #757:两个跨技能出口退回「按包布局推断」,
37
+ * 面板不再显示这两行。退出条件:下一个大版本删掉这张清单。 */
38
+ export declare const CALORIE_CONFIG_RETIRED: readonly string[];
35
39
  /** 取值形状由默认值表派生(同一件事只有一个定义地)。 */
36
40
  export type CalorieConfigValues = typeof CALORIE_CONFIG_DEFAULTS;
37
41
  /** 读回来的一份配置:文件路径(人话报错要指它)+数据目录+取值。 */
@@ -41,7 +45,10 @@ export interface LoadedCalorieConfig {
41
45
  readonly created: boolean;
42
46
  readonly values: CalorieConfigValues;
43
47
  }
44
- /** 读一份配置(文件不存在即按默认值落一份并把配置目录/数据目录建出来)。 */
48
+ /** 读一份配置(文件不存在即按默认值落一份并把配置目录/数据目录建出来)。
49
+ *
50
+ * 读回来的 `values` 是**文件里那份 ⊕ 默认值**(文件里写空串则仍是空串=按默认落点);
51
+ * 首次落文件时写下去的是 `writableDefaults()`(可改落点=绝对路径)。 */
45
52
  export declare function loadCalorieConfig(): LoadedCalorieConfig;
46
53
  /** 写一份配置(写出去的是完整一份:没给的项按默认值补齐)。写完清记忆,同进程后续读也现取。 */
47
54
  export declare function saveCalorieConfig(values: ConfigRecord): {
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,qCAAqC;AACrC,eAAO,MAAM,mBAAmB,EAAG,SAAkB,CAAC;AAEtD;;;;wDAIwD;AACxD,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;CAMnC,CAAC;AAEF,gCAAgC;AAChC,MAAM,MAAM,mBAAmB,GAAG,OAAO,uBAAuB,CAAC;AAEjE,sCAAsC;AACtC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;CACtC;AAOD,0CAA0C;AAC1C,wBAAgB,iBAAiB,IAAI,mBAAmB,CAiBvD;AAED,mDAAmD;AACnD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAIxE;AAED,4CAA4C;AAC5C,wBAAgB,kBAAkB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAIhF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,qCAAqC;AACrC,eAAO,MAAM,mBAAmB,EAAG,SAAkB,CAAC;AAEtD;;;;wDAIwD;AACxD,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;CAMnC,CAAC;AAEF;;;;mCAImC;AACnC,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAAyC,CAAC;AAE9F,gCAAgC;AAChC,MAAM,MAAM,mBAAmB,GAAG,OAAO,uBAAuB,CAAC;AA0CjE,sCAAsC;AACtC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;CACtC;AAOD;;;mDAGmD;AACnD,wBAAgB,iBAAiB,IAAI,mBAAmB,CAiBvD;AAED,mDAAmD;AACnD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAIxE;AAED,4CAA4C;AAC5C,wBAAgB,kBAAkB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAIhF"}
package/dist/config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /** 卡路里自己的配置面:那份默认值表 + 读/写/重置三个薄转调。
2
2
  *
3
3
  * 口径出处(「配置的存与生效口径裁定」#675 的解决评论,本票 #676 落地):
4
- * · 每个技能一份 YAML 配置文件,默认 `~/.ilife/calorie.yaml`;`ILIFE_CONFIG_DIR` 可覆盖位置;
4
+ * · 每个技能一份 YAML 配置文件,默认 `~/.ilife/calorie.yaml`(只此一处,由 `os.homedir()` 派生);
5
5
  * · **配置文件是唯一真相**,环境变量不参与配置;
6
6
  * · 「重置为默认」先自动备份 `.bak`,备份失败即不写盘、原文件保持不动。
7
7
  * 读写实现(受限 YAML 子集解析、键表与类型校验、备份、建目录)住 `base-link-core` 的 `src/config/`
@@ -10,8 +10,11 @@
10
10
  * 默认值逐项等于改造前的代码常量(对照读数见 `docs/plugins/plugin-calorie/t676-证据.md`)。
11
11
  * 用法上一处约定:**空串=按默认落点**——落点由各取用处按本域常量算(今天写死在哪,默认就是哪里),
12
12
  * 所以空串不是「没配」,而是「用老落点」,老数据不会看起来丢了。
13
+ * #757 起这条约定的**两半分头**(照记账 #749 样板):**读**认空串;**写**(首次落文件/保存/重置)把面板上可改的
14
+ * 那两格(`db.dir`/`photos.dir`)落成算出来的绝对路径——见下面 `writableDefaults()` 的注释。
13
15
  */
14
16
  import { configPaths, loadConfig, resetConfig, saveConfig } from 'base-link-core';
17
+ import { join } from 'node:path';
15
18
  /** 配置文件主体名:`<配置目录>/calorie.yaml`。 */
16
19
  export const CALORIE_CONFIG_STEM = 'calorie';
17
20
  /** 卡路里那份配置表:键即设置页的行,一层嵌套(子集支持范围见 base-link-core README)。
@@ -24,17 +27,67 @@ export const CALORIE_CONFIG_DEFAULTS = {
24
27
  html: { dir: 'calorie_html' },
25
28
  photos: { dir: '', gifs: 'gifs' },
26
29
  xunji: { key: '', cli: '', stateDir: '', catalog: '', backfillDays: 1 },
27
- land: { scheduleCli: '', memoCli: '', xunjiSeconds: 300, landSeconds: 60 },
30
+ land: { xunjiSeconds: 300, landSeconds: 60 },
28
31
  };
29
- /** 每进程按**配置目录**记一份:`ILIFE_CONFIG_DIR` 一变(测试逐用例换临时目录)即现读,
32
+ /** **已退休键**(#762 过渡件 + #757 真删):我们自己删过、老配置文件里必然还留着的键的叶子全路径。命中的键跳过校验、
33
+ * 不进取值、也不会被写回(任何一次保存/重置天然抹掉它)。名单**只许写已经删掉的键**——键还在默认值表里
34
+ * 就写进清单=在护栏上开洞,`base-link-core` 收到清单时当场拒。
35
+ * `land.scheduleCli`/`land.memoCli`(#748 定稿)删于本票 #757:两个跨技能出口退回「按包布局推断」,
36
+ * 面板不再显示这两行。退出条件:下一个大版本删掉这张清单。 */
37
+ export const CALORIE_CONFIG_RETIRED = ['land.scheduleCli', 'land.memoCli'];
38
+ /** 写盘那一份用的默认值表:**可改落点**(`db.dir`/`photos.dir`)写成算出来的绝对路径(#746 总口径回灌,本票 #757 落地)。
39
+ *
40
+ * 为什么写绝对路径:面板上「数据目录/照片目录」那两格显示的是从配置文件算出的生效值,而空串的语义是「按默认落点」
41
+ * ——默认落点由 `os.homedir()` 派生,换机器/改用户名就会漂到别处;写死了才是「这一格写的是什么就是什么」。
42
+ * **只读落点保持原样**(`xunji.stateDir` 的空串由落点算式解成 `<数据目录>/xunji`,绝对化=改语义)。
43
+ *
44
+ * 口径的两半:**读**仍认空串(=按默认落点,老文件一份不用动,也不与 #762 的退休键过渡打架);
45
+ * **写**(首次落文件/保存/重置)一律落绝对路径。那两格不自己拼配置目录——`db.dir` 直接用 `configPaths(stem).dataDir`,
46
+ * `photos.dir` 用 `join(dataDir, 'photos')`(今天算照片默认落点只有这一处算法)。 */
47
+ function writableDefaults() {
48
+ const dataDir = configPaths(CALORIE_CONFIG_STEM).dataDir;
49
+ return {
50
+ ...CALORIE_CONFIG_DEFAULTS,
51
+ db: { ...CALORIE_CONFIG_DEFAULTS.db, dir: dataDir },
52
+ photos: { ...CALORIE_CONFIG_DEFAULTS.photos, dir: join(dataDir, 'photos') },
53
+ };
54
+ }
55
+ /** 写盘前把可改落点那两格的**空白值**去掉:空串=按默认落点,而写的时候那个落点要落成绝对路径
56
+ * (见 `writableDefaults`)——去掉这一格,`base-link-core` 的 `fillDefaults` 自会补上写盘用的默认值。
57
+ * 组里一个子项都不剩时连组一起去掉(空组写出去解析不回来)。 */
58
+ function withoutBlankEditableDirs(values) {
59
+ let out = { ...values };
60
+ for (const groupKey of ['db', 'photos']) {
61
+ const group = out[groupKey];
62
+ if (typeof group !== 'object' || group === null || Array.isArray(group))
63
+ continue;
64
+ const dir = group['dir'];
65
+ if (typeof dir !== 'string' || dir !== '')
66
+ continue;
67
+ const next = { ...group };
68
+ delete next['dir'];
69
+ if (Object.keys(next).length === 0) {
70
+ const { [groupKey]: _dropped, ...rest } = out;
71
+ out = rest;
72
+ }
73
+ else {
74
+ out = { ...out, [groupKey]: next };
75
+ }
76
+ }
77
+ return out;
78
+ }
79
+ /** 每进程按**配置目录**记一份:家目录一变(测试逐用例换临时家目录)即现读,
30
80
  * 同一目录里不反复读盘(「保存即生效」由 `saveCalorieConfig`/`resetCalorieConfig` 清记忆保证,
31
81
  * 不靠长连接;记忆位不绑「进程」而绑「配置目录」是为了不让换目录后的读落到上一份的缓存上)。 */
32
82
  let memo = null;
33
- /** 读一份配置(文件不存在即按默认值落一份并把配置目录/数据目录建出来)。 */
83
+ /** 读一份配置(文件不存在即按默认值落一份并把配置目录/数据目录建出来)。
84
+ *
85
+ * 读回来的 `values` 是**文件里那份 ⊕ 默认值**(文件里写空串则仍是空串=按默认落点);
86
+ * 首次落文件时写下去的是 `writableDefaults()`(可改落点=绝对路径)。 */
34
87
  export function loadCalorieConfig() {
35
88
  const file = configPaths(CALORIE_CONFIG_STEM).configFile;
36
89
  if (memo === null || memo.file !== file) {
37
- const loaded = loadConfig(CALORIE_CONFIG_STEM, CALORIE_CONFIG_DEFAULTS);
90
+ const loaded = loadConfig(CALORIE_CONFIG_STEM, writableDefaults(), CALORIE_CONFIG_RETIRED);
38
91
  // base-link-core 读回来时已经过了「键齐 + 类型对」两道校验(不认识的键、类型不符一律抛),
39
92
  // 故这一处从宽松记录到形状记录的转换是有依据的投影,不是猜测。
40
93
  memo = {
@@ -51,13 +104,13 @@ export function loadCalorieConfig() {
51
104
  }
52
105
  /** 写一份配置(写出去的是完整一份:没给的项按默认值补齐)。写完清记忆,同进程后续读也现取。 */
53
106
  export function saveCalorieConfig(values) {
54
- const r = saveConfig(CALORIE_CONFIG_STEM, CALORIE_CONFIG_DEFAULTS, values);
107
+ const r = saveConfig(CALORIE_CONFIG_STEM, writableDefaults(), withoutBlankEditableDirs(values), CALORIE_CONFIG_RETIRED);
55
108
  memo = null;
56
109
  return r;
57
110
  }
58
111
  /** 重置为默认(先另存 `<配置目录>/calorie.yaml.bak`)。 */
59
112
  export function resetCalorieConfig() {
60
- const r = resetConfig(CALORIE_CONFIG_STEM, CALORIE_CONFIG_DEFAULTS);
113
+ const r = resetConfig(CALORIE_CONFIG_STEM, writableDefaults(), CALORIE_CONFIG_RETIRED);
61
114
  memo = null;
62
115
  return r;
63
116
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGlF,qCAAqC;AACrC,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAkB,CAAC;AAEtD;;;;wDAIwD;AACxD,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE;IACxC,IAAI,EAAE,EAAE,GAAG,EAAE,cAAc,EAAE;IAC7B,MAAM,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;IACjC,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE;IACvE,IAAI,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE;CAC3E,CAAC;AAaF;;oDAEoD;AACpD,IAAI,IAAI,GAAyD,IAAI,CAAC;AAEtE,0CAA0C;AAC1C,MAAM,UAAU,iBAAiB;IAC/B,MAAM,IAAI,GAAG,WAAW,CAAC,mBAAmB,CAAC,CAAC,UAAU,CAAC;IACzD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,UAAU,CAAC,mBAAmB,EAAE,uBAAuB,CAAC,CAAC;QACxE,wDAAwD;QACxD,iCAAiC;QACjC,IAAI,GAAG;YACL,IAAI;YACJ,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,MAAM,EAAE,MAAM,CAAC,MAAwC;aACxD;SACF,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC;AACrB,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,MAAM,CAAC,GAAG,UAAU,CAAC,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,CAAC,CAAC;IAC3E,IAAI,GAAG,IAAI,CAAC;IACZ,OAAO,CAAC,CAAC;AACX,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,kBAAkB;IAChC,MAAM,CAAC,GAAG,WAAW,CAAC,mBAAmB,EAAE,uBAAuB,CAAC,CAAC;IACpE,IAAI,GAAG,IAAI,CAAC;IACZ,OAAO,CAAC,CAAC;AACX,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  /** T6 #25 · 训练计划动作名审计(老家 audit_plan_names.py 同契约)。
2
2
  * 输出统一 { status, data, message } 三段式;exit 映射由 CLI 负责。
3
- * #606:库面与动作名校验搬进训记模块,本件改走它的能力门——**默认读包内预置快照**(不再隐式读
4
- * 各机 `~/.minimax` 那份:那句「这台机器放没放库」的口径与编辑器库面分叉,见 #593 R3)。
3
+ * #606:库面与动作名校验搬进训记模块,本件改走它的能力门——**默认读包内预置快照**
4
+ * (#757 起老机器路径已退场:`XUNJI_CATALOG` 只剩 `preset` 一项,不再有第二套口径)。
5
5
  */
6
6
  import type { DatabaseSync } from 'node:sqlite';
7
7
  export type AuditStatus = 'ok' | 'warn' | 'fail';
@@ -1 +1 @@
1
- {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAsBA,0BAA0B;AAC1B,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;CACvC;AAED,kEAAkE;AAClE,eAAO,MAAM,kBAAkB,EAAG,EAAW,CAAC;AAiB9C,oDAAoD;AACpD,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,GACxH;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,IAAI,UAAU,CAYtD;AA6OD,sDAAsD;AACtD,wBAAgB,wBAAwB,IAAI,mBAAmB,CAqN9D"}
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAwBA,0BAA0B;AAC1B,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;CACvC;AAED,kEAAkE;AAClE,eAAO,MAAM,kBAAkB,EAAG,EAAW,CAAC;AAc9C,oDAAoD;AACpD,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,GACxH;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,IAAI,UAAU,CAYtD;AA6OD,sDAAsD;AACtD,wBAAgB,wBAAwB,IAAI,mBAAmB,CAwN9D"}
package/dist/health.js CHANGED
@@ -19,11 +19,11 @@ import { fileURLToPath } from 'node:url';
19
19
  import { DatabaseSync } from 'node:sqlite';
20
20
  import { configPaths } from 'base-link-core';
21
21
  import { CALORIE_CONFIG_DEFAULTS, CALORIE_CONFIG_STEM } from './config.js';
22
+ import { dbDirOf, gifsDirOf, htmlDirOf, photosDirOf, stateDirOf } from './paths.js';
23
+ import { XUNJI_CATALOG } from './xunji/index.js';
22
24
  /** 库表数门槛:`src/schema.ts` 那 11 张表齐了才算正常(少了报黄:还能用,但缺功能的表要重建或迁移)。 */
23
25
  export const DB_TABLE_THRESHOLD = 11;
24
26
  const SKILL = 'calorie';
25
- /** 训记三份状态文件住在同一个目录。 */
26
- const XUNJI_STATE_DIR = join(homedir(), '.mavis');
27
27
  /** 包根:`dist/health.js` 上一级。 */
28
28
  function packageRoot() {
29
29
  return join(dirname(fileURLToPath(import.meta.url)), '..');
@@ -296,10 +296,10 @@ export function buildCalorieHealthReport() {
296
296
  const read = readCalorieConfigReadOnly();
297
297
  const values = read.kind === 'ok' ? read.values : projectOnDefaults({});
298
298
  const present = read.kind === 'ok' ? read.present : new Set();
299
- // ① 配置文件本身:能不能解析;它落在默认位置还是被 ILIFE_CONFIG_DIR 指到别处(只陈述,不评价)。
299
+ // ① 配置文件本身:能不能解析;它落在默认位置还是别处(只陈述,不评价)。
300
300
  const defaultConfigFile = join(homedir(), '.ilife', CALORIE_CONFIG_STEM + '.yaml');
301
301
  const relocated = paths.configFile !== defaultConfigFile;
302
- const where = relocated ? '位置被 ILIFE_CONFIG_DIR 指到这里' : '默认位置';
302
+ const where = relocated ? '位置与默认不同(见配置文件落点)' : '默认位置';
303
303
  if (read.kind === 'bad') {
304
304
  items.push({
305
305
  id: 'config.file', title: '配置文件', status: 'red',
@@ -324,9 +324,9 @@ export function buildCalorieHealthReport() {
324
324
  source: '配置文件',
325
325
  });
326
326
  }
327
- // ② 数据目录:在不在、能不能写。
327
+ // ② 数据目录:在不在、能不能写(算式唯一定义地=`src/paths.ts` 的 `dbDirOf`,本件只给只读值)。
328
328
  const dbDirConfigured = textOf(readValue(values, 'db', 'dir'));
329
- const dataDir = dbDirConfigured !== '' ? dbDirConfigured : paths.dataDir;
329
+ const dataDir = dbDirOf(paths.dataDir, dbDirConfigured);
330
330
  const dataDirSource = sourceOf(present, 'db.dir');
331
331
  const dataDirVerdict = dirVerdict(dataDir);
332
332
  items.push({
@@ -382,9 +382,9 @@ export function buildCalorieHealthReport() {
382
382
  });
383
383
  }
384
384
  }
385
- // ④ 产物目录:在不在、能不能写(还没建=绿:交付页面时才落这里,那时自动建)。
385
+ // ④ 产物目录:在不在、能不能写(还没建=绿:交付页面时才落这里,那时自动建;算式=`htmlDirOf`)。
386
386
  const htmlDirName = textOf(readValue(values, 'html', 'dir'));
387
- const htmlDir = join(dataDir, htmlDirName !== '' ? htmlDirName : String(CALORIE_CONFIG_DEFAULTS.html.dir));
387
+ const htmlDir = htmlDirOf(dataDir, htmlDirName);
388
388
  const htmlVerdict = dirVerdict(htmlDir);
389
389
  const htmlSource = sourceOf(present, 'html.dir');
390
390
  items.push({
@@ -395,7 +395,7 @@ export function buildCalorieHealthReport() {
395
395
  : htmlVerdict.writable
396
396
  ? '在且能写:' + p(htmlDir) + '。'
397
397
  : '在,但写不进去:' + p(htmlDir) + '(交付会转成内联回执,不再落盘)。',
398
- action: !htmlVerdict.exists || htmlVerdict.writable ? '' : '去掉这个目录的只读属性,或把「HTML 产物目录名」改到别处。',
398
+ action: !htmlVerdict.exists || htmlVerdict.writable ? '' : '去掉这个目录的只读属性,或编辑配置文件改「HTML 产物目录名」。',
399
399
  source: htmlSource,
400
400
  });
401
401
  // ⑤ 这个值从哪来:只陈述数据目录与库文件名的来源,不判好坏(六家通用最后一条)。
@@ -405,33 +405,38 @@ export function buildCalorieHealthReport() {
405
405
  + (read.kind === 'missing' ? '(配置文件还不存在,落点全按默认值)。' : '。'),
406
406
  action: '', source: dataDirSource,
407
407
  });
408
- // ⑥ 照片目录(卡路里特有):没配=黄(读照片不报错,出 GIF 与写照片会被拦下)。
409
- const photosDir = textOf(readValue(values, 'photos', 'dir'));
410
- const photosVerdict = photosDir === '' ? { exists: false, writable: false, reason: '' } : dirVerdict(photosDir);
408
+ // ⑥ 照片目录(卡路里特有,#757 新口径):空串=按默认落点 `<数据目录>/photos`,「未配置」状态消失。
409
+ // 还没建=绿(首次记身材照时自动建);在但写不进去/同名文件占位=红。算式=`photosDirOf`。
410
+ const photosConfigured = textOf(readValue(values, 'photos', 'dir'));
411
+ const photosDir = photosDirOf(dataDir, photosConfigured);
412
+ const photosVerdict = dirVerdict(photosDir);
413
+ const photosBlocked = !photosVerdict.exists
414
+ ? photosVerdict.reason !== ''
415
+ : !photosVerdict.writable;
411
416
  items.push({
412
417
  id: 'photos.dir', title: '照片目录',
413
- status: photosDir === '' ? 'yellow' : photosVerdict.exists && photosVerdict.writable ? 'green' : 'red',
414
- message: photosDir === ''
415
- ? '还没配:读照片不报错,出 GIF 与写照片会被拦下。'
416
- : photosVerdict.exists
417
- ? (photosVerdict.writable ? '在且能写:' + p(photosDir) + '。' : '在,但写不进去:' + p(photosDir) + '')
418
- : '配了但目录不在:' + p(photosDir) + '。',
419
- action: photosDir === ''
420
- ? '要用身材照就在配置页填这个目录(绝对路径)。'
421
- : photosVerdict.exists && photosVerdict.writable ? '' : '建出这个目录(或去掉只读),或换一个位置。',
422
- source: photosDir === '' ? '默认值' : '配置文件',
418
+ status: photosBlocked ? 'red' : 'green',
419
+ message: !photosVerdict.exists
420
+ ? (photosVerdict.reason !== ''
421
+ ? '同名文件占了它的位置:' + p(photosDir) + '(' + photosVerdict.reason + ')。'
422
+ : '还没建:' + p(photosDir) + '(首次记身材照时自动建)。')
423
+ : photosVerdict.writable
424
+ ? '在且能写:' + p(photosDir) + ''
425
+ : '在,但写不进去:' + p(photosDir) + '(' + photosVerdict.reason + ')。',
426
+ action: photosBlocked
427
+ ? '建出这个目录(或去掉只读),或在配置页把「照片目录」换一个位置。'
428
+ : '',
429
+ source: sourceOf(present, 'photos.dir'),
423
430
  });
424
- // ⑦ 照片 GIF 子目录:不在=黄(照片目录没配时也跟着黄)。
431
+ // ⑦ 照片 GIF 子目录:不在=黄(合成 GIF 时自动建;算式=`gifsDirOf`)。
425
432
  const gifsSub = textOf(readValue(values, 'photos', 'gifs'));
426
- const gifsDir = photosDir === '' ? '' : join(photosDir, gifsSub !== '' ? gifsSub : String(CALORIE_CONFIG_DEFAULTS.photos.gifs));
427
- const gifsExists = gifsDir !== '' && existsSync(gifsDir);
433
+ const gifsDir = gifsDirOf(photosDir, gifsSub);
434
+ const gifsExists = existsSync(gifsDir);
428
435
  items.push({
429
436
  id: 'photos.gifs', title: '照片 GIF 子目录',
430
- status: photosDir === '' || !gifsExists ? 'yellow' : 'green',
431
- message: photosDir === ''
432
- ? '照片目录还没配,这一项跟着空着。'
433
- : gifsExists ? '在:' + p(gifsDir) + '。' : '还没建:' + p(gifsDir) + '(合成 GIF 时才落这里)。',
434
- action: photosDir === '' ? '先配「照片目录」。' : gifsExists ? '' : '第一次合成 GIF 时会自动建;想提前建也行。',
437
+ status: gifsExists ? 'green' : 'yellow',
438
+ message: gifsExists ? '在:' + p(gifsDir) + '。' : '还没建:' + p(gifsDir) + '(合成 GIF 时才落这里)。',
439
+ action: gifsExists ? '' : '第一次合成 GIF 时会自动建;想提前建也行。',
435
440
  source: sourceOf(present, 'photos.gifs'),
436
441
  });
437
442
  // ⑧ 训记 KEY:只查「配了没有」,**绝不显示值**。
@@ -452,9 +457,9 @@ export function buildCalorieHealthReport() {
452
457
  message: xunjiCliExists ? '在:' + p(xunjiCli) + '。' : '不在:' + p(xunjiCli) + '。',
453
458
  action: xunjiCliExists ? '' : '技能包没构建完整:重装这个技能包,或跑一次它的构建。',
454
459
  });
455
- // ⑩ 训记三份状态文件目录:`~/.mavis/` 能不能写(不可写=黄)。
460
+ // ⑩ 训记三份状态文件目录:`<数据目录>/xunji` 能不能写(不可写=黄;算式=`stateDirOf`,#757 起不再 `~/.mavis`)。
456
461
  const stateDirConfigured = textOf(readValue(values, 'xunji', 'stateDir'));
457
- const stateDir = stateDirConfigured !== '' ? stateDirConfigured : XUNJI_STATE_DIR;
462
+ const stateDir = stateDirOf(dataDir, stateDirConfigured);
458
463
  const stateVerdict = dirVerdict(stateDir);
459
464
  items.push({
460
465
  id: 'xunji.stateDir', title: '训记状态文件目录',
@@ -462,15 +467,13 @@ export function buildCalorieHealthReport() {
462
467
  message: !stateVerdict.exists
463
468
  ? '还没建:' + p(stateDir) + '(限频与同步游标要记这里,第一次用时自动建)。'
464
469
  : stateVerdict.writable ? '在且能写:' + p(stateDir) + '。' : '在,但写不进去:' + p(stateDir) + '。',
465
- action: !stateVerdict.exists || stateVerdict.writable ? '' : '去掉只读,或把「训记状态文件目录」改到别处。',
466
- source: stateDirConfigured !== '' ? '配置文件' : '默认值',
470
+ action: !stateVerdict.exists || stateVerdict.writable ? '' : '去掉只读,或编辑配置文件改「训记状态文件目录」。',
471
+ source: sourceOf(present, 'xunji.stateDir'),
467
472
  });
468
- // ⑪ 跨技能出口(作息/备忘 CLI):不在=黄(只在用「落地训练」时才是红)。
473
+ // ⑪ 跨技能出口(作息/备忘 CLI):不在=黄(只在用「落地训练」时才是红;#757 起删键,出口按包布局推断)。
469
474
  const siblingRoot = join(packageRoot(), '..');
470
- const scheduleConfigured = textOf(readValue(values, 'land', 'scheduleCli'));
471
- const memoConfigured = textOf(readValue(values, 'land', 'memoCli'));
472
- const scheduleCli = scheduleConfigured !== '' ? scheduleConfigured : join(siblingRoot, 'skill-schedule', 'dist', 'cli', 'cmd_read.js');
473
- const memoCli = memoConfigured !== '' ? memoConfigured : join(siblingRoot, 'skill-memo-ilife', 'dist', 'cli', 'cmd_read.js');
475
+ const scheduleCli = join(siblingRoot, 'skill-schedule', 'dist', 'cli', 'cmd_read.js');
476
+ const memoCli = join(siblingRoot, 'skill-memo-ilife', 'dist', 'cli', 'cmd_read.js');
474
477
  const missingLand = [];
475
478
  if (!existsSync(scheduleCli))
476
479
  missingLand.push('作息 ' + p(scheduleCli));
@@ -483,17 +486,17 @@ export function buildCalorieHealthReport() {
483
486
  ? '两个出口都在。'
484
487
  : '不在:' + missingLand.join(';') + '。',
485
488
  action: missingLand.length === 0 ? '' : '只有「落地训练」会用到这两个出口;要用就先装上(或构建)那两家技能包。',
486
- source: sourceOf(present, 'land.scheduleCli') === '配置文件' || sourceOf(present, 'land.memoCli') === '配置文件' ? '配置文件' : '默认值',
489
+ source: '默认值',
487
490
  });
488
- // ⑫ 训记动作库包内预置:包内数据件在不在(不在=黄)。
491
+ // ⑫ 训记动作库包内预置:包内数据件在不在(不在=黄;路径唯一定义地=`XUNJI_CATALOG.preset`)。
489
492
  const catalogConfigured = textOf(readValue(values, 'xunji', 'catalog'));
490
- const catalog = catalogConfigured !== '' ? catalogConfigured : join(packageRoot(), 'src', 'xunji', 'data', '训记官方动作.json');
493
+ const catalog = catalogConfigured !== '' ? catalogConfigured : XUNJI_CATALOG.preset;
491
494
  const catalogExists = existsSync(catalog);
492
495
  items.push({
493
496
  id: 'xunji.catalog', title: '训记动作库包内预置',
494
497
  status: catalogExists ? 'green' : 'yellow',
495
498
  message: catalogExists ? '在:' + p(catalog) + '。' : '不在:' + p(catalog) + '(动作名判不了)。',
496
- action: catalogExists ? '' : '技能包装得不完整,或配置里指到了不存在的路径;重装技能包,或改回包内预置那份。',
499
+ action: catalogExists ? '' : '技能包装得不完整,或配置里指到了不存在的路径;重装技能包,或编辑配置文件改回包内预置那份。',
497
500
  source: sourceOf(present, 'xunji.catalog'),
498
501
  });
499
502
  return { skill: SKILL, configPath: p(paths.configFile), dataDir: p(dataDir), items };