@zqagent/core 0.2.20 → 0.3.7

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 (238) hide show
  1. package/README.md +1 -1
  2. package/README.oss.en.md +3 -3
  3. package/README.oss.md +8 -3
  4. package/README.oss.zh-TW.md +3 -3
  5. package/dist/McpClient-CEQBUPuC.d.cts +145 -0
  6. package/dist/McpClient-CEQBUPuC.d.ts +145 -0
  7. package/dist/{ToolRegistry-C3pTFiaJ.d.cts → ToolRegistry-Cy0fQxrb.d.ts} +2 -111
  8. package/dist/{ToolRegistry-C3pTFiaJ.d.ts → ToolRegistry-DouDvn9E.d.cts} +2 -111
  9. package/dist/{explore-RtZtb-V6.d.ts → TurnEngine-BPKhWuSI.d.cts} +108 -73
  10. package/dist/{explore-BgRhj6SP.d.cts → TurnEngine-DtGEuVLT.d.ts} +108 -73
  11. package/dist/conflictGuard-BBOOIxxK.d.cts +42 -0
  12. package/dist/conflictGuard-BBOOIxxK.d.ts +42 -0
  13. package/dist/index.cjs +5930 -1114
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +577 -195
  16. package/dist/index.d.ts +577 -195
  17. package/dist/index.js +5761 -979
  18. package/dist/index.js.map +1 -1
  19. package/dist/internal/crypto/secrets.cjs +25 -0
  20. package/dist/internal/crypto/secrets.cjs.map +1 -0
  21. package/dist/internal/crypto/secrets.d.cts +2 -0
  22. package/dist/internal/crypto/secrets.d.ts +2 -0
  23. package/dist/internal/crypto/secrets.js +3 -0
  24. package/dist/internal/crypto/secrets.js.map +1 -0
  25. package/dist/internal/engine/FileLogHook.cjs +179 -0
  26. package/dist/internal/engine/FileLogHook.cjs.map +1 -0
  27. package/dist/internal/engine/FileLogHook.d.cts +67 -0
  28. package/dist/internal/engine/FileLogHook.d.ts +67 -0
  29. package/dist/internal/engine/FileLogHook.js +144 -0
  30. package/dist/internal/engine/FileLogHook.js.map +1 -0
  31. package/dist/internal/engine/approvalView.cjs +25 -0
  32. package/dist/internal/engine/approvalView.cjs.map +1 -0
  33. package/dist/internal/engine/approvalView.d.cts +1 -0
  34. package/dist/internal/engine/approvalView.d.ts +1 -0
  35. package/dist/internal/engine/approvalView.js +3 -0
  36. package/dist/internal/engine/approvalView.js.map +1 -0
  37. package/dist/internal/engine/limits.cjs +59 -0
  38. package/dist/internal/engine/limits.cjs.map +1 -0
  39. package/dist/internal/engine/limits.d.cts +32 -0
  40. package/dist/internal/engine/limits.d.ts +32 -0
  41. package/dist/internal/engine/limits.js +31 -0
  42. package/dist/internal/engine/limits.js.map +1 -0
  43. package/dist/internal/engine/sessionNaming.cjs +47 -0
  44. package/dist/internal/engine/sessionNaming.cjs.map +1 -0
  45. package/dist/internal/engine/sessionNaming.d.cts +25 -0
  46. package/dist/internal/engine/sessionNaming.d.ts +25 -0
  47. package/dist/internal/engine/sessionNaming.js +20 -0
  48. package/dist/internal/engine/sessionNaming.js.map +1 -0
  49. package/dist/internal/llmConfig.cjs +561 -0
  50. package/dist/internal/llmConfig.cjs.map +1 -0
  51. package/dist/internal/llmConfig.d.cts +75 -0
  52. package/dist/internal/llmConfig.d.ts +75 -0
  53. package/dist/internal/llmConfig.js +535 -0
  54. package/dist/internal/llmConfig.js.map +1 -0
  55. package/dist/internal/mcp/builtin/index.cjs +685 -0
  56. package/dist/internal/mcp/builtin/index.cjs.map +1 -0
  57. package/dist/internal/mcp/builtin/index.d.cts +21 -0
  58. package/dist/internal/mcp/builtin/index.d.ts +21 -0
  59. package/dist/internal/mcp/builtin/index.js +652 -0
  60. package/dist/internal/mcp/builtin/index.js.map +1 -0
  61. package/dist/internal/mcp/config.cjs +278 -0
  62. package/dist/internal/mcp/config.cjs.map +1 -0
  63. package/dist/internal/mcp/config.d.cts +61 -0
  64. package/dist/internal/mcp/config.d.ts +61 -0
  65. package/dist/internal/mcp/config.js +235 -0
  66. package/dist/internal/mcp/config.js.map +1 -0
  67. package/dist/internal/mcp/presets.cjs +184 -0
  68. package/dist/internal/mcp/presets.cjs.map +1 -0
  69. package/dist/internal/mcp/presets.d.cts +39 -0
  70. package/dist/internal/mcp/presets.d.ts +39 -0
  71. package/dist/internal/mcp/presets.js +158 -0
  72. package/dist/internal/mcp/presets.js.map +1 -0
  73. package/dist/internal/node-name.cjs +45 -0
  74. package/dist/internal/node-name.cjs.map +1 -0
  75. package/dist/internal/node-name.d.cts +3 -0
  76. package/dist/internal/node-name.d.ts +3 -0
  77. package/dist/internal/node-name.js +10 -0
  78. package/dist/internal/node-name.js.map +1 -0
  79. package/dist/internal/paths.cjs +63 -0
  80. package/dist/internal/paths.cjs.map +1 -0
  81. package/dist/internal/paths.d.cts +17 -0
  82. package/dist/internal/paths.d.ts +17 -0
  83. package/dist/internal/paths.js +25 -0
  84. package/dist/internal/paths.js.map +1 -0
  85. package/dist/internal/providers/diagnostics.cjs +288 -0
  86. package/dist/internal/providers/diagnostics.cjs.map +1 -0
  87. package/dist/internal/providers/diagnostics.d.cts +103 -0
  88. package/dist/internal/providers/diagnostics.d.ts +103 -0
  89. package/dist/internal/providers/diagnostics.js +250 -0
  90. package/dist/internal/providers/diagnostics.js.map +1 -0
  91. package/dist/internal/providers/models.cjs +699 -0
  92. package/dist/internal/providers/models.cjs.map +1 -0
  93. package/dist/internal/providers/models.d.cts +136 -0
  94. package/dist/internal/providers/models.d.ts +136 -0
  95. package/dist/internal/providers/models.js +654 -0
  96. package/dist/internal/providers/models.js.map +1 -0
  97. package/dist/internal/terminal.cjs +161 -0
  98. package/dist/internal/terminal.cjs.map +1 -0
  99. package/dist/internal/terminal.d.cts +58 -0
  100. package/dist/internal/terminal.d.ts +58 -0
  101. package/dist/internal/terminal.js +128 -0
  102. package/dist/internal/terminal.js.map +1 -0
  103. package/dist/internal/tools/execAsync.cjs +187 -0
  104. package/dist/internal/tools/execAsync.cjs.map +1 -0
  105. package/dist/internal/tools/execAsync.d.cts +30 -0
  106. package/dist/internal/tools/execAsync.d.ts +30 -0
  107. package/dist/internal/tools/execAsync.js +162 -0
  108. package/dist/internal/tools/execAsync.js.map +1 -0
  109. package/dist/internal/tools/git.cjs +935 -0
  110. package/dist/internal/tools/git.cjs.map +1 -0
  111. package/dist/internal/tools/git.d.cts +206 -0
  112. package/dist/internal/tools/git.d.ts +206 -0
  113. package/dist/internal/tools/git.js +883 -0
  114. package/dist/internal/tools/git.js.map +1 -0
  115. package/dist/internal/tools/pathSafety.cjs +99 -0
  116. package/dist/internal/tools/pathSafety.cjs.map +1 -0
  117. package/dist/internal/tools/pathSafety.d.cts +44 -0
  118. package/dist/internal/tools/pathSafety.d.ts +44 -0
  119. package/dist/internal/tools/pathSafety.js +61 -0
  120. package/dist/internal/tools/pathSafety.js.map +1 -0
  121. package/dist/internal/tools/plan.cjs +146 -0
  122. package/dist/internal/tools/plan.cjs.map +1 -0
  123. package/dist/internal/tools/plan.d.cts +48 -0
  124. package/dist/internal/tools/plan.d.ts +48 -0
  125. package/dist/internal/tools/plan.js +117 -0
  126. package/dist/internal/tools/plan.js.map +1 -0
  127. package/dist/internal/tools/ripgrep.cjs +118 -0
  128. package/dist/internal/tools/ripgrep.cjs.map +1 -0
  129. package/dist/internal/tools/ripgrep.d.cts +6 -0
  130. package/dist/internal/tools/ripgrep.d.ts +6 -0
  131. package/dist/internal/tools/ripgrep.js +89 -0
  132. package/dist/internal/tools/ripgrep.js.map +1 -0
  133. package/dist/internal/tools/sandbox.cjs +253 -0
  134. package/dist/internal/tools/sandbox.cjs.map +1 -0
  135. package/dist/internal/tools/sandbox.d.cts +67 -0
  136. package/dist/internal/tools/sandbox.d.ts +67 -0
  137. package/dist/internal/tools/sandbox.js +213 -0
  138. package/dist/internal/tools/sandbox.js.map +1 -0
  139. package/dist/internal/tools/shell.cjs +771 -0
  140. package/dist/internal/tools/shell.cjs.map +1 -0
  141. package/dist/internal/tools/shell.d.cts +47 -0
  142. package/dist/internal/tools/shell.d.ts +47 -0
  143. package/dist/internal/tools/shell.js +731 -0
  144. package/dist/internal/tools/shell.js.map +1 -0
  145. package/dist/internal/tools/shellRuntime.cjs +220 -0
  146. package/dist/internal/tools/shellRuntime.cjs.map +1 -0
  147. package/dist/internal/tools/shellRuntime.d.cts +67 -0
  148. package/dist/internal/tools/shellRuntime.d.ts +67 -0
  149. package/dist/internal/tools/shellRuntime.js +181 -0
  150. package/dist/internal/tools/shellRuntime.js.map +1 -0
  151. package/dist/internal/update/controller.cjs +1035 -0
  152. package/dist/internal/update/controller.cjs.map +1 -0
  153. package/dist/internal/update/controller.d.cts +150 -0
  154. package/dist/internal/update/controller.d.ts +150 -0
  155. package/dist/internal/update/controller.js +991 -0
  156. package/dist/internal/update/controller.js.map +1 -0
  157. package/dist/internal/update/platform.cjs +234 -0
  158. package/dist/internal/update/platform.cjs.map +1 -0
  159. package/dist/internal/update/platform.d.cts +77 -0
  160. package/dist/internal/update/platform.d.ts +77 -0
  161. package/dist/internal/update/platform.js +184 -0
  162. package/dist/internal/update/platform.js.map +1 -0
  163. package/dist/tools/ask.d.cts +3 -1
  164. package/dist/tools/ask.d.ts +3 -1
  165. package/dist/tools/diff.cjs +17 -6
  166. package/dist/tools/diff.cjs.map +1 -1
  167. package/dist/tools/diff.d.cts +5 -2
  168. package/dist/tools/diff.d.ts +5 -2
  169. package/dist/tools/diff.js +17 -6
  170. package/dist/tools/diff.js.map +1 -1
  171. package/dist/tools/directories.d.cts +3 -1
  172. package/dist/tools/directories.d.ts +3 -1
  173. package/dist/tools/explore.cjs +1778 -204
  174. package/dist/tools/explore.cjs.map +1 -1
  175. package/dist/tools/explore.d.cts +82 -2
  176. package/dist/tools/explore.d.ts +82 -2
  177. package/dist/tools/explore.js +1780 -204
  178. package/dist/tools/explore.js.map +1 -1
  179. package/dist/tools/files.cjs +56 -21
  180. package/dist/tools/files.cjs.map +1 -1
  181. package/dist/tools/files.d.cts +6 -3
  182. package/dist/tools/files.d.ts +6 -3
  183. package/dist/tools/files.js +56 -21
  184. package/dist/tools/files.js.map +1 -1
  185. package/dist/tools/git.cjs +34 -25
  186. package/dist/tools/git.cjs.map +1 -1
  187. package/dist/tools/git.d.cts +4 -204
  188. package/dist/tools/git.d.ts +4 -204
  189. package/dist/tools/git.js +34 -25
  190. package/dist/tools/git.js.map +1 -1
  191. package/dist/tools/media.cjs +280 -3
  192. package/dist/tools/media.cjs.map +1 -1
  193. package/dist/tools/media.d.cts +3 -1
  194. package/dist/tools/media.d.ts +3 -1
  195. package/dist/tools/media.js +278 -1
  196. package/dist/tools/media.js.map +1 -1
  197. package/dist/tools/plan.cjs +62 -0
  198. package/dist/tools/plan.cjs.map +1 -1
  199. package/dist/tools/plan.d.cts +4 -17
  200. package/dist/tools/plan.d.ts +4 -17
  201. package/dist/tools/plan.js +59 -0
  202. package/dist/tools/plan.js.map +1 -1
  203. package/dist/tools/search.cjs +3 -0
  204. package/dist/tools/search.cjs.map +1 -1
  205. package/dist/tools/search.d.cts +3 -1
  206. package/dist/tools/search.d.ts +3 -1
  207. package/dist/tools/search.js +3 -0
  208. package/dist/tools/search.js.map +1 -1
  209. package/dist/tools/shell.cjs +76 -11
  210. package/dist/tools/shell.cjs.map +1 -1
  211. package/dist/tools/shell.d.cts +5 -39
  212. package/dist/tools/shell.d.ts +5 -39
  213. package/dist/tools/shell.js +75 -11
  214. package/dist/tools/shell.js.map +1 -1
  215. package/dist/tools/todo.d.cts +3 -1
  216. package/dist/tools/todo.d.ts +3 -1
  217. package/dist/tools/web.cjs +279 -5
  218. package/dist/tools/web.cjs.map +1 -1
  219. package/dist/tools/web.d.cts +3 -1
  220. package/dist/tools/web.d.ts +3 -1
  221. package/dist/tools/web.js +275 -3
  222. package/dist/tools/web.js.map +1 -1
  223. package/dist/types-BYyXWWPW.d.cts +237 -0
  224. package/dist/types-BYyXWWPW.d.ts +237 -0
  225. package/package.json +26 -37
  226. package/bin/zqagent +0 -2
  227. package/dist/cli/index.cjs +0 -25218
  228. package/dist/cli/index.cjs.map +0 -1
  229. package/dist/cli/index.d.cts +0 -2
  230. package/dist/cli/index.d.ts +0 -2
  231. package/dist/cli/index.js +0 -25201
  232. package/dist/cli/index.js.map +0 -1
  233. package/dist/server/index.cjs +0 -20510
  234. package/dist/server/index.cjs.map +0 -1
  235. package/dist/server/index.d.cts +0 -2
  236. package/dist/server/index.d.ts +0 -2
  237. package/dist/server/index.js +0 -20493
  238. package/dist/server/index.js.map +0 -1
@@ -0,0 +1,1035 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/update/controller.ts
31
+ var controller_exports = {};
32
+ __export(controller_exports, {
33
+ UpdateController: () => UpdateController,
34
+ evaluateManifest: () => evaluateManifest,
35
+ updaterChannel: () => updaterChannel,
36
+ updaterUrl: () => updaterUrl
37
+ });
38
+ module.exports = __toCommonJS(controller_exports);
39
+
40
+ // ../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.26_supports-color@7.2.0_tsx@4.23.12_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js
41
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
42
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
43
+
44
+ // src/update/controller.ts
45
+ var fs5 = __toESM(require("fs"), 1);
46
+ var path5 = __toESM(require("path"), 1);
47
+
48
+ // src/update/downloader.ts
49
+ var import_node_crypto2 = require("crypto");
50
+ var fs = __toESM(require("fs"), 1);
51
+ var import_node_stream = require("stream");
52
+ var import_promises = require("stream/promises");
53
+ var import_node_child_process = require("child_process");
54
+ var path = __toESM(require("path"), 1);
55
+
56
+ // src/update/version.ts
57
+ var PATTERN = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
58
+ function parseVersion(input) {
59
+ const m = PATTERN.exec(input.trim());
60
+ if (!m) return null;
61
+ const pre = m[4] ? m[4].split(".").map((part) => /^\d+$/.test(part) ? Number(part) : part) : [];
62
+ return {
63
+ major: Number(m[1]),
64
+ minor: Number(m[2]),
65
+ patch: Number(m[3]),
66
+ pre
67
+ };
68
+ }
69
+ function comparePre(a, b) {
70
+ if (a.length === 0 && b.length === 0) return 0;
71
+ if (a.length === 0) return 1;
72
+ if (b.length === 0) return -1;
73
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
74
+ const x = a[i];
75
+ const y = b[i];
76
+ if (x === void 0) return -1;
77
+ if (y === void 0) return 1;
78
+ if (typeof x === "number" && typeof y === "number") {
79
+ if (x !== y) return x < y ? -1 : 1;
80
+ } else if (typeof x === "number") {
81
+ return -1;
82
+ } else if (typeof y === "number") {
83
+ return 1;
84
+ } else if (x !== y) {
85
+ return x < y ? -1 : 1;
86
+ }
87
+ }
88
+ return 0;
89
+ }
90
+ function compareVersions(a, b) {
91
+ const va = parseVersion(a);
92
+ const vb = parseVersion(b);
93
+ if (!va || !vb) return 0;
94
+ if (va.major !== vb.major) return va.major < vb.major ? -1 : 1;
95
+ if (va.minor !== vb.minor) return va.minor < vb.minor ? -1 : 1;
96
+ if (va.patch !== vb.patch) return va.patch < vb.patch ? -1 : 1;
97
+ return comparePre(va.pre, vb.pre);
98
+ }
99
+
100
+ // src/update/manifest.ts
101
+ var PLATFORM_NAMES = {
102
+ darwin: "macos",
103
+ linux: "linux",
104
+ win32: "windows"
105
+ };
106
+ var LIBC_VARIANT = "";
107
+ function platformKey(platform = process.platform, arch = process.arch) {
108
+ const base = `${PLATFORM_NAMES[platform] ?? platform}-${arch}`;
109
+ return LIBC_VARIANT ? `${base}-${LIBC_VARIANT}` : base;
110
+ }
111
+ var SHA256_HEX = /^[0-9a-f]{64}$/;
112
+ function isRecord(v) {
113
+ return typeof v === "object" && v !== null;
114
+ }
115
+ function parseManifest(raw, opts = {}) {
116
+ if (!isRecord(raw)) return { error: "\u6E05\u5355\u4E0D\u662F JSON \u5BF9\u8C61" };
117
+ const version = typeof raw.version === "string" ? raw.version.trim() : "";
118
+ if (!parseVersion(version)) return { error: `\u6E05\u5355 version \u975E\u6CD5\uFF1A${String(raw.version)}` };
119
+ if (raw.minimumVersion !== void 0) {
120
+ if (typeof raw.minimumVersion !== "string" || !parseVersion(raw.minimumVersion)) {
121
+ return { error: `\u6E05\u5355 minimumVersion \u975E\u6CD5\uFF1A${String(raw.minimumVersion)}` };
122
+ }
123
+ }
124
+ if (!isRecord(raw.binaries)) return { error: "\u6E05\u5355\u7F3A\u5C11 binaries" };
125
+ const binaries = {};
126
+ for (const [key, value] of Object.entries(raw.binaries)) {
127
+ if (!isRecord(value)) return { error: `binaries.${key} \u4E0D\u662F\u5BF9\u8C61` };
128
+ const url = typeof value.url === "string" ? value.url.trim() : "";
129
+ const sha256 = typeof value.sha256 === "string" ? value.sha256.trim().toLowerCase() : "";
130
+ if (!url) return { error: `binaries.${key} \u7F3A\u5C11 url` };
131
+ let parsed;
132
+ try {
133
+ parsed = new URL(url);
134
+ } catch {
135
+ return { error: `binaries.${key}.url \u975E\u6CD5\uFF1A${url}` };
136
+ }
137
+ if (parsed.protocol !== "https:" && !opts.allowInsecureUrl) {
138
+ return { error: `binaries.${key}.url \u5FC5\u987B\u662F https\uFF1A${url}` };
139
+ }
140
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
141
+ return { error: `binaries.${key}.url \u534F\u8BAE\u4E0D\u652F\u6301\uFF1A${url}` };
142
+ }
143
+ if (!SHA256_HEX.test(sha256)) {
144
+ return { error: `binaries.${key}.sha256 \u9700\u4E3A 64 \u4F4D\u5C0F\u5199 hex` };
145
+ }
146
+ const size = typeof value.size === "number" && value.size > 0 ? value.size : void 0;
147
+ binaries[key] = { url, sha256, ...size ? { size } : {} };
148
+ }
149
+ if (Object.keys(binaries).length === 0) return { error: "\u6E05\u5355 binaries \u4E3A\u7A7A" };
150
+ return {
151
+ manifest: {
152
+ version,
153
+ ...typeof raw.minimumVersion === "string" ? { minimumVersion: raw.minimumVersion } : {},
154
+ ...typeof raw.notes === "string" ? { notes: raw.notes } : {},
155
+ ...typeof raw.publishedAt === "string" ? { publishedAt: raw.publishedAt } : {},
156
+ ...typeof raw.channel === "string" ? { channel: raw.channel } : {},
157
+ binaries
158
+ }
159
+ };
160
+ }
161
+
162
+ // src/update/signature.ts
163
+ var import_node_crypto = require("crypto");
164
+ function parsePublicKey(input) {
165
+ const text = input.trim();
166
+ if (!text) return null;
167
+ try {
168
+ if (text.includes("BEGIN PUBLIC KEY")) {
169
+ return (0, import_node_crypto.createPublicKey)({ key: text, format: "pem" });
170
+ }
171
+ return (0, import_node_crypto.createPublicKey)({
172
+ key: Buffer.from(text, "base64"),
173
+ format: "der",
174
+ type: "spki"
175
+ });
176
+ } catch {
177
+ return null;
178
+ }
179
+ }
180
+ function verifySignature(data, signatureBase64, publicKey) {
181
+ try {
182
+ const sig = Buffer.from(signatureBase64.trim(), "base64");
183
+ if (sig.length !== 64) return false;
184
+ return (0, import_node_crypto.verify)(null, data, publicKey, sig);
185
+ } catch {
186
+ return false;
187
+ }
188
+ }
189
+
190
+ // src/update/downloader.ts
191
+ var MAX_BINARY_BYTES = 400 * 1024 * 1024;
192
+ var MANIFEST_TIMEOUT_MS = 15e3;
193
+ var DOWNLOAD_TIMEOUT_MS = 10 * 6e4;
194
+ async function fetchManifest(url, publicKey, opts = {}) {
195
+ let raw;
196
+ let sig;
197
+ try {
198
+ const [mRes, sRes] = await Promise.all([
199
+ fetch(url, { signal: AbortSignal.timeout(MANIFEST_TIMEOUT_MS) }),
200
+ fetch(`${url}.sig`, { signal: AbortSignal.timeout(MANIFEST_TIMEOUT_MS) })
201
+ ]);
202
+ if (!mRes.ok) return { error: `\u6E05\u5355\u8BF7\u6C42\u5931\u8D25 HTTP ${mRes.status}` };
203
+ if (!sRes.ok) return { error: `\u7B7E\u540D\u8BF7\u6C42\u5931\u8D25 HTTP ${sRes.status}` };
204
+ raw = Buffer.from(await mRes.arrayBuffer());
205
+ sig = (await sRes.text()).trim();
206
+ } catch (err) {
207
+ return { error: `\u62C9\u53D6\u6E05\u5355\u5931\u8D25\uFF1A${err.message}` };
208
+ }
209
+ if (!sig) return { error: "\u7B7E\u540D\u6587\u4EF6\u4E3A\u7A7A" };
210
+ if (!verifySignature(raw, sig, publicKey)) {
211
+ return { error: "\u6E05\u5355\u7B7E\u540D\u6821\u9A8C\u5931\u8D25\uFF08\u516C\u94A5\u4E0D\u5339\u914D\u6216\u5185\u5BB9\u88AB\u7BE1\u6539\uFF09" };
212
+ }
213
+ let parsed;
214
+ try {
215
+ parsed = JSON.parse(raw.toString("utf-8"));
216
+ } catch (err) {
217
+ return { error: `\u6E05\u5355\u4E0D\u662F\u5408\u6CD5 JSON\uFF1A${err.message}` };
218
+ }
219
+ const { manifest, error } = parseManifest(parsed, opts);
220
+ if (!manifest) return { error };
221
+ return { manifest, raw };
222
+ }
223
+ async function downloadBinary(bin, destPath, log = () => {
224
+ }) {
225
+ const partPath = `${destPath}.part`;
226
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
227
+ fs.rmSync(partPath, { force: true });
228
+ let res;
229
+ try {
230
+ res = await fetch(bin.url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
231
+ } catch (err) {
232
+ return { error: `\u4E0B\u8F7D\u5931\u8D25\uFF1A${err.message}` };
233
+ }
234
+ if (!res.ok) return { error: `\u4E0B\u8F7D\u5931\u8D25 HTTP ${res.status}` };
235
+ if (!res.body) return { error: "\u4E0B\u8F7D\u54CD\u5E94\u6CA1\u6709 body" };
236
+ const expected = bin.size && bin.size > 0 ? bin.size : void 0;
237
+ const limit = expected ? Math.max(expected * 2, 1024 * 1024) : MAX_BINARY_BYTES;
238
+ const hash = (0, import_node_crypto2.createHash)("sha256");
239
+ let bytes = 0;
240
+ try {
241
+ const source = import_node_stream.Readable.fromWeb(res.body);
242
+ await (0, import_promises.pipeline)(
243
+ source,
244
+ async function* (chunks) {
245
+ for await (const chunk of chunks) {
246
+ bytes += chunk.length;
247
+ if (bytes > limit) throw new Error(`\u4EA7\u7269\u8D85\u8FC7\u5927\u5C0F\u4E0A\u9650\uFF08>${limit} \u5B57\u8282\uFF09`);
248
+ hash.update(chunk);
249
+ yield chunk;
250
+ }
251
+ },
252
+ fs.createWriteStream(partPath, { mode: 493 })
253
+ );
254
+ } catch (err) {
255
+ fs.rmSync(partPath, { force: true });
256
+ return { error: `\u4E0B\u8F7D\u4E2D\u65AD\uFF1A${err.message}` };
257
+ }
258
+ const actual = hash.digest("hex");
259
+ if (actual !== bin.sha256) {
260
+ fs.rmSync(partPath, { force: true });
261
+ return { error: `SHA-256 \u4E0D\u5339\u914D\uFF08\u671F\u671B ${bin.sha256.slice(0, 12)}\u2026\uFF0C\u5B9E\u9645 ${actual.slice(0, 12)}\u2026\uFF09` };
262
+ }
263
+ if (expected && bytes !== expected) {
264
+ fs.rmSync(partPath, { force: true });
265
+ return { error: `\u4EA7\u7269\u5927\u5C0F\u4E0D\u5339\u914D\uFF08\u671F\u671B ${expected}\uFF0C\u5B9E\u9645 ${bytes}\uFF09` };
266
+ }
267
+ fs.rmSync(destPath, { force: true });
268
+ fs.renameSync(partPath, destPath);
269
+ fs.chmodSync(destPath, 493);
270
+ log(`\u5DF2\u4E0B\u8F7D ${path.basename(destPath)}\uFF08${Math.round(bytes / 1024 / 1024)}MB\uFF0C\u6821\u9A8C\u901A\u8FC7\uFF09`);
271
+ return { bytes };
272
+ }
273
+ function selfCheck(binPath, expectedVersion) {
274
+ try {
275
+ const r = (0, import_node_child_process.spawnSync)(binPath, ["--version"], {
276
+ timeout: 2e4,
277
+ encoding: "utf-8",
278
+ env: { ...process.env, ZQAGENT_AUTO_UPDATE: "0" }
279
+ });
280
+ if (r.error) return { ok: false, error: `\u6267\u884C\u81EA\u68C0\u5931\u8D25\uFF1A${r.error.message}` };
281
+ if (r.status !== 0) return { ok: false, error: `\u81EA\u68C0\u9000\u51FA\u7801 ${r.status}\uFF1A${(r.stderr || "").trim()}` };
282
+ const out = `${r.stdout ?? ""}${r.stderr ?? ""}`;
283
+ if (!out.includes(expectedVersion)) {
284
+ return { ok: false, error: `\u81EA\u68C0\u7248\u672C\u4E0D\u7B26\uFF08\u671F\u671B ${expectedVersion}\uFF0C\u8F93\u51FA\uFF1A${out.trim().slice(0, 120)}\uFF09` };
285
+ }
286
+ return { ok: true };
287
+ } catch (err) {
288
+ return { ok: false, error: `\u81EA\u68C0\u5F02\u5E38\uFF1A${err.message}` };
289
+ }
290
+ }
291
+
292
+ // src/update/platform.ts
293
+ var fs2 = __toESM(require("fs"), 1);
294
+ var os = __toESM(require("os"), 1);
295
+ var path2 = __toESM(require("path"), 1);
296
+ var import_node_child_process2 = require("child_process");
297
+ var import_node_module = require("module");
298
+ function currentVersion() {
299
+ const injected = "0.3.7"?.trim();
300
+ if (injected) return injected;
301
+ try {
302
+ const req = (0, import_node_module.createRequire)(importMetaUrl);
303
+ const pkg = req("../../package.json");
304
+ if (pkg?.version) return pkg.version;
305
+ } catch {
306
+ }
307
+ return "dev";
308
+ }
309
+ function isSeaRuntime() {
310
+ if (process.env.ZQAGENT_SEA === "1") return true;
311
+ const base = path2.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
312
+ return base !== "node" && base !== "nodejs";
313
+ }
314
+ var DEFAULT_SERVICE_NAME = "zqagent";
315
+ var DEFAULT_SERVICE_LABEL = "com.zqagent.node";
316
+ function serviceManagerFromCgroup(cgroup, serviceName = DEFAULT_SERVICE_NAME) {
317
+ const unitName = `${serviceName}.service`;
318
+ for (const line of cgroup.split("\n")) {
319
+ const idx = line.lastIndexOf(":");
320
+ if (idx < 0) continue;
321
+ const unitPath = line.slice(idx + 1).trim();
322
+ if (unitPath.split("/").includes(unitName)) return "systemd";
323
+ }
324
+ return null;
325
+ }
326
+ function launchdDomain() {
327
+ const uid = typeof process.getuid === "function" ? process.getuid() : -1;
328
+ return uid === 0 ? "system" : `gui/${uid}`;
329
+ }
330
+ function launchdLabel() {
331
+ return process.env.ZQAGENT_SERVICE_LABEL?.trim() || DEFAULT_SERVICE_LABEL;
332
+ }
333
+ function launchdPlistPath() {
334
+ const label = launchdLabel();
335
+ const uid = typeof process.getuid === "function" ? process.getuid() : -1;
336
+ if (uid === 0) return `/Library/LaunchDaemons/${label}.plist`;
337
+ return path2.join(os.homedir(), "Library", "LaunchAgents", `${label}.plist`);
338
+ }
339
+ function serviceManagerFromLaunchctl(output, pid, execPath, sea = true) {
340
+ const pidMatch = /^\s*pid = (\d+)\s*$/m.exec(output);
341
+ if (pidMatch && Number(pidMatch[1]) === pid) return "launchd";
342
+ const program = /^\s*program = (.+?)\s*$/m.exec(output);
343
+ if (program) {
344
+ const shown = program[1].replace(/^"|"$/g, "");
345
+ if (path2.resolve(shown) === path2.resolve(execPath)) return "launchd";
346
+ }
347
+ if (!sea && /^\s*state = /m.test(output)) return "launchd";
348
+ return null;
349
+ }
350
+ function parseLaunchdEnvironment(xml) {
351
+ const out = {};
352
+ const marker = xml.indexOf("<key>EnvironmentVariables</key>");
353
+ if (marker < 0) return out;
354
+ const open = xml.indexOf("<dict>", marker);
355
+ if (open < 0) return out;
356
+ const close = xml.indexOf("</dict>", open);
357
+ if (close < 0) return out;
358
+ const body = xml.slice(open + "<dict>".length, close);
359
+ for (const m of body.matchAll(/<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g)) {
360
+ out[m[1]] = m[2];
361
+ }
362
+ return out;
363
+ }
364
+ function detectServiceManager() {
365
+ if (process.platform === "darwin") return detectLaunchd();
366
+ if (process.platform !== "linux") return null;
367
+ const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
368
+ try {
369
+ return serviceManagerFromCgroup(
370
+ fs2.readFileSync("/proc/self/cgroup", "utf8"),
371
+ serviceName
372
+ );
373
+ } catch {
374
+ return null;
375
+ }
376
+ }
377
+ function detectLaunchd() {
378
+ try {
379
+ const res = (0, import_node_child_process2.spawnSync)(
380
+ "launchctl",
381
+ ["print", `${launchdDomain()}/${launchdLabel()}`],
382
+ { encoding: "utf8", timeout: 3e3 }
383
+ );
384
+ if (res.status !== 0 || !res.stdout) return null;
385
+ return serviceManagerFromLaunchctl(
386
+ res.stdout,
387
+ process.pid,
388
+ process.execPath,
389
+ isSeaRuntime()
390
+ );
391
+ } catch {
392
+ return null;
393
+ }
394
+ }
395
+ function serviceManager() {
396
+ const v = process.env.ZQAGENT_SERVICE_MANAGER?.trim().toLowerCase();
397
+ if (v === "systemd" || v === "launchd") return v;
398
+ return detectServiceManager();
399
+ }
400
+ function parseServiceEnvironment(raw) {
401
+ const out = {};
402
+ for (const token of raw.trim().split(/\s+/)) {
403
+ const idx = token.indexOf("=");
404
+ if (idx > 0) out[token.slice(0, idx)] = token.slice(idx + 1);
405
+ }
406
+ return out;
407
+ }
408
+ var cachedServiceEnv = null;
409
+ function readServiceEnvironment() {
410
+ const serviceName = process.env.ZQAGENT_SERVICE_NAME?.trim() || DEFAULT_SERVICE_NAME;
411
+ try {
412
+ const res = (0, import_node_child_process2.spawnSync)(
413
+ "systemctl",
414
+ ["show", serviceName, "--property=Environment", "--value", "--no-pager"],
415
+ { encoding: "utf8", timeout: 3e3 }
416
+ );
417
+ if (res.status !== 0 || !res.stdout) return {};
418
+ return parseServiceEnvironment(res.stdout);
419
+ } catch {
420
+ return {};
421
+ }
422
+ }
423
+ function serviceEnvValue(key) {
424
+ if (process.platform === "darwin") {
425
+ if (serviceManager() !== "launchd") return void 0;
426
+ cachedLaunchdEnv ??= readLaunchdEnvironment();
427
+ return cachedLaunchdEnv[key];
428
+ }
429
+ if (process.platform !== "linux") return void 0;
430
+ if (serviceManager() !== "systemd") return void 0;
431
+ cachedServiceEnv ??= readServiceEnvironment();
432
+ return cachedServiceEnv[key];
433
+ }
434
+ var cachedLaunchdEnv = null;
435
+ function readLaunchdEnvironment() {
436
+ try {
437
+ return parseLaunchdEnvironment(fs2.readFileSync(launchdPlistPath(), "utf8"));
438
+ } catch {
439
+ return {};
440
+ }
441
+ }
442
+ function selfUpdateCapability(hasPublicKey) {
443
+ if (!isSeaRuntime()) {
444
+ return {
445
+ ok: false,
446
+ reason: "\u5F53\u524D\u4E0D\u662F\u5355\u6587\u4EF6\uFF08SEA\uFF09\u5F62\u6001\uFF1A\u8BF7\u7528 npm \u5347\u7EA7\uFF08npm i -g @zqagent/cli@latest\uFF09"
447
+ };
448
+ }
449
+ if (!serviceManager()) {
450
+ return {
451
+ ok: false,
452
+ reason: "\u672A\u88AB systemd/launchd \u6258\u7BA1\uFF1A\u8BF7\u7528 deploy/service/install.sh \u5B89\u88C5\uFF0C\u6216\u624B\u52A8\u5347\u7EA7"
453
+ };
454
+ }
455
+ if (!hasPublicKey) {
456
+ return { ok: false, reason: "\u672A\u914D\u7F6E ZQAGENT_UPDATE_PUBKEY\uFF0C\u65E0\u6CD5\u9A8C\u7B7E\uFF0C\u5DF2\u7981\u7528\u81EA\u52A8\u66F4\u65B0" };
457
+ }
458
+ return { ok: true };
459
+ }
460
+
461
+ // src/update/installer.ts
462
+ var fs4 = __toESM(require("fs"), 1);
463
+ var path4 = __toESM(require("path"), 1);
464
+
465
+ // src/update/state.ts
466
+ var fs3 = __toESM(require("fs"), 1);
467
+ var path3 = __toESM(require("path"), 1);
468
+ function updatesDir(dataDir) {
469
+ return path3.join(dataDir, "updates");
470
+ }
471
+ function statePath(dataDir) {
472
+ return path3.join(updatesDir(dataDir), "state.json");
473
+ }
474
+ function loadState(dataDir) {
475
+ try {
476
+ const raw = fs3.readFileSync(statePath(dataDir), "utf-8");
477
+ const parsed = JSON.parse(raw);
478
+ if (typeof parsed === "object" && parsed !== null) return parsed;
479
+ } catch {
480
+ }
481
+ return {};
482
+ }
483
+ function saveState(dataDir, state) {
484
+ const dir = updatesDir(dataDir);
485
+ fs3.mkdirSync(dir, { recursive: true });
486
+ const file = statePath(dataDir);
487
+ const tmp = `${file}.tmp`;
488
+ fs3.writeFileSync(tmp, JSON.stringify(state, null, 2), "utf-8");
489
+ fs3.renameSync(tmp, file);
490
+ }
491
+ function patchState(dataDir, patch) {
492
+ const next = { ...loadState(dataDir), ...patch };
493
+ saveState(dataDir, next);
494
+ return next;
495
+ }
496
+
497
+ // src/update/installer.ts
498
+ function backupDir(dataDir) {
499
+ return path4.join(updatesDir(dataDir), "backup");
500
+ }
501
+ function backupCurrent(dataDir, execPath, version) {
502
+ const dir = backupDir(dataDir);
503
+ fs4.mkdirSync(dir, { recursive: true });
504
+ const target = path4.join(dir, `zqagent-${version}${process.platform === "win32" ? ".exe" : ""}`);
505
+ fs4.copyFileSync(execPath, target);
506
+ fs4.chmodSync(target, 493);
507
+ return target;
508
+ }
509
+ function replaceExecutable(execPath, stagedPath) {
510
+ if (process.platform === "win32") {
511
+ const oldPath = `${execPath}.old`;
512
+ fs4.rmSync(oldPath, { force: true });
513
+ fs4.renameSync(execPath, oldPath);
514
+ fs4.copyFileSync(stagedPath, execPath);
515
+ fs4.chmodSync(execPath, 493);
516
+ return;
517
+ }
518
+ const tmp = `${execPath}.new`;
519
+ fs4.rmSync(tmp, { force: true });
520
+ try {
521
+ fs4.copyFileSync(stagedPath, tmp);
522
+ fs4.chmodSync(tmp, 493);
523
+ fs4.renameSync(tmp, execPath);
524
+ } catch (err) {
525
+ fs4.rmSync(tmp, { force: true });
526
+ throw err;
527
+ }
528
+ fs4.rmSync(stagedPath, { force: true });
529
+ }
530
+ function installStaged(opts) {
531
+ const log = opts.log ?? (() => {
532
+ });
533
+ const from = currentVersion();
534
+ log(`\u6B63\u5728\u5B89\u88C5 v${opts.version}\uFF08\u5F53\u524D v${from}\uFF09`);
535
+ const backupPath = backupCurrent(opts.dataDir, opts.execPath, from);
536
+ log(`\u5DF2\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5230 ${path4.basename(backupPath)}`);
537
+ replaceExecutable(opts.execPath, opts.stagedPath);
538
+ return patchState(opts.dataDir, {
539
+ stagedVersion: void 0,
540
+ stagedPath: void 0,
541
+ pendingVersion: opts.version,
542
+ previousVersion: from,
543
+ backupVersion: from,
544
+ backupPath,
545
+ installedExecPath: opts.execPath,
546
+ lastError: void 0,
547
+ failureCount: 0,
548
+ failedVersion: void 0
549
+ });
550
+ }
551
+ function rollback(dataDir, execPath, log = () => {
552
+ }) {
553
+ const state = readRollbackTarget(dataDir);
554
+ if (!state?.backupPath || !state.backupVersion) return null;
555
+ if (!fs4.existsSync(state.backupPath)) return null;
556
+ log(`\u6B63\u5728\u56DE\u6EDA\u5230 v${state.backupVersion}`);
557
+ if (process.platform === "win32") {
558
+ fs4.rmSync(`${execPath}.old`, { force: true });
559
+ fs4.renameSync(execPath, `${execPath}.old`);
560
+ fs4.copyFileSync(state.backupPath, execPath);
561
+ fs4.chmodSync(execPath, 493);
562
+ } else {
563
+ const tmp = `${execPath}.rollback`;
564
+ fs4.copyFileSync(state.backupPath, tmp);
565
+ fs4.chmodSync(tmp, 493);
566
+ fs4.renameSync(tmp, execPath);
567
+ }
568
+ patchState(dataDir, {
569
+ pendingVersion: state.backupVersion,
570
+ previousVersion: currentVersion(),
571
+ // 不让自动更新立刻把刚回滚掉的版本再装回来
572
+ skippedVersion: currentVersion(),
573
+ backupPath: void 0,
574
+ backupVersion: void 0
575
+ });
576
+ return state.backupVersion;
577
+ }
578
+ function readRollbackTarget(dataDir) {
579
+ try {
580
+ const raw = fs4.readFileSync(path4.join(updatesDir(dataDir), "state.json"), "utf-8");
581
+ const s = JSON.parse(raw);
582
+ if (s.backupPath && s.backupVersion) return s;
583
+ } catch {
584
+ }
585
+ return null;
586
+ }
587
+
588
+ // src/update/controller.ts
589
+ var DEFAULT_BASE = "https://agent.cd-zq.com/downloads/zqagent";
590
+ function env(key) {
591
+ const v = process.env[key]?.trim();
592
+ if (v) return v;
593
+ const fromService = serviceEnvValue(key)?.trim();
594
+ return fromService ? fromService : void 0;
595
+ }
596
+ function num(key, fallback) {
597
+ const n = Number(env(key));
598
+ return Number.isFinite(n) && n >= 0 ? n : fallback;
599
+ }
600
+ function updaterChannel() {
601
+ return env("ZQAGENT_UPDATE_CHANNEL") ?? "stable";
602
+ }
603
+ function updaterUrl(channel = updaterChannel()) {
604
+ return env("ZQAGENT_UPDATE_URL") ?? `${DEFAULT_BASE}/${channel}.json`;
605
+ }
606
+ function nextIntervalMs() {
607
+ const base = num("ZQAGENT_UPDATE_INTERVAL_MS", 6 * 60 * 60 * 1e3);
608
+ const jitter = base * 0.15 * (Math.random() * 2 - 1);
609
+ return Math.max(6e4, Math.round(base + jitter));
610
+ }
611
+ function evaluateManifest(base, manifest) {
612
+ if (!parseVersion(base.currentVersion) || compareVersions(manifest.version, base.currentVersion) <= 0) {
613
+ return {
614
+ ...base,
615
+ updateAvailable: false,
616
+ latestVersion: manifest.version,
617
+ reason: `\u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF08v${base.currentVersion}\uFF09`
618
+ };
619
+ }
620
+ if (manifest.minimumVersion && compareVersions(base.currentVersion, manifest.minimumVersion) < 0) {
621
+ return {
622
+ ...base,
623
+ updateAvailable: false,
624
+ latestVersion: manifest.version,
625
+ manualUpgradeRequired: manifest.minimumVersion,
626
+ reason: `\u5F53\u524D\u7248\u672C\u8FC7\u4F4E\uFF0C\u9700\u624B\u52A8\u5347\u7EA7\u5230 >= ${manifest.minimumVersion}`
627
+ };
628
+ }
629
+ return {
630
+ ...base,
631
+ updateAvailable: true,
632
+ latestVersion: manifest.version,
633
+ ...manifest.notes ? { notes: manifest.notes } : {}
634
+ };
635
+ }
636
+ var UpdateController = class {
637
+ constructor(opts, currentVersion2 = currentVersion()) {
638
+ this.opts = opts;
639
+ this.currentVersion = currentVersion2;
640
+ this.log = opts.log ?? ((msg) => console.log(`[update] ${msg}`));
641
+ }
642
+ opts;
643
+ currentVersion;
644
+ log;
645
+ timer = null;
646
+ stopped = false;
647
+ checking = false;
648
+ installing = false;
649
+ /**
650
+ * 进行中的「等空闲」被要求立刻执行。
651
+ *
652
+ * 用户点「立即更新」时若已有一个在等空闲的任务,不能回一句「已有任务在进行中」
653
+ * 就完事——那个任务可能正因为某个 running 会话而永远等下去。置位后
654
+ * `waitForIdle` 下一拍直接放行。
655
+ */
656
+ forceRequested = false;
657
+ startedAt = Date.now();
658
+ lastCheckAt;
659
+ /** 公钥(未配置时禁用自更新:无法验签就不能替换可执行文件) */
660
+ get publicKey() {
661
+ return parsePublicKey(env("ZQAGENT_UPDATE_PUBKEY") ?? "");
662
+ }
663
+ get autoUpdateOff() {
664
+ return env("ZQAGENT_AUTO_UPDATE") === "0";
665
+ }
666
+ get enabled() {
667
+ return !this.autoUpdateOff && selfUpdateCapability(Boolean(this.publicKey)).ok;
668
+ }
669
+ manifestUrl() {
670
+ return this.opts.url ?? updaterUrl();
671
+ }
672
+ /** 启动调度并核对上次更新结果 */
673
+ start() {
674
+ this.reconcile();
675
+ if (this.autoUpdateOff) {
676
+ this.log("\u81EA\u52A8\u66F4\u65B0\u5DF2\u5173\u95ED\uFF08ZQAGENT_AUTO_UPDATE=0\uFF09");
677
+ return;
678
+ }
679
+ const cap = selfUpdateCapability(Boolean(this.publicKey));
680
+ if (!cap.ok) {
681
+ this.log(`\u81EA\u52A8\u66F4\u65B0\u672A\u542F\u7528\uFF1A${cap.reason}`);
682
+ return;
683
+ }
684
+ this.log(
685
+ `\u81EA\u52A8\u66F4\u65B0\u5DF2\u542F\u7528\uFF08v${this.currentVersion}\uFF0C\u901A\u9053 ${updaterChannel()}\uFF0C\u7531 ${serviceManager()} \u6258\u7BA1\uFF09`
686
+ );
687
+ this.schedule(num("ZQAGENT_UPDATE_INITIAL_DELAY_MS", 5 * 60 * 1e3));
688
+ }
689
+ stop() {
690
+ this.stopped = true;
691
+ if (this.timer) clearTimeout(this.timer);
692
+ this.timer = null;
693
+ }
694
+ /** 启动时对账:确认上次更新是否成功、清理失效的暂存/备份状态 */
695
+ reconcile() {
696
+ const state = loadState(this.opts.dataDir);
697
+ const current = this.currentVersion;
698
+ if (state.stagedPath && !fs5.existsSync(state.stagedPath)) {
699
+ patchState(this.opts.dataDir, { stagedVersion: void 0, stagedPath: void 0 });
700
+ }
701
+ const patch = { runningVersion: current };
702
+ if (state.pendingVersion && state.pendingVersion === current) {
703
+ patch.pendingVersion = void 0;
704
+ this.log(`\u5DF2\u66F4\u65B0\u5230 v${current}\uFF08\u6B64\u524D v${state.previousVersion ?? "?"}\uFF09`);
705
+ } else if (state.pendingVersion) {
706
+ this.log(
707
+ `\u6CE8\u610F\uFF1A\u4E0A\u6B21\u5B89\u88C5 v${state.pendingVersion} \u672A\u751F\u6548\uFF0C\u5F53\u524D\u4ECD\u4E3A v${current}` + (state.backupPath ? "\uFF1B\u53EF\u6267\u884C update --rollback \u6062\u590D\u5907\u4EFD" : "")
708
+ );
709
+ patch.pendingVersion = void 0;
710
+ }
711
+ patchState(this.opts.dataDir, patch);
712
+ if (process.platform === "win32") {
713
+ fs5.rmSync(`${process.execPath}.old`, { force: true });
714
+ }
715
+ }
716
+ schedule(delayMs) {
717
+ if (this.stopped) return;
718
+ this.timer = setTimeout(() => {
719
+ this.timer = null;
720
+ void this.tick();
721
+ }, delayMs);
722
+ this.timer.unref();
723
+ }
724
+ async tick() {
725
+ try {
726
+ await this.check();
727
+ } catch (err) {
728
+ this.log(`\u68C0\u67E5\u66F4\u65B0\u5931\u8D25\uFF1A${err.message}`);
729
+ }
730
+ this.schedule(nextIntervalMs());
731
+ }
732
+ /**
733
+ * 只读检查:拉清单比对版本,不下载、不改 state。
734
+ *
735
+ * 供远端 `self_update --action check` 与 UI 展示用;与定时器 `check()` 不同,
736
+ * 这里不加 `checking` 锁、不写 `lastCheckAt`,因此不会干扰自动更新流程。
737
+ */
738
+ async inspect() {
739
+ const base = {
740
+ currentVersion: this.currentVersion,
741
+ channel: updaterChannel(),
742
+ manifestUrl: this.manifestUrl()
743
+ };
744
+ const key = this.publicKey;
745
+ const cap = selfUpdateCapability(Boolean(key));
746
+ if (!cap.ok) {
747
+ return { ...base, updateAvailable: false, reason: `\u81EA\u66F4\u65B0\u4E0D\u53EF\u7528\uFF1A${cap.reason}` };
748
+ }
749
+ const res = await fetchManifest(base.manifestUrl, key, {
750
+ allowInsecureUrl: env("ZQAGENT_UPDATE_INSECURE") === "1"
751
+ });
752
+ if (!res.manifest) {
753
+ return {
754
+ ...base,
755
+ updateAvailable: false,
756
+ reason: `\u66F4\u65B0\u68C0\u67E5\u5931\u8D25\uFF1A${res.error ?? "\u6E05\u5355\u6821\u9A8C\u5931\u8D25"}`
757
+ };
758
+ }
759
+ return evaluateManifest(base, res.manifest);
760
+ }
761
+ /**
762
+ * 远端触发安装:先同步给出结论(受理/无需更新),安装本身在后台跑。
763
+ *
764
+ * 必须「先回执、后安装」:安装成功后进程会 `exit(70)` 交给 systemd 拉起新版本,
765
+ * 如果等到安装完成再回执,回执永远发不出去(表现为网关超时)。
766
+ *
767
+ * 复用定时器同一条 `check()` 路径:验签、下载、自检、失败计数、安装后自检与
768
+ * 回滚保护全都一致,只是把 `installMode` 设为 `force` 时不等待空闲。
769
+ */
770
+ async requestInstall(force = false, opts = {}) {
771
+ const fromVersion = this.currentVersion;
772
+ const cap = selfUpdateCapability(Boolean(this.publicKey));
773
+ if (!cap.ok) {
774
+ return { accepted: false, fromVersion, message: `\u81EA\u66F4\u65B0\u4E0D\u53EF\u7528\uFF1A${cap.reason}` };
775
+ }
776
+ const info = await this.inspect();
777
+ if (!info.updateAvailable) {
778
+ return {
779
+ accepted: false,
780
+ fromVersion,
781
+ ...info.latestVersion ? { toVersion: info.latestVersion } : {},
782
+ message: info.reason ?? "\u5DF2\u662F\u6700\u65B0\u7248\u672C"
783
+ };
784
+ }
785
+ if (this.checking || this.installing) {
786
+ if (force) this.forceRequested = true;
787
+ return {
788
+ accepted: true,
789
+ fromVersion,
790
+ toVersion: info.latestVersion,
791
+ message: force ? `\u5DF2\u8BF7\u6C42\u7ACB\u5373\u5B89\u88C5 v${info.latestVersion}\uFF08\u6253\u65AD\u6B64\u524D\u7684\u7B49\u5F85\uFF09` : `\u5DF2\u6709\u66F4\u65B0\u4EFB\u52A1\u5728\u8FDB\u884C\u4E2D\uFF08\u76EE\u6807 v${info.latestVersion}\uFF09\uFF0C\u53EF\u5728\u7A0D\u540E\u7528 status \u67E5\u770B\u7ED3\u679C`
792
+ };
793
+ }
794
+ void this.check(force, false, true, {
795
+ ...opts,
796
+ // 用户显式请求:跳过「启动满 10 分钟」这道只为自动更新设的闸门
797
+ requireUptime: false
798
+ }).catch((err) => {
799
+ this.recordFailure(info.latestVersion, `\u5B89\u88C5\u5931\u8D25\uFF1A${err.message}`);
800
+ });
801
+ return {
802
+ accepted: true,
803
+ fromVersion,
804
+ toVersion: info.latestVersion,
805
+ message: force ? `\u5F00\u59CB\u5B89\u88C5 v${info.latestVersion}\uFF08\u7ACB\u5373\u91CD\u542F\uFF09` : `\u5F00\u59CB\u5B89\u88C5 v${info.latestVersion}\uFF08\u7B49\u5F85\u8282\u70B9\u7A7A\u95F2\u540E\u91CD\u542F\uFF09`
806
+ };
807
+ }
808
+ /**
809
+ * 检查并准备更新;`installMode` 为 true 时下载后立即安装(不再等空闲)。
810
+ * 返回可读结论,供 CLI `update --check` 使用。
811
+ */
812
+ async check(installMode = false, dryRun = false, ignoreSkip = false, ctx = {}) {
813
+ const key = this.publicKey;
814
+ const cap = selfUpdateCapability(Boolean(key));
815
+ if (!cap.ok) return `\u672A\u542F\u7528\u81EA\u52A8\u66F4\u65B0\uFF1A${cap.reason}`;
816
+ if (this.checking) return "\u5DF2\u6709\u68C0\u67E5\u5728\u8FDB\u884C\u4E2D";
817
+ this.checking = true;
818
+ try {
819
+ const current = this.currentVersion;
820
+ const url = this.manifestUrl();
821
+ this.log(`\u68C0\u67E5\u66F4\u65B0\uFF1A${url}`);
822
+ const res = await fetchManifest(url, key, {
823
+ allowInsecureUrl: env("ZQAGENT_UPDATE_INSECURE") === "1"
824
+ });
825
+ if (!res.manifest) {
826
+ this.recordFailure(void 0, res.error ?? "\u6E05\u5355\u6821\u9A8C\u5931\u8D25");
827
+ return `\u66F4\u65B0\u68C0\u67E5\u5931\u8D25\uFF1A${res.error}`;
828
+ }
829
+ const manifest = res.manifest;
830
+ this.lastCheckAt = (/* @__PURE__ */ new Date()).toISOString();
831
+ patchState(this.opts.dataDir, { lastCheckAt: this.lastCheckAt });
832
+ if (!parseVersion(current) || compareVersions(manifest.version, current) <= 0) {
833
+ patchState(this.opts.dataDir, {
834
+ stagedVersion: void 0,
835
+ stagedPath: void 0,
836
+ lastError: void 0
837
+ });
838
+ this.log(`\u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF08v${current}\uFF09`);
839
+ return `\u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF08v${current}\uFF09`;
840
+ }
841
+ if (manifest.minimumVersion && compareVersions(current, manifest.minimumVersion) < 0) {
842
+ this.recordFailure(manifest.version, `\u5F53\u524D\u7248\u672C\u8FC7\u4F4E\uFF0C\u9700\u624B\u52A8\u5347\u7EA7\u5230 >= ${manifest.minimumVersion}`);
843
+ return `\u9700\u8981\u624B\u52A8\u5347\u7EA7\uFF1A\u5F53\u524D v${current} \u4F4E\u4E8E\u6700\u4F4E\u8981\u6C42 v${manifest.minimumVersion}`;
844
+ }
845
+ if (dryRun) {
846
+ const notes = manifest.notes ? `\uFF1A${manifest.notes}` : "";
847
+ return `\u53D1\u73B0\u65B0\u7248\u672C v${manifest.version}\uFF08\u5F53\u524D v${current}\uFF09${notes}`;
848
+ }
849
+ const state = loadState(this.opts.dataDir);
850
+ if (!ignoreSkip && state.skippedVersion === manifest.version) {
851
+ const msg = `v${manifest.version} \u5DF2\u88AB\u56DE\u6EDA\uFF0C\u81EA\u52A8\u66F4\u65B0\u8DF3\u8FC7\uFF08\u624B\u52A8 update --now \u53EF\u5F3A\u5236\u5B89\u88C5\uFF09`;
852
+ this.log(msg);
853
+ return msg;
854
+ }
855
+ if (state.skippedVersion && compareVersions(manifest.version, state.skippedVersion) > 0) {
856
+ patchState(this.opts.dataDir, { skippedVersion: void 0 });
857
+ }
858
+ const maxFailures = num("ZQAGENT_UPDATE_MAX_FAILURES", 3);
859
+ if (state.failedVersion === manifest.version && (state.failureCount ?? 0) >= maxFailures) {
860
+ const msg = `v${manifest.version} \u5DF2\u8FDE\u7EED\u5931\u8D25 ${state.failureCount} \u6B21\uFF0C\u8DF3\u8FC7\uFF08\u7B49\u5F85\u4E0B\u4E00\u7248\u672C\uFF09`;
861
+ this.log(msg);
862
+ return msg;
863
+ }
864
+ const platform = platformKey();
865
+ const bin = manifest.binaries[platform];
866
+ if (!bin) {
867
+ const available = Object.keys(manifest.binaries).join(", ") || "\uFF08\u7A7A\uFF09";
868
+ const msg = `\u6E05\u5355\u6CA1\u6709\u5F53\u524D\u5E73\u53F0\uFF08${platform}\uFF09\u7684\u4EA7\u7269\uFF1B\u6E05\u5355\u63D0\u4F9B\uFF1A${available}`;
869
+ this.log(msg);
870
+ return msg;
871
+ }
872
+ let stagedPath = state.stagedPath;
873
+ if (!(state.stagedVersion === manifest.version && stagedPath && fs5.existsSync(stagedPath))) {
874
+ stagedPath = path5.join(
875
+ updatesDir(this.opts.dataDir),
876
+ "staged",
877
+ `zqagent-${manifest.version}${process.platform === "win32" ? ".exe" : ""}`
878
+ );
879
+ const dl = await downloadBinary(bin, stagedPath, this.log);
880
+ if (dl.error) {
881
+ this.recordFailure(manifest.version, dl.error);
882
+ return `\u4E0B\u8F7D\u5931\u8D25\uFF1A${dl.error}`;
883
+ }
884
+ const selfTest = selfCheck(stagedPath, manifest.version);
885
+ if (!selfTest.ok) {
886
+ fs5.rmSync(stagedPath, { force: true });
887
+ this.recordFailure(manifest.version, selfTest.error ?? "\u81EA\u68C0\u5931\u8D25");
888
+ return `\u5B89\u88C5\u5305\u81EA\u68C0\u5931\u8D25\uFF1A${selfTest.error}`;
889
+ }
890
+ patchState(this.opts.dataDir, {
891
+ stagedVersion: manifest.version,
892
+ stagedPath,
893
+ lastError: void 0,
894
+ failureCount: 0,
895
+ failedVersion: void 0
896
+ });
897
+ }
898
+ this.log(
899
+ `v${manifest.version} \u5DF2\u5C31\u7EEA${manifest.notes ? `\uFF1A${manifest.notes}` : ""}`
900
+ );
901
+ await this.installWhenIdle(manifest.version, stagedPath, installMode, ctx);
902
+ return `v${manifest.version} \u5DF2\u5C31\u7EEA`;
903
+ } finally {
904
+ this.checking = false;
905
+ }
906
+ }
907
+ recordFailure(version, error) {
908
+ const state = loadState(this.opts.dataDir);
909
+ const sameVersion = version !== void 0 && state.failedVersion === version;
910
+ patchState(this.opts.dataDir, {
911
+ lastError: error,
912
+ failedVersion: version ?? state.failedVersion,
913
+ failureCount: sameVersion ? (state.failureCount ?? 0) + 1 : 1
914
+ });
915
+ this.log(error);
916
+ }
917
+ /** 等待节点连续空闲后安装;`installMode` 立即安装 */
918
+ async installWhenIdle(version, stagedPath, installMode, ctx = {}) {
919
+ if (this.installing) return;
920
+ this.installing = true;
921
+ try {
922
+ if (!installMode) {
923
+ const idleMs = num("ZQAGENT_UPDATE_IDLE_MS", 60 * 1e3);
924
+ this.log(
925
+ `\u7B49\u5F85\u7A7A\u95F2\u540E\u5B89\u88C5 v${version}\uFF08\u9700\u8FDE\u7EED\u7A7A\u95F2 ${Math.round(idleMs / 1e3)}s${ctx.excludeSessionId ? "\uFF0C\u5DF2\u6392\u9664\u53D1\u8D77\u66F4\u65B0\u7684\u4F1A\u8BDD" : ""}\uFF09`
926
+ );
927
+ const ok = await this.waitForIdle(ctx);
928
+ if (!ok) {
929
+ this.installing = false;
930
+ return;
931
+ }
932
+ }
933
+ this.applyUpdate(version, stagedPath);
934
+ } catch (err) {
935
+ this.installing = false;
936
+ this.recordFailure(version, `\u5B89\u88C5\u5931\u8D25\uFF1A${err.message}`);
937
+ }
938
+ }
939
+ waitForIdle(ctx = {}) {
940
+ const requireUptime = ctx.requireUptime !== false;
941
+ const minUptime = requireUptime ? num("ZQAGENT_UPDATE_MIN_UPTIME_MS", 10 * 60 * 1e3) : 0;
942
+ const idleMs = num("ZQAGENT_UPDATE_IDLE_MS", 60 * 1e3);
943
+ let idleSince = null;
944
+ return new Promise((resolve2) => {
945
+ const timer = setInterval(() => {
946
+ if (this.stopped) {
947
+ clearInterval(timer);
948
+ resolve2(false);
949
+ return;
950
+ }
951
+ if (this.forceRequested) {
952
+ this.forceRequested = false;
953
+ clearInterval(timer);
954
+ resolve2(true);
955
+ return;
956
+ }
957
+ const ready = Date.now() - this.startedAt >= minUptime;
958
+ if (!ready || !this.opts.isIdle(ctx.excludeSessionId)) {
959
+ idleSince = null;
960
+ return;
961
+ }
962
+ idleSince ??= Date.now();
963
+ if (Date.now() - idleSince >= idleMs) {
964
+ clearInterval(timer);
965
+ resolve2(true);
966
+ }
967
+ }, 1e3);
968
+ timer.unref();
969
+ });
970
+ }
971
+ applyUpdate(version, stagedPath) {
972
+ this.opts.prepareExit?.();
973
+ const execPath = process.execPath;
974
+ installStaged({
975
+ dataDir: this.opts.dataDir,
976
+ execPath,
977
+ stagedPath,
978
+ version,
979
+ log: this.log
980
+ });
981
+ const verify2 = selfCheck(execPath, version);
982
+ if (!verify2.ok) {
983
+ const restored = rollback(this.opts.dataDir, execPath, this.log);
984
+ patchState(this.opts.dataDir, { skippedVersion: version });
985
+ this.recordFailure(
986
+ version,
987
+ `\u5B89\u88C5\u540E\u81EA\u68C0\u5931\u8D25\uFF08${verify2.error ?? "\u672A\u77E5\u9519\u8BEF"}\uFF09` + (restored ? `\uFF0C\u5DF2\u56DE\u6EDA\u5230 v${restored}` : "\uFF0C\u56DE\u6EDA\u5931\u8D25\uFF0C\u9700\u4EBA\u5DE5\u5904\u7406")
988
+ );
989
+ process.exit(1);
990
+ }
991
+ this.log(`\u5DF2\u5B89\u88C5 v${version}\uFF0C\u6B63\u5728\u91CD\u542F\u2026`);
992
+ process.exit(70);
993
+ }
994
+ /**
995
+ * 用备份回滚;返回恢复到的版本。
996
+ *
997
+ * 目标路径优先取上次安装记录的路径:`zqagent update --rollback` 可能由 node 进程执行
998
+ * (如 install.sh 内),此时 `process.execPath` 是 node 本身,绝不能拿它做替换。
999
+ */
1000
+ rollbackNow() {
1001
+ const state = loadState(this.opts.dataDir);
1002
+ const target = state.installedExecPath ?? (isSeaRuntime() ? process.execPath : null);
1003
+ if (!target) {
1004
+ this.log("\u65E0\u6CD5\u786E\u5B9A\u8282\u70B9\u53EF\u6267\u884C\u6587\u4EF6\u4F4D\u7F6E\uFF08\u7F3A\u5C11\u5B89\u88C5\u8BB0\u5F55\uFF09\uFF1B\u8BF7\u7528 deploy/service/install.sh rollback");
1005
+ return null;
1006
+ }
1007
+ return rollback(this.opts.dataDir, target, this.log);
1008
+ }
1009
+ status() {
1010
+ const state = loadState(this.opts.dataDir);
1011
+ const cap = selfUpdateCapability(Boolean(this.publicKey));
1012
+ return {
1013
+ enabled: !this.autoUpdateOff && cap.ok,
1014
+ reason: cap.reason,
1015
+ currentVersion: this.currentVersion,
1016
+ channel: updaterChannel(),
1017
+ manifestUrl: this.manifestUrl(),
1018
+ serviceManager: serviceManager(),
1019
+ lastCheckAt: this.lastCheckAt ?? state.lastCheckAt,
1020
+ stagedVersion: state.stagedVersion,
1021
+ pendingVersion: state.pendingVersion,
1022
+ previousVersion: state.previousVersion,
1023
+ backupVersion: state.backupVersion,
1024
+ lastError: state.lastError
1025
+ };
1026
+ }
1027
+ };
1028
+ // Annotate the CommonJS export names for ESM import in node:
1029
+ 0 && (module.exports = {
1030
+ UpdateController,
1031
+ evaluateManifest,
1032
+ updaterChannel,
1033
+ updaterUrl
1034
+ });
1035
+ //# sourceMappingURL=controller.cjs.map