@harrylabsj/kiwi 0.8.0 → 0.9.0

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 (214) hide show
  1. package/README.md +1 -1
  2. package/dist/a2a/negotiate.d.ts +6 -2
  3. package/dist/a2a/negotiate.js +10 -3
  4. package/dist/a2a/negotiate.js.map +1 -1
  5. package/dist/a2a/node.d.ts +4 -1
  6. package/dist/a2a/node.js +56 -3
  7. package/dist/a2a/node.js.map +1 -1
  8. package/dist/a2a/server/merchant-handler.d.ts +11 -3
  9. package/dist/a2a/server/merchant-handler.js +84 -26
  10. package/dist/a2a/server/merchant-handler.js.map +1 -1
  11. package/dist/agent/buyer/buyer-tools.js +2 -2
  12. package/dist/agent/buyer/buyer-tools.js.map +1 -1
  13. package/dist/agent/kernel.js +5 -1
  14. package/dist/agent/kernel.js.map +1 -1
  15. package/dist/agent/memory/schema.d.ts +1 -1
  16. package/dist/agent/memory/schema.js +9 -1
  17. package/dist/agent/memory/schema.js.map +1 -1
  18. package/dist/agent/merchant/action-candidate.d.ts +18 -0
  19. package/dist/agent/merchant/action-candidate.js +51 -0
  20. package/dist/agent/merchant/action-candidate.js.map +1 -1
  21. package/dist/agent/merchant/fake-merchant-client.js +6 -1
  22. package/dist/agent/merchant/fake-merchant-client.js.map +1 -1
  23. package/dist/agent/merchant/intelligence/default-backend.js +25 -2
  24. package/dist/agent/merchant/intelligence/default-backend.js.map +1 -1
  25. package/dist/agent/merchant/merchant-client.d.ts +50 -1
  26. package/dist/agent/merchant/merchant-client.js +157 -8
  27. package/dist/agent/merchant/merchant-client.js.map +1 -1
  28. package/dist/agent/merchant/merchant-presentations.js +25 -2
  29. package/dist/agent/merchant/merchant-presentations.js.map +1 -1
  30. package/dist/agent/merchant/merchant-tools.js +102 -188
  31. package/dist/agent/merchant/merchant-tools.js.map +1 -1
  32. package/dist/agent/merchant/types.d.ts +6 -2
  33. package/dist/agent/merchant/types.js +45 -0
  34. package/dist/agent/merchant/types.js.map +1 -1
  35. package/dist/auth/merchant-authorization.d.ts +49 -0
  36. package/dist/auth/merchant-authorization.js +61 -0
  37. package/dist/auth/merchant-authorization.js.map +1 -0
  38. package/dist/auth/merchant-oauth.d.ts +255 -0
  39. package/dist/auth/merchant-oauth.js +764 -0
  40. package/dist/auth/merchant-oauth.js.map +1 -0
  41. package/dist/auth/merchant-pairing.d.ts +62 -0
  42. package/dist/auth/merchant-pairing.js +199 -0
  43. package/dist/auth/merchant-pairing.js.map +1 -0
  44. package/dist/auth/merchant-sessions.d.ts +71 -0
  45. package/dist/auth/merchant-sessions.js +168 -0
  46. package/dist/auth/merchant-sessions.js.map +1 -0
  47. package/dist/buyer-core/build-service.d.ts +5 -0
  48. package/dist/buyer-core/build-service.js +12 -0
  49. package/dist/buyer-core/build-service.js.map +1 -1
  50. package/dist/buyer-core/errors.d.ts +1 -1
  51. package/dist/buyer-core/errors.js +2 -0
  52. package/dist/buyer-core/errors.js.map +1 -1
  53. package/dist/buyer-core/merchant-index.d.ts +19 -4
  54. package/dist/buyer-core/merchant-index.js +137 -16
  55. package/dist/buyer-core/merchant-index.js.map +1 -1
  56. package/dist/buyer-core/negotiator.d.ts +0 -10
  57. package/dist/buyer-core/negotiator.js +12 -1
  58. package/dist/buyer-core/negotiator.js.map +1 -1
  59. package/dist/buyer-core/quote-fetcher.js +2 -1
  60. package/dist/buyer-core/quote-fetcher.js.map +1 -1
  61. package/dist/buyer-core/service.d.ts +104 -0
  62. package/dist/buyer-core/service.js +105 -1
  63. package/dist/buyer-core/service.js.map +1 -1
  64. package/dist/cli.d.ts +4 -2
  65. package/dist/cli.js +612 -20
  66. package/dist/cli.js.map +1 -1
  67. package/dist/config/profile.d.ts +30 -0
  68. package/dist/config/profile.js +126 -61
  69. package/dist/config/profile.js.map +1 -1
  70. package/dist/demo/demo-runner.js +12 -9
  71. package/dist/demo/demo-runner.js.map +1 -1
  72. package/dist/discovery/catalog-source/buyer-follows.d.ts +82 -0
  73. package/dist/discovery/catalog-source/buyer-follows.js +228 -0
  74. package/dist/discovery/catalog-source/buyer-follows.js.map +1 -0
  75. package/dist/discovery/catalog-source/connector-identity.d.ts +75 -0
  76. package/dist/discovery/catalog-source/connector-identity.js +175 -0
  77. package/dist/discovery/catalog-source/connector-identity.js.map +1 -0
  78. package/dist/discovery/catalog-source/errors.d.ts +1 -1
  79. package/dist/discovery/catalog-source/errors.js +2 -0
  80. package/dist/discovery/catalog-source/errors.js.map +1 -1
  81. package/dist/discovery/catalog-source/merchant-publications.d.ts +65 -0
  82. package/dist/discovery/catalog-source/merchant-publications.js +207 -0
  83. package/dist/discovery/catalog-source/merchant-publications.js.map +1 -0
  84. package/dist/discovery/catalog-source/register.d.ts +24 -0
  85. package/dist/discovery/catalog-source/register.js +46 -0
  86. package/dist/discovery/catalog-source/register.js.map +1 -1
  87. package/dist/discovery/catalog-source/source.js +2 -1
  88. package/dist/discovery/catalog-source/source.js.map +1 -1
  89. package/dist/fs/atomic-write.d.ts +18 -0
  90. package/dist/fs/atomic-write.js +29 -0
  91. package/dist/fs/atomic-write.js.map +1 -0
  92. package/dist/http/server.js +4 -1
  93. package/dist/http/server.js.map +1 -1
  94. package/dist/mcp/cli.d.ts +2 -0
  95. package/dist/mcp/cli.js +7 -6
  96. package/dist/mcp/cli.js.map +1 -1
  97. package/dist/mcp/merchant-auth.d.ts +78 -0
  98. package/dist/mcp/merchant-auth.js +146 -0
  99. package/dist/mcp/merchant-auth.js.map +1 -0
  100. package/dist/mcp/merchant-dirs.d.ts +26 -0
  101. package/dist/mcp/merchant-dirs.js +44 -0
  102. package/dist/mcp/merchant-dirs.js.map +1 -0
  103. package/dist/mcp/merchant-resources.d.ts +38 -0
  104. package/dist/mcp/merchant-resources.js +146 -0
  105. package/dist/mcp/merchant-resources.js.map +1 -0
  106. package/dist/mcp/merchant-server.d.ts +106 -0
  107. package/dist/mcp/merchant-server.js +615 -0
  108. package/dist/mcp/merchant-server.js.map +1 -0
  109. package/dist/mcp/merchant-tools.d.ts +103 -0
  110. package/dist/mcp/merchant-tools.js +517 -0
  111. package/dist/mcp/merchant-tools.js.map +1 -0
  112. package/dist/mcp/server.js +4 -3
  113. package/dist/mcp/server.js.map +1 -1
  114. package/dist/mcp/tools.d.ts +5 -3
  115. package/dist/mcp/tools.js +102 -5
  116. package/dist/mcp/tools.js.map +1 -1
  117. package/dist/mcp/types.d.ts +1 -1
  118. package/dist/mcp/types.js +4 -0
  119. package/dist/mcp/types.js.map +1 -1
  120. package/dist/merchant/ops.d.ts +0 -11
  121. package/dist/merchant/ops.js +13 -1
  122. package/dist/merchant/ops.js.map +1 -1
  123. package/dist/merchant/workbench-service.d.ts +189 -0
  124. package/dist/merchant/workbench-service.js +417 -0
  125. package/dist/merchant/workbench-service.js.map +1 -0
  126. package/dist/merchant-admin/pending-page.d.ts +35 -0
  127. package/dist/merchant-admin/pending-page.js +69 -0
  128. package/dist/merchant-admin/pending-page.js.map +1 -0
  129. package/dist/merchant-core/commands.d.ts +99 -0
  130. package/dist/merchant-core/commands.js +179 -0
  131. package/dist/merchant-core/commands.js.map +1 -0
  132. package/dist/merchant-core/executor.d.ts +69 -0
  133. package/dist/merchant-core/executor.js +218 -0
  134. package/dist/merchant-core/executor.js.map +1 -0
  135. package/dist/merchant-core/negotiation-adapters.d.ts +55 -0
  136. package/dist/merchant-core/negotiation-adapters.js +55 -0
  137. package/dist/merchant-core/negotiation-adapters.js.map +1 -0
  138. package/dist/merchant-core/network-checks.d.ts +65 -0
  139. package/dist/merchant-core/network-checks.js +111 -0
  140. package/dist/merchant-core/network-checks.js.map +1 -0
  141. package/dist/merchant-core/operations.d.ts +64 -0
  142. package/dist/merchant-core/operations.js +136 -0
  143. package/dist/merchant-core/operations.js.map +1 -0
  144. package/dist/merchant-core/policy-runtime.d.ts +70 -0
  145. package/dist/merchant-core/policy-runtime.js +181 -0
  146. package/dist/merchant-core/policy-runtime.js.map +1 -0
  147. package/dist/merchant-core/product-import.d.ts +67 -0
  148. package/dist/merchant-core/product-import.js +160 -0
  149. package/dist/merchant-core/product-import.js.map +1 -0
  150. package/dist/merchant-core/service.d.ts +203 -0
  151. package/dist/merchant-core/service.js +402 -0
  152. package/dist/merchant-core/service.js.map +1 -0
  153. package/dist/merchant-gateway/catalog-publications.d.ts +71 -0
  154. package/dist/merchant-gateway/catalog-publications.js +175 -0
  155. package/dist/merchant-gateway/catalog-publications.js.map +1 -0
  156. package/dist/merchant-gateway/catalog-tools.d.ts +46 -0
  157. package/dist/merchant-gateway/catalog-tools.js +277 -0
  158. package/dist/merchant-gateway/catalog-tools.js.map +1 -0
  159. package/dist/merchant-gateway/cli.d.ts +75 -0
  160. package/dist/merchant-gateway/cli.js +458 -0
  161. package/dist/merchant-gateway/cli.js.map +1 -0
  162. package/dist/merchant-gateway/credential-vault.d.ts +44 -0
  163. package/dist/merchant-gateway/credential-vault.js +111 -0
  164. package/dist/merchant-gateway/credential-vault.js.map +1 -0
  165. package/dist/merchant-gateway/entry-server.d.ts +117 -0
  166. package/dist/merchant-gateway/entry-server.js +710 -0
  167. package/dist/merchant-gateway/entry-server.js.map +1 -0
  168. package/dist/merchant-gateway/instance-registration.d.ts +143 -0
  169. package/dist/merchant-gateway/instance-registration.js +382 -0
  170. package/dist/merchant-gateway/instance-registration.js.map +1 -0
  171. package/dist/merchant-gateway/instance-tools.d.ts +58 -0
  172. package/dist/merchant-gateway/instance-tools.js +184 -0
  173. package/dist/merchant-gateway/instance-tools.js.map +1 -0
  174. package/dist/merchant-gateway/mcp-proxy.d.ts +25 -0
  175. package/dist/merchant-gateway/mcp-proxy.js +192 -0
  176. package/dist/merchant-gateway/mcp-proxy.js.map +1 -0
  177. package/dist/merchant-gateway/pinned-fetch.d.ts +52 -0
  178. package/dist/merchant-gateway/pinned-fetch.js +212 -0
  179. package/dist/merchant-gateway/pinned-fetch.js.map +1 -0
  180. package/dist/merchant-gateway/tenant-registry.d.ts +82 -0
  181. package/dist/merchant-gateway/tenant-registry.js +190 -0
  182. package/dist/merchant-gateway/tenant-registry.js.map +1 -0
  183. package/dist/merchant-gateway/tool-bundle.d.ts +26 -0
  184. package/dist/merchant-gateway/tool-bundle.js +56 -0
  185. package/dist/merchant-gateway/tool-bundle.js.map +1 -0
  186. package/dist/merchant-runtime/backup.d.ts +48 -0
  187. package/dist/merchant-runtime/backup.js +226 -0
  188. package/dist/merchant-runtime/backup.js.map +1 -0
  189. package/dist/merchant-runtime/health.d.ts +99 -0
  190. package/dist/merchant-runtime/health.js +206 -0
  191. package/dist/merchant-runtime/health.js.map +1 -0
  192. package/dist/merchant-runtime/jobs.d.ts +64 -0
  193. package/dist/merchant-runtime/jobs.js +107 -0
  194. package/dist/merchant-runtime/jobs.js.map +1 -0
  195. package/dist/merchant-runtime/manager.d.ts +94 -0
  196. package/dist/merchant-runtime/manager.js +305 -0
  197. package/dist/merchant-runtime/manager.js.map +1 -0
  198. package/dist/merchant-runtime/services.d.ts +30 -0
  199. package/dist/merchant-runtime/services.js +51 -0
  200. package/dist/merchant-runtime/services.js.map +1 -0
  201. package/dist/net/url.d.ts +26 -0
  202. package/dist/net/url.js +32 -0
  203. package/dist/net/url.js.map +1 -0
  204. package/dist/product-cli.js +22 -3
  205. package/dist/product-cli.js.map +1 -1
  206. package/dist/product-compat.d.ts +9 -1
  207. package/dist/product-compat.js +12 -1
  208. package/dist/product-compat.js.map +1 -1
  209. package/dist/product-init.js +17 -5
  210. package/dist/product-init.js.map +1 -1
  211. package/dist/product-publish.js +2 -1
  212. package/dist/product-publish.js.map +1 -1
  213. package/package.json +3 -2
  214. package/skills/kiwi-buyer/SKILL.md +2 -0
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Copyright 2026 harrylabsj
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * 持续备份(V2 阶段四 + BUG-06 修复:src/merchant-runtime/backup.ts)。
18
+ *
19
+ * 事务一致性:
20
+ * - SQLite 文件用 `VACUUM INTO`(读事务一致性快照;源库 WAL 模式下并发写
21
+ * 安全;busy 时短暂重试,仍失败则本轮备份 fail-closed 记错误);
22
+ * - 快照完成后实际打开备份库跑 `PRAGMA integrity_check`,不通过即删除该轮
23
+ * 快照并抛错(绝不留下不可恢复的快照);
24
+ * - Ledger 等文件类数据在 SQLite 快照之后复制——最终一致性边界:append
25
+ * 中的最后一行可能是部分写入,恢复端按 Ledger 链校验容错(文档化,
26
+ * 见 deploy/merchant-bundle/README.md)。
27
+ *
28
+ * RPO 口径(工程诚实):周期快照的 RPO ≤ 备份周期(缺省 5 分钟),
29
+ * 不宣称 RPO=0;RPO=0 需同步持久化/持续复制(留主备阶段)。
30
+ * 快照写 manifest(文件清单 + 大小 + sha256)供恢复校验;轮换保留最近 N 份。
31
+ */
32
+ import { createHash } from "node:crypto";
33
+ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
34
+ import path from "node:path";
35
+ import { DatabaseSync } from "node:sqlite";
36
+ import { writeFileAtomic } from "../fs/atomic-write.js";
37
+ function walk(dir, base, out, skipped) {
38
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
39
+ const full = path.join(dir, entry.name);
40
+ if (entry.isDirectory())
41
+ walk(full, base, out, skipped);
42
+ else if (entry.isFile())
43
+ out.push(path.relative(base, full));
44
+ else if (entry.isSymbolicLink()) {
45
+ // 审查 P2:symlink 数据静默丢弃会让恢复后出现「校验全绿」的数据缺口
46
+ // ——至少记入 manifest 跳过清单,让缺口可见。
47
+ skipped.push(path.relative(base, full));
48
+ }
49
+ }
50
+ }
51
+ function sha256File(file) {
52
+ return createHash("sha256").update(readFileSync(file)).digest("hex");
53
+ }
54
+ /**
55
+ * SQLite 事务一致性快照(BUG-06):VACUUM INTO(读事务一致;源库 WAL 下并发
56
+ * 写安全)。busy/失败短暂重试,仍失败抛错(本轮备份 fail-closed)。
57
+ * 快照后打开备份库跑 integrity_check——不通过即抛错(调用方删除该轮快照)。
58
+ */
59
+ function snapshotSqlite(src, dst) {
60
+ mkdirSync(path.dirname(dst), { recursive: true, mode: 0o700 });
61
+ rmSync(dst, { force: true });
62
+ if (!existsSync(src)) {
63
+ // 审查 P2:DatabaseSync 缺省会静默建空库——源在 walk 与快照之间消失时
64
+ // 会产出「校验全绿」的空库快照(静默数据丢失)。fail-closed。
65
+ throw new Error(`SQLite 源库不存在,拒绝快照(fail-closed):${src}`);
66
+ }
67
+ let lastErr;
68
+ for (let attempt = 0; attempt < 3; attempt += 1) {
69
+ let db;
70
+ try {
71
+ db = new DatabaseSync(src, { readOnly: true });
72
+ db.exec("PRAGMA busy_timeout = 2000");
73
+ // VACUUM INTO 目标路径防注入:单引号转义
74
+ db.exec(`VACUUM INTO '${dst.replaceAll("'", "''")}'`);
75
+ db.close();
76
+ db = undefined;
77
+ const check = new DatabaseSync(dst);
78
+ try {
79
+ const rows = check.prepare("PRAGMA integrity_check").all();
80
+ if (rows.length !== 1 || rows[0]?.integrity_check !== "ok") {
81
+ throw new Error(`备份库 integrity_check 未通过:${dst}(${JSON.stringify(rows)})`);
82
+ }
83
+ }
84
+ finally {
85
+ check.close();
86
+ }
87
+ return;
88
+ }
89
+ catch (err) {
90
+ lastErr = err;
91
+ if (db !== undefined) {
92
+ try {
93
+ db.close();
94
+ }
95
+ catch {
96
+ // 忽略
97
+ }
98
+ }
99
+ rmSync(dst, { force: true });
100
+ }
101
+ }
102
+ throw new Error(`SQLite 快照失败(${src}):${lastErr instanceof Error ? lastErr.message : String(lastErr)}`);
103
+ }
104
+ /** 快照备份 + 轮换(keepLatest 份)。幂等:同时间戳目录复用覆盖。
105
+ * 启动时清理孤儿快照目录(审查 P2:优雅关闭/崩溃打断备份留下的无 manifest
106
+ * 目录既不参与轮换也永不清理,反复在备份窗口重启可累积占满磁盘)。 */
107
+ export function runBackup(options) {
108
+ const now = (options.now ?? (() => new Date().toISOString()))();
109
+ const keep = options.keepLatest ?? 10;
110
+ // 孤儿快照清理:无 manifest.json 且修改时间超过 1 小时的目录(1 小时缓冲
111
+ // 避免误删并发进行中的快照——jobs 已串行,此处是双保险)。
112
+ try {
113
+ for (const entry of readdirSync(options.backupsDir, { withFileTypes: true })) {
114
+ if (!entry.isDirectory())
115
+ continue;
116
+ const dir = path.join(options.backupsDir, entry.name);
117
+ if (existsSync(path.join(dir, "manifest.json")))
118
+ continue;
119
+ try {
120
+ if (Date.now() - statSync(dir).mtimeMs > 60 * 60 * 1000) {
121
+ rmSync(dir, { recursive: true, force: true });
122
+ }
123
+ }
124
+ catch {
125
+ // 单个目录清理失败不影响本轮备份
126
+ }
127
+ }
128
+ }
129
+ catch {
130
+ // backups 目录不存在等:交给下方 mkdirSync
131
+ }
132
+ const stamp = now.replace(/[:.]/g, "-");
133
+ const snapshotDir = path.join(options.backupsDir, stamp);
134
+ mkdirSync(snapshotDir, { recursive: true, mode: 0o700 });
135
+ const files = [];
136
+ const skipped = [];
137
+ walk(options.dataDir, options.dataDir, files, skipped);
138
+ // 不备份 backups 自身(若 backups 在 dataDir 内)、锁文件与 SQLite WAL/SHM 临时文件
139
+ const included = files.filter((f) => !f.startsWith("backups") &&
140
+ !f.endsWith(".lock") &&
141
+ !f.endsWith(".tmp") &&
142
+ !f.endsWith(".sqlite-wal") &&
143
+ !f.endsWith(".sqlite-shm"));
144
+ try {
145
+ // SQLite 先行(VACUUM INTO 一致性快照),文件类随后(最终一致性边界见文件头)
146
+ const sorted = included.sort((a, b) => {
147
+ const aSql = a.endsWith(".sqlite") ? 0 : 1;
148
+ const bSql = b.endsWith(".sqlite") ? 0 : 1;
149
+ return aSql - bSql || (a < b ? -1 : 1);
150
+ });
151
+ const manifest = {
152
+ created_at: now,
153
+ source_dir: options.dataDir,
154
+ files: [],
155
+ ...(skipped.length > 0 ? { skipped } : {}),
156
+ };
157
+ for (const rel of sorted) {
158
+ const src = path.join(options.dataDir, rel);
159
+ const dst = path.join(snapshotDir, rel);
160
+ if (rel.endsWith(".sqlite")) {
161
+ snapshotSqlite(src, dst);
162
+ }
163
+ else {
164
+ mkdirSync(path.dirname(dst), { recursive: true, mode: 0o700 });
165
+ cpSync(src, dst);
166
+ }
167
+ // manifest 记录的是**快照内容**的摘要(dst),恢复校验的是快照自身完整性
168
+ manifest.files.push({ path: rel, bytes: statSync(dst).size, sha256: sha256File(dst) });
169
+ }
170
+ writeFileSync(path.join(snapshotDir, "manifest.json"), JSON.stringify(manifest, null, 2), {
171
+ mode: 0o600,
172
+ });
173
+ // 审查 P2:成功备份的最新时间戳(health 的 backup_stale 告警读取)。
174
+ writeFileAtomic(path.join(options.backupsDir, "latest-backup.json"), `${JSON.stringify({ created_at: now, snapshot_dir: snapshotDir }, null, 2)}\n`, { mode: 0o600 });
175
+ }
176
+ catch (err) {
177
+ // 失败轮次不留半成品快照
178
+ rmSync(snapshotDir, { recursive: true, force: true });
179
+ throw err;
180
+ }
181
+ // 轮换:保留最近 keep 份
182
+ const snapshots = readdirSync(options.backupsDir, { withFileTypes: true })
183
+ .filter((e) => e.isDirectory() && existsSync(path.join(options.backupsDir, e.name, "manifest.json")))
184
+ .map((e) => e.name)
185
+ .sort()
186
+ .reverse();
187
+ const rotatedOut = [];
188
+ for (const old of snapshots.slice(keep)) {
189
+ rmSync(path.join(options.backupsDir, old), { recursive: true, force: true });
190
+ rotatedOut.push(old);
191
+ }
192
+ const manifest = JSON.parse(readFileSync(path.join(snapshotDir, "manifest.json"), "utf8"));
193
+ return { snapshot_dir: snapshotDir, manifest, rotated_out: rotatedOut };
194
+ }
195
+ /** 恢复(恢复演练同路径):按 manifest 校验快照完整性后回写目标目录。 */
196
+ export function restoreBackup(options) {
197
+ const manifest = JSON.parse(readFileSync(path.join(options.snapshotDir, "manifest.json"), "utf8"));
198
+ // 路径越界守卫(审查 P2):manifest 可能来自异地/他人,files[].path 含
199
+ // `..` 段或绝对路径即是任意文件写入原语——逐条校验后再落盘。
200
+ const targetRoot = path.resolve(options.targetDir);
201
+ const safeRel = (rel) => {
202
+ const normalized = path.normalize(rel);
203
+ if (path.isAbsolute(normalized) || normalized.split(path.sep).includes("..")) {
204
+ throw new Error(`备份清单路径越界(拒绝恢复):${rel}`);
205
+ }
206
+ return normalized;
207
+ };
208
+ // 先校验完整性(manifest 内每个文件存在且 sha256 一致)——不完整拒绝恢复。
209
+ for (const f of manifest.files) {
210
+ const rel = safeRel(f.path);
211
+ const src = path.join(options.snapshotDir, rel);
212
+ if (!existsSync(src))
213
+ throw new Error(`备份快照缺文件 ${f.path}(不完整,拒绝恢复)`);
214
+ if (sha256File(src) !== f.sha256) {
215
+ throw new Error(`备份快照文件 ${f.path} 摘要不一致(损坏,拒绝恢复)`);
216
+ }
217
+ }
218
+ mkdirSync(options.targetDir, { recursive: true, mode: 0o700 });
219
+ for (const f of manifest.files) {
220
+ const dst = path.join(targetRoot, safeRel(f.path));
221
+ mkdirSync(path.dirname(dst), { recursive: true, mode: 0o700 });
222
+ cpSync(path.join(options.snapshotDir, safeRel(f.path)), dst);
223
+ }
224
+ return { restored: manifest.files.length, verified: true };
225
+ }
226
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/merchant-runtime/backup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Copyright 2026 harrylabsj
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import type { ManagedServiceState } from "./manager.js";
17
+ export interface MerchantAlert {
18
+ code: "process_down" | "product_source_unavailable" | "registration_invalid" | "backlog" | "disk_low" | "cert_expiring" | "backup_stale";
19
+ severity: "critical" | "warning";
20
+ message: string;
21
+ }
22
+ export interface MerchantHealthReport {
23
+ ok: boolean;
24
+ checked_at: string;
25
+ checks: {
26
+ processes: {
27
+ ok: boolean;
28
+ services: ManagedServiceState[];
29
+ };
30
+ product_source: {
31
+ ok: boolean;
32
+ version?: string;
33
+ error?: string;
34
+ };
35
+ data_dir: {
36
+ ok: boolean;
37
+ path: string;
38
+ writable: boolean;
39
+ };
40
+ disk: {
41
+ ok: boolean;
42
+ free_bytes?: number;
43
+ };
44
+ registration: {
45
+ ok: boolean;
46
+ error?: string;
47
+ evaluated: boolean;
48
+ };
49
+ backlog: {
50
+ ok: true;
51
+ pending: number;
52
+ threshold: number;
53
+ };
54
+ certificate: {
55
+ ok: boolean;
56
+ days_left?: number;
57
+ evaluated: boolean;
58
+ };
59
+ backup: {
60
+ ok: true;
61
+ evaluated: boolean;
62
+ last_success_at?: string;
63
+ error?: string;
64
+ };
65
+ };
66
+ /** 结构化告警事件(V2 阶段四 7×24;超阈值/失效即产生,可供通知通道消费)。 */
67
+ alerts: MerchantAlert[];
68
+ }
69
+ /** 磁盘余量下限(字节;缺省 100MB)。 */
70
+ export declare const MIN_FREE_BYTES: number;
71
+ /** 待处理命令积压告警阈值(缺省 50)。 */
72
+ export declare const BACKLOG_ALERT_THRESHOLD = 50;
73
+ /** 证书临期告警阈值(天;缺省 14)。 */
74
+ export declare const CERT_EXPIRING_DAYS = 14;
75
+ /** 能力探测记录最大有效期(BUG-05;缺省 3 分钟 = 3 个健康轮询周期)。 */
76
+ export declare const PROBE_MAX_AGE_MS = 180000;
77
+ export interface MerchantHealthDeps {
78
+ /** merchantDataDir(V2 §5.1)。 */
79
+ dataDir: string;
80
+ /** 受管服务状态(来自 MerchantRuntimeManager.status())。 */
81
+ services: ManagedServiceState[];
82
+ now?: () => string;
83
+ minFreeBytes?: number;
84
+ /** 待处理命令数(积压告警;缺省 0)。 */
85
+ pendingCommands?: number;
86
+ /** 注册检查报告(F25;缺省不评)。 */
87
+ registration?: {
88
+ ok: boolean;
89
+ error?: string;
90
+ };
91
+ /** TLS 证书剩余天数(可选;配置了才评)。 */
92
+ certDaysLeft?: number;
93
+ /** 能力探测记录最大有效期(BUG-05;过期记录判 unhealthy)。 */
94
+ probeMaxAgeMs?: number;
95
+ /** 备份新鲜度(审查 P2:备份停摆进告警通道,不再只活在内存 jobs 里)。
96
+ * 读 <backupsDir>/latest-backup.json(runBackup 成功后原子落盘)。 */
97
+ backupMaxAgeMs?: number;
98
+ }
99
+ export declare function collectMerchantHealth(deps: MerchantHealthDeps): MerchantHealthReport;
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Copyright 2026 harrylabsj
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * Merchant 实例分项健康检查(V2 阶段一:src/merchant-runtime/health.ts)。
18
+ *
19
+ * 分项:受管进程存活、商品源(shopping-cli 能力探测结果,读
20
+ * capability-probe.json 落盘记录——探测本身在 merchant-client.ts)、状态
21
+ * 目录可写、磁盘余量。输出结构化健康报告;任一分项失败 → ok:false,
22
+ * 不吞错不编造(fail-closed 口径与能力探测一致)。
23
+ */
24
+ import { existsSync, readFileSync, statfsSync, writeFileSync, rmSync } from "node:fs";
25
+ import path from "node:path";
26
+ /** 磁盘余量下限(字节;缺省 100MB)。 */
27
+ export const MIN_FREE_BYTES = 100 * 1024 * 1024;
28
+ /** 待处理命令积压告警阈值(缺省 50)。 */
29
+ export const BACKLOG_ALERT_THRESHOLD = 50;
30
+ /** 证书临期告警阈值(天;缺省 14)。 */
31
+ export const CERT_EXPIRING_DAYS = 14;
32
+ /** 能力探测记录最大有效期(BUG-05;缺省 3 分钟 = 3 个健康轮询周期)。 */
33
+ export const PROBE_MAX_AGE_MS = 180_000;
34
+ export function collectMerchantHealth(deps) {
35
+ const checkedAt = (deps.now ?? (() => new Date().toISOString()))();
36
+ const minFree = deps.minFreeBytes ?? MIN_FREE_BYTES;
37
+ const processes = {
38
+ ok: deps.services.every((s) => s.running),
39
+ services: deps.services,
40
+ };
41
+ // 商品源:读能力探测落盘记录(探测动作在 mcp serve 启动/健康轮询时执行)。
42
+ // 商品源:读能力探测落盘记录(BUG-05:加新鲜度判定——超时/连接失败/HTTP
43
+ // 异常/记录过期均判 unhealthy;实时探测由周期任务执行,见 cli runtime start)。
44
+ const probePath = path.join(deps.dataDir, "capability-probe.json");
45
+ const probeMaxAgeMs = deps.probeMaxAgeMs ?? PROBE_MAX_AGE_MS;
46
+ let productSource;
47
+ if (!existsSync(probePath)) {
48
+ productSource = { ok: false, error: "无能力探测记录(capability-probe.json 不存在)" };
49
+ }
50
+ else {
51
+ try {
52
+ const probe = JSON.parse(readFileSync(probePath, "utf8"));
53
+ const probedAt = Date.parse(probe.probed_at);
54
+ const stale = !Number.isFinite(probedAt) || Date.parse(checkedAt) - probedAt > probeMaxAgeMs;
55
+ productSource = {
56
+ ok: probe.ok === true && !stale,
57
+ ...(probe.version !== undefined ? { version: probe.version } : {}),
58
+ ...(probe.error !== undefined ? { error: probe.error } : {}),
59
+ ...(stale ? { error: `能力探测记录过期(probed_at ${probe.probed_at},超过 ${probeMaxAgeMs}ms 未刷新)` } : {}),
60
+ };
61
+ }
62
+ catch (err) {
63
+ productSource = {
64
+ ok: false,
65
+ error: `能力探测记录损坏:${err instanceof Error ? err.message : String(err)}`,
66
+ };
67
+ }
68
+ }
69
+ // 状态目录可写(写探针文件即删)
70
+ let writable = false;
71
+ const probe = path.join(deps.dataDir, "runtime", ".health-probe");
72
+ try {
73
+ writeFileSync(probe, checkedAt, { mode: 0o600 });
74
+ rmSync(probe, { force: true });
75
+ writable = true;
76
+ }
77
+ catch {
78
+ writable = false;
79
+ }
80
+ let freeBytes;
81
+ try {
82
+ const fs = statfsSync(deps.dataDir);
83
+ freeBytes = fs.bavail * fs.bsize;
84
+ }
85
+ catch {
86
+ freeBytes = undefined;
87
+ }
88
+ const pending = deps.pendingCommands ?? 0;
89
+ // 备份新鲜度(审查 P2):runBackup 每次成功后落 latest-backup.json;
90
+ // 超过 maxAge(缺省 3×5min 备份周期)或从未成功 → 告警(warning,不翻转
91
+ // 整体 ok——进程/数据面健康不受备份停摆影响,但必须可见)。
92
+ const backupMaxAgeMs = deps.backupMaxAgeMs ?? 15 * 60_000;
93
+ const latestBackupPath = path.join(deps.dataDir, "backups", "latest-backup.json");
94
+ // backup 与 backlog 同为 warning 面:不翻转整体 ok(进程/数据面健康不受
95
+ // 备份停摆影响),停摆经 backup_stale 告警进入通知通道。
96
+ let backupStale;
97
+ let backupLastSuccessAt;
98
+ if (!existsSync(latestBackupPath)) {
99
+ backupStale = "尚无成功备份记录(latest-backup.json 不存在)";
100
+ }
101
+ else {
102
+ try {
103
+ const record = JSON.parse(readFileSync(latestBackupPath, "utf8"));
104
+ const at = typeof record.created_at === "string" ? record.created_at : undefined;
105
+ const age = at !== undefined ? Date.parse(checkedAt) - Date.parse(at) : Number.NaN;
106
+ if (!Number.isFinite(age)) {
107
+ backupStale = "latest-backup.json 缺少有效 created_at";
108
+ }
109
+ else if (age > backupMaxAgeMs) {
110
+ backupStale = `最近成功备份距今 ${Math.round(age / 60000)} 分钟(阈值 ${Math.round(backupMaxAgeMs / 60000)})`;
111
+ }
112
+ else if (at !== undefined) {
113
+ backupLastSuccessAt = at;
114
+ }
115
+ }
116
+ catch (err) {
117
+ backupStale = `latest-backup.json 损坏:${err instanceof Error ? err.message : String(err)}`;
118
+ }
119
+ }
120
+ const checks = {
121
+ processes,
122
+ product_source: productSource,
123
+ data_dir: { ok: writable, path: deps.dataDir, writable },
124
+ disk: {
125
+ ok: freeBytes !== undefined && freeBytes >= minFree,
126
+ ...(freeBytes !== undefined ? { free_bytes: freeBytes } : {}),
127
+ },
128
+ // registration/certificate are optional inputs, but when supplied their
129
+ // critical failures must affect health.ok rather than only producing an
130
+ // alert that callers might ignore.
131
+ registration: {
132
+ ok: deps.registration?.ok ?? true,
133
+ evaluated: deps.registration !== undefined,
134
+ ...(deps.registration?.error !== undefined ? { error: deps.registration.error } : {}),
135
+ },
136
+ // Backlog is a warning condition and should not trigger process restart.
137
+ backlog: { ok: true, pending, threshold: BACKLOG_ALERT_THRESHOLD },
138
+ backup: {
139
+ ok: true,
140
+ evaluated: true,
141
+ ...(backupLastSuccessAt !== undefined ? { last_success_at: backupLastSuccessAt } : {}),
142
+ ...(backupStale !== undefined ? { error: backupStale } : {}),
143
+ },
144
+ certificate: {
145
+ ok: deps.certDaysLeft === undefined || deps.certDaysLeft > 0,
146
+ evaluated: deps.certDaysLeft !== undefined,
147
+ ...(deps.certDaysLeft !== undefined ? { days_left: deps.certDaysLeft } : {}),
148
+ },
149
+ };
150
+ // 结构化告警(7×24;每个告警有明确 code/severity,供通知通道消费)。
151
+ const alerts = [];
152
+ for (const s of deps.services) {
153
+ if (!s.running) {
154
+ alerts.push({
155
+ code: "process_down",
156
+ severity: "critical",
157
+ message: `受管进程 ${s.name} 未运行`,
158
+ });
159
+ }
160
+ }
161
+ if (!productSource.ok) {
162
+ alerts.push({
163
+ code: "product_source_unavailable",
164
+ severity: "critical",
165
+ message: `商品源不可用:${productSource.error ?? "未知"}`,
166
+ });
167
+ }
168
+ if (deps.registration !== undefined && !deps.registration.ok) {
169
+ alerts.push({
170
+ code: "registration_invalid",
171
+ severity: "critical",
172
+ message: `catalog 注册失效:${deps.registration.error ?? "未知"}`,
173
+ });
174
+ }
175
+ if (pending > BACKLOG_ALERT_THRESHOLD) {
176
+ alerts.push({
177
+ code: "backlog",
178
+ severity: "warning",
179
+ message: `待处理命令积压 ${pending}(阈值 ${BACKLOG_ALERT_THRESHOLD})`,
180
+ });
181
+ }
182
+ if (!checks.disk.ok) {
183
+ alerts.push({ code: "disk_low", severity: "warning", message: "磁盘余量低于下限" });
184
+ }
185
+ if (!checks.backup.ok) {
186
+ alerts.push({
187
+ code: "backup_stale",
188
+ severity: "warning",
189
+ message: `备份停摆:${checks.backup.error ?? "未知"}`,
190
+ });
191
+ }
192
+ if (deps.certDaysLeft !== undefined && deps.certDaysLeft <= CERT_EXPIRING_DAYS) {
193
+ alerts.push({
194
+ code: "cert_expiring",
195
+ severity: deps.certDaysLeft <= 3 ? "critical" : "warning",
196
+ message: `TLS 证书 ${deps.certDaysLeft} 天后到期`,
197
+ });
198
+ }
199
+ return {
200
+ ok: Object.values(checks).every((c) => c.ok),
201
+ checked_at: checkedAt,
202
+ checks,
203
+ alerts,
204
+ };
205
+ }
206
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/merchant-runtime/health.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Copyright 2026 harrylabsj
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * Merchant 实例周期任务骨架(V2 阶段一:src/merchant-runtime/jobs.ts)。
18
+ *
19
+ * 本轮落地:健康轮询(health-poll,刷新 capability-probe 与健康报告)+
20
+ * 持续备份接缝(backup hook——备份实现放阶段四,本轮注册即明确报「未实现」,
21
+ * 不静默假装已备份)。任务串行执行、异常隔离(单个任务失败不影响其他任务
22
+ * 与宿主进程),错误进最近运行记录。
23
+ */
24
+ export interface MerchantJob {
25
+ name: string;
26
+ intervalMs: number;
27
+ run: () => Promise<void>;
28
+ }
29
+ export interface MerchantJobRunRecord {
30
+ name: string;
31
+ last_run_at?: string;
32
+ last_error?: string;
33
+ runs: number;
34
+ }
35
+ export declare class MerchantJobs {
36
+ private readonly jobs;
37
+ private readonly timers;
38
+ private readonly records;
39
+ /** 在途任务(审查 P2:重入保护,防长备份与健康轮询自我重叠)。 */
40
+ private readonly inFlight;
41
+ private readonly now;
42
+ private running;
43
+ constructor(options?: {
44
+ now?: () => string;
45
+ });
46
+ register(job: MerchantJob): void;
47
+ /** 立即执行一次(异常隔离:记录 last_error,不抛出)。同任务在途时跳过
48
+ * (审查 P2:与「串行执行」文档口径一致——长任务不与下一轮并发重叠)。 */
49
+ runOnce(name: string): Promise<MerchantJobRunRecord>;
50
+ /** 启动全部周期任务。 */
51
+ start(): void;
52
+ stop(): void;
53
+ list(): MerchantJobRunRecord[];
54
+ }
55
+ /**
56
+ * 标准任务集:健康轮询 + 持续备份接缝。
57
+ * backupHook 缺省 = 阶段四未实现的明确接缝(记录错误,绝不静默成功)。
58
+ */
59
+ export declare function standardMerchantJobs(deps: {
60
+ healthPoll: () => Promise<void>;
61
+ backupHook?: () => Promise<void>;
62
+ healthIntervalMs?: number;
63
+ backupIntervalMs?: number;
64
+ }): MerchantJob[];