skill-home 0.1.0 → 0.2.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 (56) hide show
  1. package/SKILL.md +167 -0
  2. package/dist/cli/cmd_read.js +150 -31
  3. package/dist/cli/cmd_read.js.map +1 -1
  4. package/dist/cli/config.d.ts +16 -0
  5. package/dist/cli/config.d.ts.map +1 -0
  6. package/dist/cli/config.js +84 -0
  7. package/dist/cli/config.js.map +1 -0
  8. package/dist/cli/health.d.ts +7 -0
  9. package/dist/cli/health.d.ts.map +1 -0
  10. package/dist/cli/health.js +32 -0
  11. package/dist/cli/health.js.map +1 -0
  12. package/dist/config.d.ts +44 -0
  13. package/dist/config.d.ts.map +1 -0
  14. package/dist/config.js +65 -0
  15. package/dist/config.js.map +1 -0
  16. package/dist/fetch/archive.d.ts +13 -0
  17. package/dist/fetch/archive.d.ts.map +1 -0
  18. package/dist/fetch/archive.js +115 -0
  19. package/dist/fetch/archive.js.map +1 -0
  20. package/dist/fetch/backup.d.ts +77 -0
  21. package/dist/fetch/backup.d.ts.map +1 -0
  22. package/dist/fetch/backup.js +292 -0
  23. package/dist/fetch/backup.js.map +1 -0
  24. package/dist/fetch/index.d.ts +2 -1
  25. package/dist/fetch/index.d.ts.map +1 -1
  26. package/dist/fetch/index.js +3 -1
  27. package/dist/fetch/index.js.map +1 -1
  28. package/dist/fetch/paths.d.ts +5 -3
  29. package/dist/fetch/paths.d.ts.map +1 -1
  30. package/dist/fetch/paths.js +19 -24
  31. package/dist/fetch/paths.js.map +1 -1
  32. package/dist/health.d.ts +41 -0
  33. package/dist/health.d.ts.map +1 -0
  34. package/dist/health.js +507 -0
  35. package/dist/health.js.map +1 -0
  36. package/dist/help/helpAssets.d.ts +38 -0
  37. package/dist/help/helpAssets.d.ts.map +1 -0
  38. package/dist/help/helpAssets.js +1122 -0
  39. package/dist/help/helpAssets.js.map +1 -0
  40. package/dist/help/helpFile.d.ts +62 -0
  41. package/dist/help/helpFile.d.ts.map +1 -0
  42. package/dist/help/helpFile.js +219 -0
  43. package/dist/help/helpFile.js.map +1 -0
  44. package/dist/help/index.d.ts +3 -0
  45. package/dist/help/index.d.ts.map +1 -1
  46. package/dist/help/index.js +6 -0
  47. package/dist/help/index.js.map +1 -1
  48. package/dist/help/manifest.d.ts +11 -0
  49. package/dist/help/manifest.d.ts.map +1 -0
  50. package/dist/help/manifest.js +38 -0
  51. package/dist/help/manifest.js.map +1 -0
  52. package/dist/help/output.d.ts +12 -0
  53. package/dist/help/output.d.ts.map +1 -0
  54. package/dist/help/output.js +65 -0
  55. package/dist/help/output.js.map +1 -0
  56. package/package.json +9 -4
@@ -0,0 +1,115 @@
1
+ // 备份容器:条目的打包与解包(ZIP 的最小可用子集,只用 node:zlib,不引外部依赖)。
2
+ //
3
+ // 为什么自己写而不引 zip 库:本仓规矩是不加外部依赖(见地图 #671 Notes 的冻结口径);
4
+ // 而备份产物要**用户能直接双击打开 / 拿系统解压**,故格式必须是 ZIP 而不是 tar.gz。
5
+ // 只做写与读各自必需的部分——不做流式、不做 ZIP64、不做加密、不收目录树(调用方给扁平条目表)。
6
+ // 读侧只认「无数据描述符(bit 3 = 0)+ stored/deflate」两条;其余一律带原因抛错,不静默跳过。
7
+ import { deflateRawSync, inflateRawSync } from 'node:zlib';
8
+ const SIG_LOCAL = 0x04034b50;
9
+ const SIG_CENTRAL = 0x02014b50;
10
+ const SIG_EOCD = 0x06054b50;
11
+ // msdos 时间:秒只有 2 秒精度;备份时间戳另由文件名承载,这里给常数,不谎报时间。
12
+ const DOS_TIME = 0;
13
+ const DOS_DATE = 0x2821; // 2000-01-01
14
+ function u16(v) {
15
+ const b = Buffer.allocUnsafe(2);
16
+ b.writeUInt16LE(v, 0);
17
+ return b;
18
+ }
19
+ function u32(v) {
20
+ const b = Buffer.allocUnsafe(4);
21
+ b.writeUInt32LE(v >>> 0, 0);
22
+ return b;
23
+ }
24
+ const CRC_TABLE = (() => {
25
+ const t = new Int32Array(256);
26
+ for (let n = 0; n < 256; n++) {
27
+ let c = n;
28
+ for (let k = 0; k < 8; k++)
29
+ c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
30
+ t[n] = c;
31
+ }
32
+ return t;
33
+ })();
34
+ function crc32(buf) {
35
+ let c = -1;
36
+ for (let i = 0; i < buf.length; i++)
37
+ c = CRC_TABLE[(c ^ buf[i]) & 0xff] ^ (c >>> 8);
38
+ return (c ^ -1) >>> 0;
39
+ }
40
+ /** 打包:条目按给定顺序写入,逐个 deflate(压不小就退回 stored,两种都合法)。 */
41
+ export function zipWrite(entries) {
42
+ const locals = [];
43
+ const centrals = [];
44
+ let offset = 0;
45
+ for (const e of entries) {
46
+ const name = Buffer.from(e.name, 'utf8');
47
+ const deflated = deflateRawSync(e.data, { level: 9 });
48
+ const stored = deflated.length >= e.data.length;
49
+ const body = stored ? e.data : deflated;
50
+ const method = stored ? 0 : 8;
51
+ const crc = crc32(e.data);
52
+ const local = Buffer.concat([
53
+ u32(SIG_LOCAL), u16(20), u16(0), u16(method), u16(DOS_TIME), u16(DOS_DATE),
54
+ u32(crc), u32(body.length), u32(e.data.length), u16(name.length), u16(0), name, body,
55
+ ]);
56
+ locals.push(local);
57
+ centrals.push(Buffer.concat([
58
+ u32(SIG_CENTRAL), u16(20), u16(20), u16(0), u16(method), u16(DOS_TIME), u16(DOS_DATE),
59
+ u32(crc), u32(body.length), u32(e.data.length), u16(name.length),
60
+ u16(0), u16(0), u16(0), u16(0), u32(0), u32(offset), name,
61
+ ]));
62
+ offset += local.length;
63
+ }
64
+ const central = Buffer.concat(centrals);
65
+ const eocd = Buffer.concat([
66
+ u32(SIG_EOCD), u16(0), u16(0), u16(entries.length), u16(entries.length),
67
+ u32(central.length), u32(offset), u16(0),
68
+ ]);
69
+ return Buffer.concat([...locals, central, eocd]);
70
+ }
71
+ /** 解包:从 EOCD 走中央目录取每个条目的偏移,再读本地头与数据。 */
72
+ export function zipRead(buf) {
73
+ const eocd = buf.lastIndexOf(u32(SIG_EOCD));
74
+ if (eocd < 0)
75
+ throw new Error('不是 ZIP:未找到中央目录结尾记录');
76
+ const count = buf.readUInt16LE(eocd + 10);
77
+ let p = buf.readUInt32LE(eocd + 16);
78
+ const out = [];
79
+ for (let i = 0; i < count; i++) {
80
+ if (p + 46 > buf.length || buf.readUInt32LE(p) !== SIG_CENTRAL)
81
+ throw new Error('ZIP 中央目录第 ' + (i + 1) + ' 条损坏');
82
+ const method = buf.readUInt16LE(p + 10);
83
+ const crc = buf.readUInt32LE(p + 16);
84
+ const compSize = buf.readUInt32LE(p + 20);
85
+ const rawSize = buf.readUInt32LE(p + 24);
86
+ const nameLen = buf.readUInt16LE(p + 28);
87
+ const extraLen = buf.readUInt16LE(p + 30);
88
+ const commentLen = buf.readUInt16LE(p + 32);
89
+ const localOff = buf.readUInt32LE(p + 42);
90
+ const name = buf.toString('utf8', p + 46, p + 46 + nameLen);
91
+ if (localOff + 30 > buf.length || buf.readUInt32LE(localOff) !== SIG_LOCAL)
92
+ throw new Error('ZIP 条目缺少本地头:' + name);
93
+ if ((buf.readUInt16LE(localOff + 6) & 0x08) !== 0)
94
+ throw new Error('ZIP 条目用了数据描述符(不支持):' + name);
95
+ const lNameLen = buf.readUInt16LE(localOff + 26);
96
+ const lExtraLen = buf.readUInt16LE(localOff + 28);
97
+ const start = localOff + 30 + lNameLen + lExtraLen;
98
+ const body = buf.subarray(start, start + compSize);
99
+ let data;
100
+ if (method === 0)
101
+ data = Buffer.from(body);
102
+ else if (method === 8)
103
+ data = inflateRawSync(body);
104
+ else
105
+ throw new Error('ZIP 条目压缩方式不支持(method=' + method + '):' + name);
106
+ if (data.length !== rawSize)
107
+ throw new Error('ZIP 条目长度不符:' + name);
108
+ if (crc32(data) !== crc)
109
+ throw new Error('ZIP 条目校验和不符(备份可能损坏):' + name);
110
+ out.push({ name, data });
111
+ p += 46 + nameLen + extraLen + commentLen;
112
+ }
113
+ return out;
114
+ }
115
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/fetch/archive.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,EAAE;AACF,qDAAqD;AACrD,qDAAqD;AACrD,qDAAqD;AACrD,6DAA6D;AAC7D,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAY3D,MAAM,SAAS,GAAG,UAAU,CAAC;AAC7B,MAAM,WAAW,GAAG,UAAU,CAAC;AAC/B,MAAM,QAAQ,GAAG,UAAU,CAAC;AAC5B,+CAA+C;AAC/C,MAAM,QAAQ,GAAG,CAAC,CAAC;AACnB,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,aAAa;AAEtC,SAAS,GAAG,CAAC,CAAS;IACpB,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACtB,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,GAAG,CAAC,CAAS;IACpB,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5B,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE;IACtB,MAAM,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACX,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC,CAAC,EAAE,CAAC;AAEL,SAAS,KAAK,CAAC,GAAW;IACxB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACpF,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,QAAQ,CAAC,OAAmB;IAC1C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QAChD,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;YAC1B,GAAG,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC;YAC1E,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI;SACrF,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnB,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;YAC1B,GAAG,CAAC,WAAW,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC;YACrF,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;YAChE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI;SAC1D,CAAC,CAAC,CAAC;QACJ,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC;IACzB,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC;QACzB,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;QACvE,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;KACzC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC5C,IAAI,IAAI,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,GAAG,GAAG,CAAC,YAAY,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IACpC,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QACjH,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACxC,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACrC,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC1C,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC;QAC5D,IAAI,QAAQ,GAAG,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;QACnH,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;QACjG,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,QAAQ,GAAG,EAAE,GAAG,QAAQ,GAAG,SAAS,CAAC;QACnD,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,CAAC,CAAC;QACnD,IAAI,IAAY,CAAC;QACjB,IAAI,MAAM,KAAK,CAAC;YAAE,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aACtC,IAAI,MAAM,KAAK,CAAC;YAAE,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;;YAC9C,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;QACrE,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;QACnE,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;QACxE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACzB,CAAC,IAAI,EAAE,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC5C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,77 @@
1
+ /** 备份目录名的默认值:=改造前的代码常量 `BACKUP_DIR_NAME`(配置项 `backup.dir` 空串即用它)。 */
2
+ export declare const DEFAULT_BACKUP_DIR_NAME = "backups";
3
+ export interface BackupRow {
4
+ file: string;
5
+ size: number;
6
+ created_at: string;
7
+ days_ago: number;
8
+ }
9
+ export interface BackupResult {
10
+ status: 'ok';
11
+ keep_n: number;
12
+ file: string;
13
+ size: number;
14
+ created_at: string;
15
+ items: number;
16
+ history: BackupRow[];
17
+ days_since_last: number | null;
18
+ }
19
+ export interface BackupListResult {
20
+ status: 'ok';
21
+ dir: string;
22
+ keep_n: number;
23
+ count: number;
24
+ history: BackupRow[];
25
+ days_since_last: number | null;
26
+ }
27
+ export interface RestoreResult {
28
+ status: 'ok';
29
+ restored_from: string;
30
+ safety_backup: string;
31
+ before_items: number;
32
+ items_total: number;
33
+ message: string;
34
+ }
35
+ /** 预告(`dryRun`)结果:只报「这份备份装了多少件、当前库多少件」,不落盘。 */
36
+ export interface RestorePreview {
37
+ status: 'ok';
38
+ dry_run: true;
39
+ restored_from: string;
40
+ items_in_backup: number | null;
41
+ items_current: number;
42
+ message: string;
43
+ }
44
+ export type RestoreOutcome = RestoreResult | RestorePreview;
45
+ export interface ExportResult {
46
+ status: 'ok';
47
+ file: string;
48
+ format: 'json' | 'csv';
49
+ size: number;
50
+ rows: number;
51
+ }
52
+ /** 备份目录名:配置 `backup.dir`,空串=默认(`backups`)。 */
53
+ export declare function backupDirName(): string;
54
+ /** 备份目录(库目录下 `backups/`)——设置页「备份目录」那一项的取值口;只算路径,不建目录。 */
55
+ export declare function resolveBackupDir(): string;
56
+ /** 备份:库快照(含 WAL)+ 清单打包成 zip,保留 N 份、删最旧;返回回执载荷。 */
57
+ export declare function createBackup(opts?: {
58
+ keepN?: number;
59
+ }): BackupResult;
60
+ /** 备份历史:现读盘,不缓存(面板/回执要的是当前真状态)。 */
61
+ export declare function listBackups(opts?: {
62
+ keepN?: number;
63
+ }): BackupListResult;
64
+ /**
65
+ * 导入恢复:拿备份里的 `home.db` 覆盖当前库;覆盖前先自备份一次(数据安全优先于便捷)。
66
+ * `dryRun`=预告支(SM8-4「文件选择(预告式)→ 校验 → 冲突预览 → 确认导入」的中间那一步):只校验+报数,不落盘。
67
+ */
68
+ export declare function restoreBackup(file: string, opts?: {
69
+ keepN?: number;
70
+ dryRun?: boolean;
71
+ }): RestoreOutcome;
72
+ /** 导出:JSON=全表可迁移;CSV=物品 ⋈ 位置便携(列与老家 ops.py:620 逐列相同)。 */
73
+ export declare function exportData(opts?: {
74
+ format?: string;
75
+ output?: string;
76
+ }): ExportResult;
77
+ //# sourceMappingURL=backup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../src/fetch/backup.ts"],"names":[],"mappings":"AAkBA,qEAAqE;AACrE,eAAO,MAAM,uBAAuB,YAAY,CAAC;AAKjD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,IAAI,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,IAAI,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,IAAI,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,cAAc,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,IAAI,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AA0BD,8CAA8C;AAC9C,wBAAgB,aAAa,IAAI,MAAM,CAGtC;AAED,yDAAyD;AACzD,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AA8BD,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,YAAY,CA0CxE;AAED,mCAAmC;AACnC,wBAAgB,WAAW,CAAC,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,gBAAgB,CAK3E;AAmBD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,cAAc,CAmD3G;AAaD,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,YAAY,CA6CxF"}
@@ -0,0 +1,292 @@
1
+ // 备份导出/导入恢复:HELP SM8-3「备份与导出(数据资产)」+ SM8-4「导入与恢复(迁移)」的落点。
2
+ //
3
+ // 与老家的关系(`docs/plugins/plugin-home-ilife/t707-备份取舍.md` 有完整对账):形状照 `ops.py` 那套
4
+ // (`backups/` 同库目录、`home_backup_<时间戳>.zip`、保留 N 份、覆盖前二次备份),但有三处**有意不同**:
5
+ // 1. **不做照片**:本包 src 零读 `HOME_PHOTOS_DIR`(`docs/env.md:12` 已登记的实况),故 zip 只装库 + 清单;
6
+ // 2. **快照用 `VACUUM INTO`**:老家直接 `zf.write(DB_PATH)`,而本包库开 WAL(`db.ts:66`),
7
+ // 照抄会把未落主库的 WAL 内容漏掉——`VACUUM INTO` 出的是包含 WAL 的一致快照;
8
+ // 3. **不做 JSON 导入**:SM8-4 的「冲突预览 → 跳过/合并/覆盖」是老家的物品级合并,本票只做
9
+ // **整库恢复**(`import` 收 `.zip` 备份),物品级合并留作后续票(见该文档「遗留」)。
10
+ // 三条都在票面「留给执行者判断取舍」的范围内,理由与代价写在上面的文档里。
11
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
12
+ import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
13
+ import { closeHomeDb, openHomeDb } from './db.js';
14
+ import { HomeFetchError, HomePolicyError } from './errors.js';
15
+ import { dbFilename, resolveDbDir, resolveDbPath } from './paths.js';
16
+ import { loadHomeConfig } from '../config.js';
17
+ import { zipRead, zipWrite } from './archive.js';
18
+ /** 备份目录名的默认值:=改造前的代码常量 `BACKUP_DIR_NAME`(配置项 `backup.dir` 空串即用它)。 */
19
+ export const DEFAULT_BACKUP_DIR_NAME = 'backups'; // 老家 ops.py:26 同值
20
+ const BACKUP_KEEP_N = 5; // 老家 ops.py:25 同值
21
+ const BACKUP_PREFIX = 'home_backup_';
22
+ const EXPORT_SCHEMA_VERSION = 1;
23
+ function p2(n, w = 2) { return String(n).padStart(w, '0'); }
24
+ function stamp(d = new Date()) {
25
+ return p2(d.getFullYear(), 4) + p2(d.getMonth() + 1) + p2(d.getDate()) + '_' +
26
+ p2(d.getHours()) + p2(d.getMinutes()) + p2(d.getSeconds()) + '_' + p2(d.getMilliseconds(), 3);
27
+ }
28
+ function nowStr(d = new Date()) {
29
+ return p2(d.getFullYear(), 4) + '-' + p2(d.getMonth() + 1) + '-' + p2(d.getDate()) + ' ' +
30
+ p2(d.getHours()) + ':' + p2(d.getMinutes()) + ':' + p2(d.getSeconds());
31
+ }
32
+ /** 从备份文件名解析时间(毫秒与老家六位微秒两种后缀都认)。 */
33
+ function parseStamp(name) {
34
+ const m = /^home_backup_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})(?:_(\d{1,6}))?\.zip$/.exec(name);
35
+ if (!m)
36
+ return null;
37
+ const ms = m[7] ? Number(m[7].slice(0, 3).padEnd(3, '0')) : 0;
38
+ return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4]), Number(m[5]), Number(m[6]), ms);
39
+ }
40
+ function daysBetween(from, to = new Date()) {
41
+ return Math.floor((to.getTime() - from.getTime()) / 86400000);
42
+ }
43
+ /** 备份目录名:配置 `backup.dir`,空串=默认(`backups`)。 */
44
+ export function backupDirName() {
45
+ const dir = loadHomeConfig().values.backup.dir;
46
+ return dir === '' ? DEFAULT_BACKUP_DIR_NAME : dir;
47
+ }
48
+ /** 备份目录(库目录下 `backups/`)——设置页「备份目录」那一项的取值口;只算路径,不建目录。 */
49
+ export function resolveBackupDir() {
50
+ return join(resolveDbDir(), backupDirName());
51
+ }
52
+ function backupFiles(dir) {
53
+ if (!existsSync(dir))
54
+ return [];
55
+ return readdirSync(dir).filter((f) => f.startsWith(BACKUP_PREFIX) && f.endsWith('.zip')).sort();
56
+ }
57
+ function readHistory(dir) {
58
+ return backupFiles(dir).map((f) => {
59
+ const st = statSync(join(dir, f));
60
+ return { file: f, size: st.size, created_at: nowStr(st.mtime), days_ago: daysBetween(st.mtime) };
61
+ });
62
+ }
63
+ function daysSinceLast(dir) {
64
+ const files = backupFiles(dir);
65
+ if (!files.length)
66
+ return null;
67
+ const t = parseStamp(files[files.length - 1]);
68
+ return t ? daysBetween(t) : null;
69
+ }
70
+ function pruneBackups(dir, keepN) {
71
+ const files = backupFiles(dir);
72
+ for (const old of files.slice(0, Math.max(0, files.length - keepN))) {
73
+ try {
74
+ unlinkSync(join(dir, old));
75
+ }
76
+ catch { /* 删不掉不挡本次备份,历史里仍在 */ }
77
+ }
78
+ }
79
+ function esc1(s) { return s.replace(/'/g, "''"); }
80
+ /** 备份:库快照(含 WAL)+ 清单打包成 zip,保留 N 份、删最旧;返回回执载荷。 */
81
+ export function createBackup(opts = {}) {
82
+ const keepN = opts.keepN ?? BACKUP_KEEP_N;
83
+ if (!Number.isInteger(keepN) || keepN <= 0)
84
+ throw new HomePolicyError('POLICY_BAD_INPUT', 'keep_n 须为正整数');
85
+ const dbPath = resolveDbPath();
86
+ if (!existsSync(dbPath))
87
+ throw new HomeFetchError('HOME_DB_MISSING', '库不存在,无可备份:' + dbPath);
88
+ const dir = resolveBackupDir();
89
+ mkdirSync(dir, { recursive: true });
90
+ const base = stamp();
91
+ let target = join(dir, BACKUP_PREFIX + base + '.zip');
92
+ for (let i = 1; existsSync(target); i++)
93
+ target = join(dir, BACKUP_PREFIX + base + '_' + i + '.zip');
94
+ let dbBytes;
95
+ let items = 0;
96
+ const snap = join(dir, '.snapshot-' + base + '.db');
97
+ try {
98
+ const handle = openHomeDb(dbPath);
99
+ try {
100
+ handle.db.exec("VACUUM INTO '" + esc1(snap) + "'");
101
+ items = Number(handle.db.prepare('SELECT count(*) AS c FROM items').get().c);
102
+ }
103
+ finally {
104
+ closeHomeDb(handle);
105
+ }
106
+ dbBytes = readFileSync(snap);
107
+ }
108
+ catch (e) {
109
+ throw new HomeFetchError('HOME_DB_UNREADABLE', '备份快照失败:' + e.message, { cause: e });
110
+ }
111
+ finally {
112
+ try {
113
+ if (existsSync(snap))
114
+ unlinkSync(snap);
115
+ }
116
+ catch { /* 临时件删不掉不挡备份 */ }
117
+ }
118
+ const created = nowStr();
119
+ const dbName = dbFilename();
120
+ const manifest = { schema_version: EXPORT_SCHEMA_VERSION, created_at: created, db_filename: dbName, items };
121
+ const entries = [
122
+ { name: dbName, data: dbBytes },
123
+ { name: 'manifest.json', data: Buffer.from(JSON.stringify(manifest, null, 1) + '\n', 'utf8') },
124
+ ];
125
+ try {
126
+ writeFileSync(target, zipWrite(entries));
127
+ }
128
+ catch (e) {
129
+ throw new HomeFetchError('HOME_DB_UNREADABLE', '备份写盘失败:' + target + '(' + e.message + ')', { cause: e });
130
+ }
131
+ pruneBackups(dir, keepN);
132
+ return {
133
+ status: 'ok', keep_n: keepN, file: target, size: statSync(target).size, created_at: created,
134
+ items, history: readHistory(dir), days_since_last: daysSinceLast(dir),
135
+ };
136
+ }
137
+ /** 备份历史:现读盘,不缓存(面板/回执要的是当前真状态)。 */
138
+ export function listBackups(opts = {}) {
139
+ const keepN = opts.keepN ?? BACKUP_KEEP_N;
140
+ const dir = resolveBackupDir();
141
+ const history = readHistory(dir);
142
+ return { status: 'ok', dir, keep_n: keepN, count: history.length, history, days_since_last: daysSinceLast(dir) };
143
+ }
144
+ function resolveBackupFile(file, dir) {
145
+ const abs = isAbsolute(file) ? file : join(dir, file);
146
+ const base = basename(abs);
147
+ if (!base.startsWith(BACKUP_PREFIX) || !base.endsWith('.zip')) {
148
+ throw new HomePolicyError('POLICY_BAD_INPUT', '备份文件名不合规(须 ' + BACKUP_PREFIX + '*.zip):' + base);
149
+ }
150
+ if (!existsSync(abs))
151
+ throw new HomePolicyError('POLICY_BAD_INPUT', '备份文件不存在:' + abs);
152
+ return abs;
153
+ }
154
+ function countItems(dbPath) {
155
+ if (!existsSync(dbPath))
156
+ return 0;
157
+ const handle = openHomeDb(dbPath);
158
+ try {
159
+ return Number(handle.db.prepare('SELECT count(*) AS c FROM items').get().c);
160
+ }
161
+ finally {
162
+ closeHomeDb(handle);
163
+ }
164
+ }
165
+ /**
166
+ * 导入恢复:拿备份里的 `home.db` 覆盖当前库;覆盖前先自备份一次(数据安全优先于便捷)。
167
+ * `dryRun`=预告支(SM8-4「文件选择(预告式)→ 校验 → 冲突预览 → 确认导入」的中间那一步):只校验+报数,不落盘。
168
+ */
169
+ export function restoreBackup(file, opts = {}) {
170
+ if (!file)
171
+ throw new HomePolicyError('POLICY_BAD_INPUT', '导入恢复须给 file(备份文件名或绝对路径)');
172
+ const src = resolveBackupFile(file, resolveBackupDir());
173
+ let entries;
174
+ let dbBytes;
175
+ try {
176
+ entries = zipRead(readFileSync(src));
177
+ const dbName = dbFilename();
178
+ const hit = entries.find((e) => e.name === dbName);
179
+ if (!hit)
180
+ throw new Error('备份内未找到 ' + dbName);
181
+ dbBytes = hit.data;
182
+ }
183
+ catch (e) {
184
+ throw new HomePolicyError('POLICY_BAD_INPUT', '备份文件无效:' + src + '(' + e.message + ')');
185
+ }
186
+ const dbPath = resolveDbPath();
187
+ if (opts.dryRun) {
188
+ const mf = entries.find((e) => e.name === 'manifest.json');
189
+ let inBackup = null;
190
+ if (mf) {
191
+ try {
192
+ const j = JSON.parse(mf.data.toString('utf8'));
193
+ if (typeof j.items === 'number')
194
+ inBackup = j.items;
195
+ }
196
+ catch { /* 清单坏就报 null,不谎报件数 */ }
197
+ }
198
+ const cur = countItems(dbPath);
199
+ return {
200
+ status: 'ok', dry_run: true, restored_from: src, items_in_backup: inBackup, items_current: cur,
201
+ message: '预告通过:这份备份' + (inBackup === null ? '(件数未知)' : '含 ' + inBackup + ' 件') +
202
+ ',当前库 ' + cur + ' 件;恢复会**整库覆盖**(恢复前自动自备份)。确认请给 confirm:true',
203
+ };
204
+ }
205
+ const safety = createBackup(opts);
206
+ // 覆盖前清掉 WAL/SHM:留着旧 WAL 会把刚恢复的库再改写坏。
207
+ for (const side of ['-wal', '-shm']) {
208
+ try {
209
+ if (existsSync(dbPath + side))
210
+ unlinkSync(dbPath + side);
211
+ }
212
+ catch { /* 清不掉则下面写入会大声失败 */ }
213
+ }
214
+ try {
215
+ writeFileSync(dbPath, dbBytes);
216
+ }
217
+ catch (e) {
218
+ throw new HomeFetchError('HOME_DB_UNREADABLE', '写回数据库失败:' + dbPath + '(' + e.message + ')', { cause: e });
219
+ }
220
+ let itemsTotal = 0;
221
+ try {
222
+ itemsTotal = countItems(dbPath);
223
+ }
224
+ catch (e) {
225
+ throw new HomeFetchError('HOME_DB_UNREADABLE', '恢复后库不可读:' + dbPath + '(' + e.message + ')', { cause: e });
226
+ }
227
+ return {
228
+ status: 'ok', restored_from: src, safety_backup: safety.file,
229
+ before_items: safety.items, items_total: itemsTotal,
230
+ message: '已从备份恢复:' + basename(src) + '(恢复前已自备份)',
231
+ };
232
+ }
233
+ function csvCell(v) {
234
+ if (v === null || v === undefined)
235
+ return '';
236
+ const s = String(v);
237
+ return /[",\n\r]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
238
+ }
239
+ const CSV_COLS = [
240
+ 'id', 'name', 'category', 'owner', 'purchase_price', 'remark',
241
+ 'location', 'quantity', 'location_status', 'purchase_date', 'expiration_date',
242
+ ];
243
+ /** 导出:JSON=全表可迁移;CSV=物品 ⋈ 位置便携(列与老家 ops.py:620 逐列相同)。 */
244
+ export function exportData(opts = {}) {
245
+ const format = String(opts.format ?? 'json').toLowerCase();
246
+ if (format !== 'json' && format !== 'csv') {
247
+ throw new HomePolicyError('POLICY_BAD_INPUT', '不支持的格式:' + opts.format + '(只认 json/csv)');
248
+ }
249
+ const dbPath = resolveDbPath();
250
+ if (!existsSync(dbPath))
251
+ throw new HomeFetchError('HOME_DB_MISSING', '库不存在,无可导出:' + dbPath);
252
+ let text = '';
253
+ let rows = 0;
254
+ const handle = openHomeDb(dbPath);
255
+ try {
256
+ if (format === 'json') {
257
+ const items = handle.db.prepare('SELECT * FROM items').all();
258
+ const data = {
259
+ schema_version: EXPORT_SCHEMA_VERSION,
260
+ exported_at: nowStr(),
261
+ items,
262
+ item_locations: handle.db.prepare('SELECT * FROM item_locations').all(),
263
+ item_tags: handle.db.prepare('SELECT * FROM item_tags').all(),
264
+ categories: handle.db.prepare('SELECT * FROM categories').all(),
265
+ };
266
+ rows = items.length;
267
+ text = JSON.stringify(data, null, 1) + '\n';
268
+ }
269
+ else {
270
+ const rs = handle.db.prepare('SELECT i.id, i.name, i.category, i.owner, i.purchase_price, i.remark, ' +
271
+ 'l.location, l.quantity, l.location_status, l.purchase_date, l.expiration_date ' +
272
+ 'FROM items i LEFT JOIN item_locations l ON l.item_id = i.id ORDER BY i.id').all();
273
+ rows = rs.length;
274
+ text = [CSV_COLS.join(','), ...rs.map((r) => CSV_COLS.map((c) => csvCell(r[c])).join(','))].join('\n') + '\n';
275
+ }
276
+ }
277
+ finally {
278
+ closeHomeDb(handle);
279
+ }
280
+ const out = opts.output
281
+ ? resolve(opts.output)
282
+ : join(resolveBackupDir(), 'home_export_' + stamp().replace(/_\d{3}$/, '') + (format === 'json' ? '.json' : '.csv'));
283
+ try {
284
+ mkdirSync(dirname(out), { recursive: true });
285
+ writeFileSync(out, text, 'utf8');
286
+ }
287
+ catch (e) {
288
+ throw new HomeFetchError('HOME_DB_UNREADABLE', '导出写盘失败:' + out + '(' + e.message + ')', { cause: e });
289
+ }
290
+ return { status: 'ok', file: out, format, size: Buffer.byteLength(text, 'utf8'), rows };
291
+ }
292
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/fetch/backup.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  export { HomeFetchError, HomePolicyError } from './errors.js';
2
2
  export { SCHEMA_VERSION, openHomeDb, closeHomeDb, addItem, getItemById, listLocationsByItem, listTagsByItem, searchItems, updateItem, adjustQuantity, setLocationStatus, moveLocation, setItemTags, listAllTags, mergeTags, listCategories, getCategoryById, encryptPassword, decryptPassword, assertMasterKey, } from './db.js';
3
3
  export type { HomeItem, HomeLocation, HomeDb, SearchFilter } from './db.js';
4
- export { resolveDbDir, resolveDbPath, assertWritablePath, DB_FILENAME } from './paths.js';
4
+ export { resolveDbDir, resolveDbPath, dbFilename, DEFAULT_DB_FILENAME } from './paths.js';
5
+ export { resolveBackupDir, createBackup, listBackups, restoreBackup, exportData } from './backup.js';
5
6
  export { addInventoryRecord, listInventoryRecords, listLocationNodes, ensureLocationNode, listShopping, addShopping, checkShopping, missingItems, stockList, setThreshold, listPurchases, addPurchase, purchaseYearStats, listWarranties, addWarranty, addServiceEvent, listCerts, addCert, listAccounts, listMembers, addMember, listBorrows, addBorrow, statsOverview, highFreq, idleItems, expiringItems, } from './domains.js';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fetch/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAC7D,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAC5E,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EACpE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,GACnF,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EACL,kBAAkB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAC/E,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAC/E,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAC3F,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAChF,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAClD,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fetch/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAC7D,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAC5E,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EACpE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,GACnF,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE1F,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrG,OAAO,EACL,kBAAkB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAC/E,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAC/E,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAC3F,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAChF,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAClD,MAAM,cAAc,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export { HomeFetchError, HomePolicyError } from './errors.js';
2
2
  export { SCHEMA_VERSION, openHomeDb, closeHomeDb, addItem, getItemById, listLocationsByItem, listTagsByItem, searchItems, updateItem, adjustQuantity, setLocationStatus, moveLocation, setItemTags, listAllTags, mergeTags, listCategories, getCategoryById, encryptPassword, decryptPassword, assertMasterKey, } from './db.js';
3
- export { resolveDbDir, resolveDbPath, assertWritablePath, DB_FILENAME } from './paths.js';
3
+ export { resolveDbDir, resolveDbPath, dbFilename, DEFAULT_DB_FILENAME } from './paths.js';
4
+ // #707:备份导出/导入恢复(HELP SM8-3/SM8-4)的落点。
5
+ export { resolveBackupDir, createBackup, listBackups, restoreBackup, exportData } from './backup.js';
4
6
  export { addInventoryRecord, listInventoryRecords, listLocationNodes, ensureLocationNode, listShopping, addShopping, checkShopping, missingItems, stockList, setThreshold, listPurchases, addPurchase, purchaseYearStats, listWarranties, addWarranty, addServiceEvent, listCerts, addCert, listAccounts, listMembers, addMember, listBorrows, addBorrow, statsOverview, highFreq, idleItems, expiringItems, } from './domains.js';
5
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fetch/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAC7D,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAC5E,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EACpE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,GACnF,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EACL,kBAAkB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAC/E,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAC/E,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAC3F,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAChF,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAClD,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fetch/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAC7D,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAC5E,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EACpE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,GACnF,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC1F,uCAAuC;AACvC,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrG,OAAO,EACL,kBAAkB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAC/E,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAC/E,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAC3F,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAChF,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAClD,MAAM,cAAc,CAAC"}
@@ -1,6 +1,8 @@
1
- export declare const DB_FILENAME = "home.db";
1
+ /** 库文件名的默认值:=改造前的代码常量 `DB_FILENAME`(配置项 `db.name` 空串即用它)。 */
2
+ export declare const DEFAULT_DB_FILENAME = "home.db";
3
+ /** 库文件名:配置 `db.name`,空串=默认(`home.db`)。 */
4
+ export declare function dbFilename(): string;
5
+ /** 库目录:配置 `db.dir`,空串=数据目录(`~/.ilife/data/`,`ILIFE_CONFIG_DIR` 可整体接管)。 */
2
6
  export declare function resolveDbDir(): string;
3
7
  export declare function resolveDbPath(filename?: string, dir?: string): string;
4
- /** 测试隔离守卫:非 tmp 路径写库须 HOME_FORCE_PROD=1(卡路里哨兵语义的居家版)。 */
5
- export declare function assertWritablePath(dbPath: string): void;
6
8
  //# sourceMappingURL=paths.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/fetch/paths.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,wBAAgB,YAAY,IAAI,MAAM,CASrC;AAED,wBAAgB,aAAa,CAAC,QAAQ,SAAc,EAAE,GAAG,SAAiB,GAAG,MAAM,CAGlF;AAED,yDAAyD;AACzD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAYvD"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/fetch/paths.ts"],"names":[],"mappings":"AAUA,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAE7C,0CAA0C;AAC1C,wBAAgB,UAAU,IAAI,MAAM,CAGnC;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAgB,aAAa,CAAC,QAAQ,SAAe,EAAE,GAAG,SAAiB,GAAG,MAAM,CAGnF"}
@@ -1,31 +1,26 @@
1
- // DB 路径解析与隔离守卫:SKILLS_DB_PATH 必设,无默认值(铁律缺失阻断)。
2
- // 老家 home_manager/db.py Q6 fallback 链(D:/.db 等)TS 线有意不继承:拒绝隐式写生产。
1
+ // 库路径解析:**配置文件的唯一真相**(#695;口径见「配置的存与生效口径裁定」#675 的解决评论)。
2
+ // 改造前这里读 `SKILLS_DB_PATH`(无默认值、缺失即抛),并按 `HOME_FORCE_PROD=1` 守「非 tmp 不许写库」;
3
+ // #695 按用户裁决把两个环境变量**一并删掉**:路径改从 `~/.ilife/home.yaml` 取,写库边界改由
4
+ // 「测试基座强制 `ILIFE_CONFIG_DIR`」(`base-link-core` 的 `CONFIG_TEST_ISOLATION_MISSING`)承担——
5
+ // 把「忘了配」从静默写真实库变成响亮失败。
6
+ // 老家 home_manager/db.py Q6 fallback 链(D:/.db 等)TS 线仍有意不继承:拒绝隐式写生产。
3
7
  import { mkdirSync } from 'node:fs';
4
- import { tmpdir } from 'node:os';
5
- import { join, resolve } from 'node:path';
6
- export const DB_FILENAME = 'home.db';
8
+ import { join } from 'node:path';
9
+ import { loadHomeConfig } from '../config.js';
10
+ /** 库文件名的默认值:=改造前的代码常量 `DB_FILENAME`(配置项 `db.name` 空串即用它)。 */
11
+ export const DEFAULT_DB_FILENAME = 'home.db';
12
+ /** 库文件名:配置 `db.name`,空串=默认(`home.db`)。 */
13
+ export function dbFilename() {
14
+ const name = loadHomeConfig().values.db.name;
15
+ return name === '' ? DEFAULT_DB_FILENAME : name;
16
+ }
17
+ /** 库目录:配置 `db.dir`,空串=数据目录(`~/.ilife/data/`,`ILIFE_CONFIG_DIR` 可整体接管)。 */
7
18
  export function resolveDbDir() {
8
- const env = process.env.SKILLS_DB_PATH;
9
- if (!env) {
10
- throw new Error('[skill-home] SKILLS_DB_PATH 未设置:拒绝隐式落盘。' +
11
- '生产请设置 SKILLS_DB_PATH;测试请传显式 tmp 路径。');
12
- }
13
- return env;
19
+ const dir = loadHomeConfig().values.db.dir;
20
+ return dir === '' ? loadHomeConfig().dataDir : dir;
14
21
  }
15
- export function resolveDbPath(filename = DB_FILENAME, dir = resolveDbDir()) {
22
+ export function resolveDbPath(filename = dbFilename(), dir = resolveDbDir()) {
16
23
  mkdirSync(dir, { recursive: true });
17
24
  return join(dir, filename);
18
25
  }
19
- /** 测试隔离守卫:非 tmp 路径写库须 HOME_FORCE_PROD=1(卡路里哨兵语义的居家版)。 */
20
- export function assertWritablePath(dbPath) {
21
- const forceProd = process.env.HOME_FORCE_PROD === '1';
22
- const underTmp = resolve(dbPath).startsWith(resolve(tmpdir()));
23
- if (!underTmp && !forceProd) {
24
- throw new Error('[skill-home] 拒绝写入非 tmp 路径 ' + dbPath + ':测试隔离守卫。' +
25
- '如确需生产写,请设置 HOME_FORCE_PROD=1(opt-in 可见)。');
26
- }
27
- if (!underTmp && forceProd) {
28
- console.error('[skill-home] HOME_FORCE_PROD=1: 写入 ' + dbPath);
29
- }
30
- }
31
26
  //# sourceMappingURL=paths.js.map