@pab-iprg/huohuo 1.0.0-preview.6 → 1.0.0-preview.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 (4) hide show
  1. package/README.md +20 -0
  2. package/cli.js +305 -158
  3. package/package.json +5 -4
  4. package/release.json +110 -12
package/README.md CHANGED
@@ -11,3 +11,23 @@
11
11
  显式升级检查旧迁移不可变、追加迁移顺序、执行库格式与 DSH 会话兼容范围,先一致性备份再迁移和验证,成功后切换;不自动恢复旧备份覆盖新数据。默认卸载保留实例、凭据、会话和备份,不提供直接彻底删除业务数据的快捷入口。
12
12
 
13
13
  首版终端前台管理生命周期,Ctrl+C 停止本次实例。退出终端、休眠或关机后不承诺持续运行。模型凭据在现有设置页面填写,安装检查不调用真实模型或通知。
14
+
15
+
16
+ Windows x64 候选沿用同一入口,使用系统自带 Windows PowerShell/CIM 读取本机状态,自动准备独立 Python 和固定 Windows wheels;不要求预装 Python、编译器、Git Bash、管理员权限或开发者模式。需要兼容的 Node 24.x(至少24.8)和自带 npm。候选构建成功不等于 Windows 实机已通过;实际支持范围以发行清单和验收记录为准。PowerShell 若拦截 npm/npx 的 .ps1 启动脚本,可使用 Node 自带的 `npx.cmd`,无需更改系统执行策略。
17
+
18
+
19
+ Windows 用户可以直接使用命令提示符(CMD)或 PowerShell,两者调用同一安装器,无需先切换终端。候选发布到 preview 标签后:
20
+
21
+ CMD:
22
+
23
+ ```bat
24
+ npx --yes --prefer-online @pab-iprg/huohuo@preview
25
+ ```
26
+
27
+ PowerShell:
28
+
29
+ ```powershell
30
+ npx.cmd --yes --prefer-online @pab-iprg/huohuo@preview
31
+ ```
32
+
33
+ 默认目录无需手动填写。需要独立试用目录时,CMD 使用 `--home "%USERPROFILE%\.huohuo-windows-trial-01"`,PowerShell 使用 `--home "$env:USERPROFILE\.huohuo-windows-trial-01"`。程序内部使用系统自带 PowerShell 采样,不要求用户用 PowerShell 启动。两种终端的首装、Ctrl+C、再次启动及数据保留均需分别实机验收。
package/cli.js CHANGED
@@ -4,28 +4,150 @@
4
4
  import { open as open3, readFile as readFile9, realpath as realpath5 } from "node:fs/promises";
5
5
  import { Readable } from "node:stream";
6
6
  import { homedir as homedir2 } from "node:os";
7
- import { dirname as dirname6, join as join8, resolve as resolve5 } from "node:path";
7
+ import { dirname as dirname6, join as join9, resolve as resolve5 } from "node:path";
8
8
  import { fileURLToPath as fileURLToPath2 } from "node:url";
9
9
 
10
+ // distribution/processes.ts
11
+ import { execFile } from "node:child_process";
12
+ import { join } from "node:path";
13
+ function interruptProcessTree(child, signal) {
14
+ if (!child.pid || child.exitCode !== null || child.signalCode !== null) return;
15
+ if (process.platform === "win32") {
16
+ const executable = join(process.env.SystemRoot ?? "C:\\Windows", "System32", "taskkill.exe");
17
+ execFile(executable, ["/PID", String(child.pid), "/T", "/F"], { windowsHide: true, timeout: 5e3 }, (error) => {
18
+ if (error && child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
19
+ });
20
+ } else {
21
+ try {
22
+ process.kill(-child.pid, signal);
23
+ } catch {
24
+ }
25
+ }
26
+ }
27
+
10
28
  // distribution/bootstrap/install.ts
11
29
  import { readFile as readFile4, mkdir as mkdir3, access, rename as rename3, rm as rm2, realpath as realpath3, readdir } from "node:fs/promises";
12
30
  import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
13
- import { join as join3, resolve as resolve3 } from "node:path";
31
+ import { join as join4, resolve as resolve3 } from "node:path";
14
32
  import { release as osRelease } from "node:os";
15
- import { spawn, execFile as execFile3 } from "node:child_process";
16
- import { promisify as promisify3 } from "node:util";
33
+ import { spawn, execFile as execFile5 } from "node:child_process";
34
+ import { promisify as promisify4 } from "node:util";
17
35
 
18
36
  // distribution/bootstrap/state.ts
19
37
  import { open, rename, unlink, readFile, mkdir } from "node:fs/promises";
20
38
  import { dirname } from "node:path";
21
39
  import { randomUUID } from "node:crypto";
22
- import { execFile } from "node:child_process";
40
+ import { execFile as execFile3 } from "node:child_process";
41
+ import { promisify as promisify2 } from "node:util";
42
+
43
+ // distribution/windows.ts
44
+ import { execFile as execFile2 } from "node:child_process";
45
+ import { win32 } from "node:path";
23
46
  import { promisify } from "node:util";
24
- var execute = promisify(execFile);
47
+ var execute = promisify(execFile2);
48
+ var MAX_OUTPUT = 8 * 1024 * 1024;
49
+ var prelude = "$ErrorActionPreference='Stop';[Console]::OutputEncoding=[Text.UTF8Encoding]::new($false);";
50
+ var sidPattern = /^S-\d+(?:-\d+){1,15}$/;
51
+ async function powershell(script) {
52
+ if (process.platform !== "win32") throw new Error("Windows \u63A2\u9488\u53EA\u80FD\u5728 Windows \u672C\u673A\u6267\u884C");
53
+ const root = process.env.SystemRoot ?? "C:\\Windows";
54
+ if (!win32.isAbsolute(root)) throw new Error("Windows \u7CFB\u7EDF\u76EE\u5F55\u65E0\u6548");
55
+ const executable = win32.join(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
56
+ const { stdout } = await execute(
57
+ executable,
58
+ [
59
+ "-NoLogo",
60
+ "-NoProfile",
61
+ "-NonInteractive",
62
+ "-EncodedCommand",
63
+ Buffer.from(prelude + script, "utf16le").toString("base64")
64
+ ],
65
+ { timeout: 5e3, maxBuffer: MAX_OUTPUT, windowsHide: true, encoding: "utf8" }
66
+ );
67
+ return stdout;
68
+ }
69
+ function document(stdout) {
70
+ if (Buffer.byteLength(stdout, "utf8") > MAX_OUTPUT) throw new Error("Windows \u63A2\u9488\u54CD\u5E94\u8D85\u9650");
71
+ const value = JSON.parse(stdout.trim());
72
+ if (!value || typeof value !== "object" || Array.isArray(value) || value.format !== 1) throw new Error("Windows \u63A2\u9488\u54CD\u5E94\u65E0\u6548");
73
+ return value;
74
+ }
75
+ function sid(value) {
76
+ if (typeof value !== "string" || value.length > 184 || !sidPattern.test(value)) throw new Error("Windows \u7528\u6237\u8EAB\u4EFD\u672A\u77E5");
77
+ return value;
78
+ }
79
+ function integer(value, label) {
80
+ if (typeof value !== "number" && !(typeof value === "string" && /^(0|[1-9]\d*)$/.test(value))) throw new Error(`Windows ${label}\u63A2\u9488\u65E0\u6548`);
81
+ const n = Number(value);
82
+ if (!Number.isSafeInteger(n) || n < 0) throw new Error(`Windows ${label}\u63A2\u9488\u65E0\u6548`);
83
+ return n;
84
+ }
85
+ function parseWindowsUserIdentity(stdout) {
86
+ return sid(document(stdout).sid);
87
+ }
88
+ async function windowsUserIdentity() {
89
+ return parseWindowsUserIdentity(await powershell("@{format=1;sid=[Security.Principal.WindowsIdentity]::GetCurrent().User.Value}|ConvertTo-Json -Compress"));
90
+ }
91
+ function parseWindowsProcessIdentity(stdout) {
92
+ const value = document(stdout);
93
+ if (value.exists === false && Object.keys(value).every((key) => ["format", "exists"].includes(key))) return null;
94
+ if (value.exists !== true || typeof value.created !== "string" || !/^[1-9]\d{15,18}$/.test(value.created)) throw new Error("Windows \u8FDB\u7A0B\u542F\u52A8\u8EAB\u4EFD\u672A\u77E5");
95
+ return `${sid(value.sid)}:${value.created}`;
96
+ }
97
+ async function windowsProcessIdentity(pid) {
98
+ if (!Number.isSafeInteger(pid) || pid <= 0 || pid > 4294967295) throw new Error("Windows \u8FDB\u7A0B PID \u65E0\u6548");
99
+ return parseWindowsProcessIdentity(await powershell(`$p=Get-CimInstance -ClassName Win32_Process -Filter 'ProcessId=${pid}' -ErrorAction Stop;
100
+ if($null -eq $p){@{format=1;exists=$false}|ConvertTo-Json -Compress}else{
101
+ if($null -eq $p.CreationDate){throw 'Process creation time unavailable'};
102
+ $owner=Invoke-CimMethod -InputObject $p -MethodName GetOwnerSid -ErrorAction Stop;
103
+ if($owner.ReturnValue -ne 0 -or [string]::IsNullOrEmpty($owner.Sid)){throw 'Process owner unavailable'};
104
+ @{format=1;exists=$true;sid=$owner.Sid;created=$p.CreationDate.ToUniversalTime().Ticks.ToString([Globalization.CultureInfo]::InvariantCulture)}|ConvertTo-Json -Compress
105
+ }`));
106
+ }
107
+ function parseWindowsResourceSnapshot(stdout) {
108
+ const value = document(stdout);
109
+ const totalBytes = integer(value.totalKiB, "\u5185\u5B58\u5BB9\u91CF") * 1024;
110
+ const availableBytes = integer(value.availableKiB, "\u53EF\u7528\u5185\u5B58") * 1024;
111
+ if (!Number.isSafeInteger(totalBytes) || totalBytes <= 0 || !Number.isSafeInteger(availableBytes) || availableBytes > totalBytes || !Array.isArray(value.processes) || !value.processes.length || value.processes.length > 1e5) throw new Error("Windows \u8D44\u6E90\u5FEB\u7167\u65E0\u6548");
112
+ const seen = /* @__PURE__ */ new Set();
113
+ const processes = value.processes.map((row) => {
114
+ if (!row || typeof row !== "object" || Array.isArray(row)) throw new Error("Windows \u8FDB\u7A0B\u5FEB\u7167\u65E0\u6548");
115
+ const pid = integer(row.pid, "PID"), parent = integer(row.parent, "\u7236\u8FDB\u7A0B"), rss = integer(row.rss, "\u5DE5\u4F5C\u96C6");
116
+ if (pid > 4294967295 || parent > 4294967295 || seen.has(pid) || pid !== 0 && pid === parent) throw new Error("Windows \u8FDB\u7A0B\u5FEB\u7167\u65E0\u6548");
117
+ seen.add(pid);
118
+ return { pid, parent, rss };
119
+ });
120
+ return { totalBytes, availableBytes, processes };
121
+ }
122
+ function windowsMemoryPressure(availableBytes, totalBytes) {
123
+ if (!Number.isSafeInteger(availableBytes) || availableBytes < 0 || !Number.isSafeInteger(totalBytes) || totalBytes <= 0 || availableBytes > totalBytes) return "unknown";
124
+ const ratio = availableBytes / totalBytes;
125
+ return ratio <= 0.02 ? "critical" : ratio <= 0.05 ? "high" : "normal";
126
+ }
127
+ var cached;
128
+ async function windowsResourceSnapshot() {
129
+ const now = performance.now();
130
+ if (!cached || now - cached.sampledAt >= 1e3) {
131
+ const pending = powershell(`$memory=Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop;
132
+ $rows=@(Get-CimInstance -ClassName Win32_Process -ErrorAction Stop|ForEach-Object {@{pid=[string]$_.ProcessId;parent=[string]$_.ParentProcessId;rss=[string]$_.WorkingSetSize}});
133
+ @{format=1;totalKiB=[string]$memory.TotalVisibleMemorySize;availableKiB=[string]$memory.FreePhysicalMemory;processes=$rows}|ConvertTo-Json -Compress -Depth 4`).then(parseWindowsResourceSnapshot);
134
+ const entry = { sampledAt: now, pending };
135
+ cached = entry;
136
+ void pending.catch(() => {
137
+ if (cached === entry) cached = void 0;
138
+ });
139
+ }
140
+ return structuredClone(await cached.pending);
141
+ }
142
+
143
+ // distribution/bootstrap/state.ts
144
+ var execute2 = promisify2(execFile3);
145
+ var lockUser = () => process.platform === "win32" ? windowsUserIdentity() : Promise.resolve(process.getuid?.());
25
146
  async function processIdentity(pid) {
147
+ if (process.platform === "win32") return windowsProcessIdentity(pid);
26
148
  if (!Number.isSafeInteger(pid) || pid <= 0 || !["darwin", "linux"].includes(process.platform)) throw new Error("\u8FDB\u7A0B\u542F\u52A8\u8EAB\u4EFD\u63A2\u9488\u4E0D\u652F\u6301\u6216\u65E0\u6548");
27
149
  try {
28
- const { stdout } = await execute("ps", ["-p", String(pid), "-o", "lstart=,uid="], { timeout: 2e3 });
150
+ const { stdout } = await execute2("ps", ["-p", String(pid), "-o", "lstart=,uid="], { timeout: 2e3 });
29
151
  if (!stdout.trim()) throw new Error("\u8FDB\u7A0B\u542F\u52A8\u8EAB\u4EFD\u63A2\u9488\u8FD4\u56DE\u7A7A\u503C");
30
152
  return stdout.trim().replace(/\s+/g, " ");
31
153
  } catch (e) {
@@ -42,7 +164,7 @@ async function processIdentity(pid) {
42
164
  async function isLockOwnerDead(path, token) {
43
165
  try {
44
166
  const previous = JSON.parse(await readFile(path, "utf8"));
45
- return previous.format === 1 && previous.path === path && previous.uid === process.getuid?.() && typeof previous.processStart === "string" && !!previous.processStart && typeof previous.token === "string" && /^[a-f0-9-]{36}$/.test(previous.token) && (token === void 0 || previous.token === token) && Number.isSafeInteger(previous.pid) && await processIdentity(previous.pid) === null;
167
+ return previous.format === 1 && previous.path === path && previous.uid === await lockUser() && typeof previous.processStart === "string" && !!previous.processStart && typeof previous.token === "string" && /^[a-f0-9-]{36}$/.test(previous.token) && (token === void 0 || previous.token === token) && Number.isSafeInteger(previous.pid) && await processIdentity(previous.pid) === null;
46
168
  } catch {
47
169
  return false;
48
170
  }
@@ -62,11 +184,13 @@ async function atomicJSON(path, value) {
62
184
  }
63
185
  try {
64
186
  await rename(pending, path);
65
- const directory = await open(dirname(path), "r");
66
- try {
67
- await directory.sync();
68
- } finally {
69
- await directory.close();
187
+ if (process.platform !== "win32") {
188
+ const directory = await open(dirname(path), "r");
189
+ try {
190
+ await directory.sync();
191
+ } finally {
192
+ await directory.close();
193
+ }
70
194
  }
71
195
  } finally {
72
196
  await unlink(pending).catch(() => {
@@ -100,7 +224,7 @@ async function acquireLock(path) {
100
224
  try {
101
225
  const processStart = await processIdentity(process.pid);
102
226
  if (!processStart) throw new Error("\u5F53\u524D\u8FDB\u7A0B\u542F\u52A8\u8EAB\u4EFD\u672A\u77E5");
103
- await lock.writeFile(JSON.stringify({ format: 1, path, uid: process.getuid?.(), pid: process.pid, processStart, startedAt: (/* @__PURE__ */ new Date()).toISOString(), token }));
227
+ await lock.writeFile(JSON.stringify({ format: 1, path, uid: await lockUser(), pid: process.pid, processStart, startedAt: (/* @__PURE__ */ new Date()).toISOString(), token }));
104
228
  await lock.sync();
105
229
  } catch (error) {
106
230
  await lock.close();
@@ -129,7 +253,7 @@ async function withLock(path, action) {
129
253
  // distribution/bootstrap/release.ts
130
254
  var version = /^\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?$/;
131
255
  var hash = /^[a-f0-9]{64}$/;
132
- function integer(n) {
256
+ function integer2(n) {
133
257
  return Number.isSafeInteger(n) && Number(n) >= 0;
134
258
  }
135
259
  function httpsURL(value) {
@@ -142,14 +266,14 @@ function httpsURL(value) {
142
266
  }
143
267
  }
144
268
  function artifact(a) {
145
- if (!a || !hash.test(a.sha256) || !integer(a.downloadBytes) || !integer(a.expandedBytes) || !a.downloadBytes || !a.expandedBytes) return false;
269
+ if (!a || !hash.test(a.sha256) || !integer2(a.downloadBytes) || !integer2(a.expandedBytes) || !a.downloadBytes || !a.expandedBytes) return false;
146
270
  if (a.redirectHosts !== void 0 && (!Array.isArray(a.redirectHosts) || a.redirectHosts.length > 4 || !a.redirectHosts.every((s) => typeof s === "string" && /^[a-z0-9.-]+$/.test(s)))) return false;
147
271
  if (a.bundled !== void 0) return a.bundled === true && a.url === `https://bundled.invalid/${a.sha256}.tgz` && a.redirectHosts === void 0 && a.mirrors === void 0;
148
272
  return httpsURL(a.url) && (a.mirrors === void 0 || Array.isArray(a.mirrors) && a.mirrors.length <= 3 && a.mirrors.every(httpsURL));
149
273
  }
150
274
  function validateRelease(input) {
151
275
  const r = input;
152
- if (!r || r.format !== 1 || r.bootstrapFormat !== 1 || !version.test(r.version) || !integer(r.nodeMajor) || r.nodeMajor < 24 || !integer(r.npmMajor) || r.npmMajor < 11 || !r.platform?.minimumOsRelease || !["darwin", "linux", "win32"].includes(r.platform.os) || !["arm64", "x64"].includes(r.platform.arch) || r.platform.libc !== void 0 && (r.platform.os !== "linux" || r.platform.libc !== "glibc") || r.assetPackage !== void 0 && (r.assetPackage.name !== `@pab-iprg/huohuo-assets-${r.platform.os}-${r.platform.arch}` || !version.test(r.assetPackage.version) || !r.runtime?.bundled || !r.pythonDependencies?.bundled) || !artifact(r.runtime) || r.runtime.name !== "@huohuo/research-agent" || r.runtime.version !== r.version || !/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(r.runtime.integrity) || !artifact(r.python) || !artifact(r.pythonDependencies) || !r.python.build || !r.python.license || r.pythonDependencies.upstreamWheelCount !== void 0 && !integer(r.pythonDependencies.upstreamWheelCount) || !r.python.executable || r.python.executable.startsWith("/") || r.python.executable.split(/[\\/]/).some((p) => p === ".." || !p) || !version.test(r.dsh?.version) || !hash.test(r.dsh?.assemblySha256) || !hash.test(r.requirementsSha256) || !hash.test(r.profilesSha256) || !r.budget || ![
276
+ if (!r || r.format !== 1 || r.bootstrapFormat !== 1 || !version.test(r.version) || !integer2(r.nodeMajor) || r.nodeMajor < 24 || !integer2(r.npmMajor) || r.npmMajor < 11 || !r.platform?.minimumOsRelease || !["darwin", "linux", "win32"].includes(r.platform.os) || !["arm64", "x64"].includes(r.platform.arch) || r.platform.libc !== void 0 && (r.platform.os !== "linux" || r.platform.libc !== "glibc") || r.assetPackage !== void 0 && (r.assetPackage.name !== `@pab-iprg/huohuo-assets-${r.platform.os}-${r.platform.arch}` || !version.test(r.assetPackage.version) || !r.runtime?.bundled || !r.pythonDependencies?.bundled) || !artifact(r.runtime) || r.runtime.name !== "@huohuo/research-agent" || r.runtime.version !== r.version || !/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(r.runtime.integrity) || !artifact(r.python) || !artifact(r.pythonDependencies) || !r.python.build || !r.python.license || r.pythonDependencies.upstreamWheelCount !== void 0 && !integer2(r.pythonDependencies.upstreamWheelCount) || !r.python.executable || r.python.executable.startsWith("/") || r.python.executable.split(/[\\/]/).some((p) => p === ".." || !p) || !version.test(r.dsh?.version) || !hash.test(r.dsh?.assemblySha256) || !hash.test(r.requirementsSha256) || !hash.test(r.profilesSha256) || !r.budget || ![
153
277
  "minimumEffectiveMemoryBytes",
154
278
  "startupHeadroomBytes",
155
279
  "heavyJobHeadroomBytes",
@@ -161,7 +285,7 @@ function validateRelease(input) {
161
285
  "processTreeBudgetBytes",
162
286
  "sampleIntervalMs",
163
287
  "sampleMaxAgeMs"
164
- ].every((k) => integer(r.budget[k])) || r.budget.observedInstallRssBytes !== void 0 && (!integer(r.budget.observedInstallRssBytes) || r.budget.observedInstallRssBytes <= 0) || r.budget.startupHeadroomBytes <= 0 || r.budget.minimumEffectiveMemoryBytes <= 0 || r.budget.diskReserveBytes <= 0 || r.budget.recoverySamples < 2 || r.budget.heavyJobHeadroomBytes < r.budget.startupHeadroomBytes || r.budget.processTreeBudgetBytes <= 0 || r.budget.sampleIntervalMs < 1e3 || r.budget.sampleMaxAgeMs < r.budget.sampleIntervalMs * 2 || r.budget.sampleMaxAgeMs > 3e4 || !r.schema || !integer(r.schema.minimum) || !integer(r.schema.maximum) || r.schema.minimum > r.schema.maximum) {
288
+ ].every((k) => integer2(r.budget[k])) || r.budget.observedInstallRssBytes !== void 0 && (!integer2(r.budget.observedInstallRssBytes) || r.budget.observedInstallRssBytes <= 0) || r.budget.startupHeadroomBytes <= 0 || r.budget.minimumEffectiveMemoryBytes <= 0 || r.budget.diskReserveBytes <= 0 || r.budget.recoverySamples < 2 || r.budget.heavyJobHeadroomBytes < r.budget.startupHeadroomBytes || r.budget.processTreeBudgetBytes <= 0 || r.budget.sampleIntervalMs < 1e3 || r.budget.sampleMaxAgeMs < r.budget.sampleIntervalMs * 2 || r.budget.sampleMaxAgeMs > 3e4 || !r.schema || !integer2(r.schema.minimum) || !integer2(r.schema.maximum) || r.schema.minimum > r.schema.maximum) {
165
289
  throw new Error("\u53D1\u884C\u6E05\u5355\u65E0\u6548\u6216\u5C1A\u672A\u5B8C\u6210\u5E73\u53F0\u3001\u6458\u8981\u4E0E\u8D44\u6E90\u6D4B\u91CF\uFF1B\u4E0D\u80FD\u5B89\u88C5");
166
290
  }
167
291
  if (!hash.test(r.schema.migrationsSha256)) throw new Error("\u7F3A\u5C11\u6570\u636E\u5E93\u8FC1\u79FB\u88C5\u914D\u6458\u8981");
@@ -172,7 +296,7 @@ function validateRelease(input) {
172
296
  // distribution/bootstrap/artifact.ts
173
297
  import { createHash, randomUUID as randomUUID2 } from "node:crypto";
174
298
  import { createReadStream } from "node:fs";
175
- import { mkdir as mkdir2, open as open2, rename as rename2, rm, realpath, symlink, link, lstat } from "node:fs/promises";
299
+ import { mkdir as mkdir2, open as open2, rename as rename2, rm, realpath, symlink, link, lstat, copyFile } from "node:fs/promises";
176
300
  import { dirname as dirname2, resolve, sep, posix } from "node:path";
177
301
  import { createGunzip } from "node:zlib";
178
302
  async function verifyArtifact(path, artifact2) {
@@ -286,8 +410,12 @@ async function transfer(artifact2, path, source, fetcher, onBytes) {
286
410
  await rm(pending, { force: true });
287
411
  }
288
412
  }
289
- function safePath(root, path) {
413
+ function validateArchiveName(path, platform = process.platform) {
290
414
  if (!path || path.includes("\\") || path.includes("\0") || path.startsWith("/") || /^[A-Za-z]:/.test(path) || path.split("/").includes("..")) throw new Error("\u538B\u7F29\u5305\u8DEF\u5F84\u8D8A\u754C");
415
+ if (platform === "win32" && path.split("/").some((part) => part && part !== "." && (/[<>:"|?*\x00-\x1f]/.test(part) || /[. ]$/.test(part) || /^(?:con|prn|aux|nul|conin\$|conout\$|com[1-9¹²³]|lpt[1-9¹²³])$/i.test(part.split(".")[0].trimEnd())))) throw new Error("\u538B\u7F29\u5305\u542B Windows \u4E0D\u652F\u6301\u7684\u8DEF\u5F84\u540D\u79F0");
416
+ }
417
+ function safePath(root, path) {
418
+ validateArchiveName(path);
291
419
  const target = resolve(root, path);
292
420
  if (target !== root && !target.startsWith(root + sep)) throw new Error("\u538B\u7F29\u5305\u8DEF\u5F84\u8D8A\u754C");
293
421
  return target;
@@ -385,7 +513,8 @@ async function extractArchive(archive, root, expandedLimit) {
385
513
  throw e;
386
514
  }
387
515
  if (!source.startsWith(root + sep) || !(await lstat(source)).isFile()) throw new Error("\u94FE\u63A5\u5FC5\u987B\u6307\u5411\u5305\u5185\u666E\u901A\u6587\u4EF6");
388
- if (item.symbolic) await symlink(item.text, item.target);
516
+ if (process.platform === "win32") await copyFile(source, item.target, 1);
517
+ else if (item.symbolic) await symlink(item.text, item.target);
389
518
  else await link(source, item.target);
390
519
  }
391
520
  if (later.length === pending.length) throw new Error("\u538B\u7F29\u5305\u94FE\u63A5\u7F3A\u5931\u6216\u5FAA\u73AF");
@@ -401,9 +530,9 @@ import { availableParallelism, totalmem } from "node:os";
401
530
  import { availableMemory, constrainedMemory } from "node:process";
402
531
  import { readFile as readFile3, realpath as realpath2, stat, statfs } from "node:fs/promises";
403
532
  import { dirname as dirname3, resolve as resolve2, sep as sep2 } from "node:path";
404
- import { execFile as execFile2 } from "node:child_process";
405
- import { promisify as promisify2 } from "node:util";
406
- var execute2 = promisify2(execFile2);
533
+ import { execFile as execFile4 } from "node:child_process";
534
+ import { promisify as promisify3 } from "node:util";
535
+ var execute3 = promisify3(execFile4);
407
536
  async function existingParent(path) {
408
537
  let current = resolve2(path);
409
538
  while (true) {
@@ -449,13 +578,13 @@ async function sampleResources(paths) {
449
578
  if (!(effectiveMemoryBytes > 0)) effectiveMemoryBytes = null;
450
579
  if (process.platform === "darwin") {
451
580
  try {
452
- const { stdout } = await execute2("/usr/sbin/sysctl", ["-n", "kern.memorystatus_vm_pressure_level"], { timeout: 2e3 });
581
+ const { stdout } = await execute3("/usr/sbin/sysctl", ["-n", "kern.memorystatus_vm_pressure_level"], { timeout: 2e3 });
453
582
  pressure = { 1: "normal", 2: "high", 4: "critical" }[Number(stdout.trim())] ?? "unknown";
454
583
  } catch {
455
584
  }
456
585
  if (pressure === "normal" && availableMemoryBytes !== null && effectiveMemoryBytes !== null) {
457
586
  try {
458
- const { stdout } = await execute2("/usr/bin/vm_stat", [], { timeout: 2e3 });
587
+ const { stdout } = await execute3("/usr/bin/vm_stat", [], { timeout: 2e3 });
459
588
  availableMemoryBytes = darwinAvailableMemory(availableMemoryBytes, effectiveMemoryBytes, pressure, stdout, limit > 0);
460
589
  } catch {
461
590
  }
@@ -470,6 +599,16 @@ async function sampleResources(paths) {
470
599
  } catch {
471
600
  }
472
601
  }
602
+ if (process.platform === "win32") {
603
+ try {
604
+ const snapshot = await windowsResourceSnapshot();
605
+ effectiveMemoryBytes = Math.min(effectiveMemoryBytes ?? Infinity, snapshot.totalBytes);
606
+ availableMemoryBytes = availableMemoryBytes === null ? null : Math.min(availableMemoryBytes, snapshot.availableBytes);
607
+ pressure = windowsMemoryPressure(snapshot.availableBytes, snapshot.totalBytes);
608
+ } catch {
609
+ pressure = "unknown";
610
+ }
611
+ }
473
612
  const disks = [];
474
613
  for (const path of [...new Set(paths.map((p) => resolve2(p)))]) {
475
614
  try {
@@ -544,10 +683,10 @@ function evaluateResources(sample, policy, costs, phase) {
544
683
 
545
684
  // runtime/npm.mjs
546
685
  import { existsSync } from "node:fs";
547
- import { dirname as dirname4, join as join2 } from "node:path";
686
+ import { dirname as dirname4, join as join3 } from "node:path";
548
687
  function npmCommand() {
549
688
  if (process.platform !== "win32") return { command: "npm", prefix: [] };
550
- const cli = join2(dirname4(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
689
+ const cli = join3(dirname4(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
551
690
  if (existsSync(cli)) return { command: process.execPath, prefix: [cli] };
552
691
  return { command: process.env.ComSpec ?? "cmd.exe", prefix: ["/d", "/s", "/c", "npm"] };
553
692
  }
@@ -602,29 +741,33 @@ async function ownedDirectory(path, owner) {
602
741
  await mkdir3(resolve3(path, ".."), { recursive: true, mode: 448 });
603
742
  try {
604
743
  await mkdir3(path, { mode: 448 });
605
- await atomicJSON(join3(path, ".huohuo-owner.json"), { format: 1, owner });
744
+ await atomicJSON(join4(path, ".huohuo-owner.json"), { format: 1, owner });
606
745
  } catch (e) {
607
746
  if (e.code !== "EEXIST") throw e;
608
- const marker = JSON.parse(await readFile4(join3(path, ".huohuo-owner.json"), "utf8"));
747
+ const marker = JSON.parse(await readFile4(join4(path, ".huohuo-owner.json"), "utf8"));
609
748
  if (marker.format !== 1 || marker.owner !== owner) throw new Error("\u76EE\u5F55\u5F52\u5C5E\u65E0\u6CD5\u6838\u9A8C\uFF1B\u672A\u63A5\u7BA1\u6216\u8986\u76D6");
610
749
  }
611
750
  }
612
- async function run(command, args, cwd, env = process.env) {
751
+ async function run(command, args, cwd, env = process.env, onInterrupt) {
613
752
  await new Promise((done, fail) => {
614
753
  const child = spawn(command, args, { cwd, env, stdio: "inherit", detached: process.platform !== "win32" });
615
754
  let interrupted = false;
616
755
  let timer;
617
756
  const kill = (signal) => {
618
- if (!child.pid) return;
619
- try {
620
- process.kill(process.platform === "win32" ? child.pid : -child.pid, signal);
621
- } catch {
622
- }
757
+ interruptProcessTree(child, signal);
623
758
  };
624
759
  const stop = () => {
760
+ if (interrupted) return;
625
761
  interrupted = true;
626
- kill("SIGTERM");
627
- timer ??= setTimeout(() => kill("SIGKILL"), 3e3);
762
+ if (process.platform === "win32" && onInterrupt) {
763
+ void onInterrupt().then((stopping) => {
764
+ if (!stopping) kill("SIGTERM");
765
+ }, () => kill("SIGTERM"));
766
+ timer = setTimeout(() => kill("SIGKILL"), 8e3);
767
+ } else {
768
+ kill("SIGTERM");
769
+ timer = setTimeout(() => kill("SIGKILL"), 3e3);
770
+ }
628
771
  };
629
772
  process.on("SIGINT", stop);
630
773
  process.on("SIGTERM", stop);
@@ -639,14 +782,14 @@ async function run(command, args, cwd, env = process.env) {
639
782
  });
640
783
  child.once("close", (code) => {
641
784
  cleanup();
642
- code === 0 && !interrupted ? done() : fail(Object.assign(new Error("\u672C\u673A\u5B89\u88C5\u5B50\u8FDB\u7A0B\u5931\u8D25\u6216\u4E2D\u6B62\uFF0C\u4FDD\u7559\u539F\u9636\u6BB5\u4E0E\u5B9E\u4F8B"), { code: interrupted ? "INTERRUPTED" : `CHILD_${code}` }));
785
+ code === 0 && (!interrupted || process.platform === "win32" && onInterrupt !== void 0) ? done() : fail(Object.assign(new Error("\u672C\u673A\u5B89\u88C5\u5B50\u8FDB\u7A0B\u5931\u8D25\u6216\u4E2D\u6B62\uFF0C\u4FDD\u7559\u539F\u9636\u6BB5\u4E0E\u5B9E\u4F8B"), { code: interrupted ? "INTERRUPTED" : `CHILD_${code}` }));
643
786
  });
644
787
  });
645
788
  }
646
789
  async function readInstallation(home, recordFile = "installation.json") {
647
790
  let record;
648
791
  try {
649
- record = JSON.parse(await readFile4(join3(home, recordFile), "utf8"));
792
+ record = JSON.parse(await readFile4(join4(home, recordFile), "utf8"));
650
793
  } catch (e) {
651
794
  if (e.code === "ENOENT") return;
652
795
  throw new Error("\u5B89\u88C5\u8BB0\u5F55\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u672A\u91CD\u5EFA\u5B9E\u4F8B", { cause: e });
@@ -658,15 +801,15 @@ async function readInstallation(home, recordFile = "installation.json") {
658
801
  }
659
802
  function installationPaths(home, release) {
660
803
  const id = `${release.version}-${digest(release).slice(0, 16)}`;
661
- const directory = join3(home, "releases", id);
804
+ const directory = join4(home, "releases", id);
662
805
  const dependencyBundle = release.pythonDependencies.bundled ? `-${release.pythonDependencies.sha256.slice(0, 16)}` : "";
663
806
  return {
664
807
  directory,
665
- runtime: join3(directory, "node_modules/@huohuo/research-agent"),
666
- python: join3(home, "runtimes/python", release.python.sha256),
667
- environment: join3(home, "environments", `${release.python.sha256.slice(0, 16)}-${release.requirementsSha256.slice(0, 16)}${dependencyBundle}`),
668
- archive: join3(home, "cache", release.runtime.sha256 + ".tgz"),
669
- pythonArchive: join3(home, "cache", release.python.sha256 + ".tgz")
808
+ runtime: join4(directory, "node_modules/@huohuo/research-agent"),
809
+ python: join4(home, "runtimes/python", release.python.sha256),
810
+ environment: join4(home, "environments", `${release.python.sha256.slice(0, 16)}-${release.requirementsSha256.slice(0, 16)}${dependencyBundle}`),
811
+ archive: join4(home, "cache", release.runtime.sha256 + ".tgz"),
812
+ pythonArchive: join4(home, "cache", release.python.sha256 + ".tgz")
670
813
  };
671
814
  }
672
815
  function versionAtLeast(actual, minimum) {
@@ -688,20 +831,20 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
688
831
  home = resolve3(home);
689
832
  await mkdir3(home, { recursive: true, mode: 448 });
690
833
  home = await realpath3(home);
691
- return withLock(join3(home, "state/install.lock"), async () => {
834
+ return withLock(join4(home, "state/install.lock"), async () => {
692
835
  if (!programsOnly) {
693
836
  try {
694
- await access(join3(home, "state/default-run.lock"));
837
+ await access(join4(home, "state/default-run.lock"));
695
838
  throw new Error("\u5B9E\u4F8B\u4ECD\u6709\u8FD0\u884C\u9501\uFF1B\u5148\u6B63\u5E38\u505C\u6B62\u81EA\u5DF1\u7684\u5B9E\u4F8B\u518D\u4FEE\u590D\u7A0B\u5E8F");
696
839
  } catch (e) {
697
840
  if (e.code !== "ENOENT") throw e;
698
841
  }
699
842
  }
700
843
  const recordFile = programsOnly ? "state/upgrade-target.json" : "installation.json";
701
- const recordPath = join3(home, recordFile);
844
+ const recordPath = join4(home, recordFile);
702
845
  let record = await readInstallation(home, recordFile);
703
846
  if (!programsOnly && !record && (await readdir(home)).some((name) => name !== "state")) throw new Error("\u76EE\u5F55\u542B\u65E0\u5B89\u88C5\u8BB0\u5F55\u7684\u65E2\u6709\u5185\u5BB9\uFF1B\u8BF7\u6838\u67E5\u5F52\u5C5E\u6216\u9009\u62E9\u7A7A\u76EE\u5F55");
704
- if (!programsOnly && !record && (await readdir(join3(home, "state"))).some((name) => name !== "install.lock")) throw new Error("\u76EE\u5F55\u542B\u65E0\u6CD5\u786E\u8BA4\u5F52\u5C5E\u7684\u72B6\u6001\uFF1B\u672A\u91CD\u5EFA\u5B89\u88C5");
847
+ if (!programsOnly && !record && (await readdir(join4(home, "state"))).some((name) => name !== "install.lock")) throw new Error("\u76EE\u5F55\u542B\u65E0\u6CD5\u786E\u8BA4\u5F52\u5C5E\u7684\u72B6\u6001\uFF1B\u672A\u91CD\u5EFA\u5B89\u88C5");
705
848
  if (programsOnly && (await readInstallation(home))?.status !== "ready") throw new Error("\u5F53\u524D\u5B89\u88C5\u4E0D\u5B8C\u6574\uFF0C\u5148\u4FEE\u590D\u539F\u7248\u672C");
706
849
  const bundleDirectory = hooks.bundleDirectory;
707
850
  if (!programsOnly && record && bundleDirectory && candidate && ["installing", "blocked", "failed"].includes(record.status) && ["preflight", "download"].includes(record.stage) && Object.keys(record.stages).every((stage2) => stage2 === "preflight" || stage2 === "download")) {
@@ -718,12 +861,12 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
718
861
  const oldAssets = bundled(record.release);
719
862
  const available = await Promise.all(oldAssets.map(async (artifact2) => {
720
863
  const file = artifact2.sha256 + ".tgz";
721
- return await hasFile(join3(bundleDirectory, file)) || await hasFile(join3(home, "cache", file));
864
+ return await hasFile(join4(bundleDirectory, file)) || await hasFile(join4(home, "cache", file));
722
865
  }));
723
866
  if (available.includes(false)) {
724
867
  const next = validateRelease(candidate);
725
868
  const nextAssets = bundled(next);
726
- if (next.runtime.bundled && next.version === record.release.version && (await Promise.all(nextAssets.map((artifact2) => hasFile(join3(bundleDirectory, artifact2.sha256 + ".tgz"))))).every(Boolean)) {
869
+ if (next.runtime.bundled && next.version === record.release.version && (await Promise.all(nextAssets.map((artifact2) => hasFile(join4(bundleDirectory, artifact2.sha256 + ".tgz"))))).every(Boolean)) {
727
870
  record = void 0;
728
871
  hooks.progress?.("resume-current-package");
729
872
  }
@@ -737,12 +880,12 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
737
880
  record ??= { format: 1, release, releaseDigest: digest(release), home, status: "installing", stage: "preflight", stages: {} };
738
881
  await atomicJSON(recordPath, record);
739
882
  const paths = installationPaths(home, release);
740
- const execute4 = hooks.run ?? run;
883
+ const execute5 = hooks.run ?? run;
741
884
  const sample = hooks.sample ?? sampleResources;
742
885
  const files = [
743
886
  { name: "\u706B\u706B\u8FD0\u884C\u5305", artifact: release.runtime, path: paths.archive },
744
887
  { name: "Python \u8FD0\u884C\u73AF\u5883", artifact: release.python, path: paths.pythonArchive },
745
- { name: "Python \u4F9D\u8D56\u6E05\u5355\u6216\u79BB\u7EBF\u5305", artifact: release.pythonDependencies, path: join3(home, "cache", release.pythonDependencies.sha256 + ".tgz") }
888
+ { name: "Python \u4F9D\u8D56\u6E05\u5355\u6216\u79BB\u7EBF\u5305", artifact: release.pythonDependencies, path: join4(home, "cache", release.pythonDependencies.sha256 + ".tgz") }
746
889
  ];
747
890
  const withActivity = async (label, action) => {
748
891
  hooks.progress?.(label);
@@ -753,7 +896,7 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
753
896
  if (timer) clearInterval(timer);
754
897
  }
755
898
  };
756
- const cached = async (path, artifact2) => {
899
+ const cached2 = async (path, artifact2) => {
757
900
  try {
758
901
  await verifyArtifact(path, artifact2);
759
902
  return true;
@@ -764,22 +907,22 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
764
907
  };
765
908
  const installed = async () => {
766
909
  try {
767
- const owner = JSON.parse(await readFile4(join3(paths.python, ".huohuo-owner.json"), "utf8"));
768
- await access(join3(paths.python, release.python.executable));
910
+ const owner = JSON.parse(await readFile4(join4(paths.python, ".huohuo-owner.json"), "utf8"));
911
+ await access(join4(paths.python, release.python.executable));
769
912
  const python = owner.owner === release.python.sha256;
770
- const root = join3(home, "instances/default");
771
- const initialization = JSON.parse(await readFile4(join3(root, "initialization.json"), "utf8"));
772
- const dependencies = JSON.parse(await readFile4(join3(paths.environment, ".dependencies-ready.json"), "utf8"));
773
- const worker = JSON.parse(await readFile4(join3(root, "worker-install.json"), "utf8"));
774
- const chat = JSON.parse(await readFile4(join3(root, "dsh/profiles/huohuo-chat/installed.json"), "utf8"));
775
- for (const profile of ["huohuo-chat", "huohuo-worker", "huohuo-strategy-worker"]) await access(join3(root, "dsh/profiles", profile, "node_modules/@huohuo/research-agent/package.json"));
776
- await access(join3(paths.runtime, "lib/distribution/managed.js"));
913
+ const root = join4(home, "instances/default");
914
+ const initialization = JSON.parse(await readFile4(join4(root, "initialization.json"), "utf8"));
915
+ const dependencies = JSON.parse(await readFile4(join4(paths.environment, ".dependencies-ready.json"), "utf8"));
916
+ const worker = JSON.parse(await readFile4(join4(root, "worker-install.json"), "utf8"));
917
+ const chat = JSON.parse(await readFile4(join4(root, "dsh/profiles/huohuo-chat/installed.json"), "utf8"));
918
+ for (const profile of ["huohuo-chat", "huohuo-worker", "huohuo-strategy-worker"]) await access(join4(root, "dsh/profiles", profile, "node_modules/@huohuo/research-agent/package.json"));
919
+ await access(join4(paths.runtime, "lib/distribution/managed.js"));
777
920
  return { python, dependencies: initialization.package_sha256 === release.runtime.sha256 && initialization.status === "complete" && dependencies.sha256 === release.pythonDependencies.sha256 && worker.complete === true && chat.complete === true };
778
921
  } catch (error) {
779
922
  if (error.code !== "ENOENT") throw error;
780
923
  try {
781
- const owner = JSON.parse(await readFile4(join3(paths.python, ".huohuo-owner.json"), "utf8"));
782
- await access(join3(paths.python, release.python.executable));
924
+ const owner = JSON.parse(await readFile4(join4(paths.python, ".huohuo-owner.json"), "utf8"));
925
+ await access(join4(paths.python, release.python.executable));
783
926
  return { python: owner.owner === release.python.sha256, dependencies: false };
784
927
  } catch {
785
928
  return { python: false, dependencies: false };
@@ -787,19 +930,19 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
787
930
  }
788
931
  };
789
932
  const check = async () => {
790
- const [cacheHits, inventory] = await Promise.all([Promise.all(files.map((file) => cached(file.path, file.artifact))), installed()]);
933
+ const [cacheHits, inventory] = await Promise.all([Promise.all(files.map((file) => cached2(file.path, file.artifact))), installed()]);
791
934
  const remainingDependencies = inventory.dependencies ? 0 : release.budget.dependenciesBytes;
792
935
  const costs = [
793
- { path: join3(home, "cache"), bytes: files.reduce((sum, file, index) => sum + (cacheHits[index] ? 0 : file.artifact.downloadBytes), 0) },
936
+ { path: join4(home, "cache"), bytes: files.reduce((sum, file, index) => sum + (cacheHits[index] ? 0 : file.artifact.downloadBytes), 0) },
794
937
  { path: paths.directory, bytes: inventory.dependencies ? 0 : release.runtime.expandedBytes + release.budget.installTemporaryBytes },
795
938
  { path: paths.python, bytes: inventory.python ? 0 : release.python.expandedBytes },
796
939
  { path: paths.environment, bytes: 0 },
797
- { path: join3(home, "instances/default"), bytes: 0 },
940
+ { path: join4(home, "instances/default"), bytes: 0 },
798
941
  { path: paths.directory, bytes: inventory.dependencies ? 0 : release.pythonDependencies.expandedBytes },
799
- { path: join3(home, "backups"), bytes: release.budget.backupBytes }
942
+ { path: join4(home, "backups"), bytes: release.budget.backupBytes }
800
943
  ];
801
944
  const observation = await sample(costs.map((c) => c.path));
802
- const dependencyPaths = [paths.directory, paths.environment, join3(home, "instances/default")];
945
+ const dependencyPaths = [paths.directory, paths.environment, join4(home, "instances/default")];
803
946
  const volumes = /* @__PURE__ */ new Set();
804
947
  for (const path of dependencyPaths) {
805
948
  const device = diskBudgets(observation, [{ path, bytes: 0 }], 0)[0].device;
@@ -809,7 +952,7 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
809
952
  }
810
953
  }
811
954
  const result = evaluateResources(observation, release.budget, costs, "install");
812
- await atomicJSON(join3(home, "state/resources.json"), result);
955
+ await atomicJSON(join4(home, "state/resources.json"), result);
813
956
  if (result.status === "unknown" || result.status === "blocked") {
814
957
  const details = result.reasons.map((reason) => `- ${reason}`).join("\n");
815
958
  throw Object.assign(new Error(`\u706B\u706B\u6682\u65F6\u65E0\u6CD5\u7EE7\u7EED\u5B89\u88C5\u3002
@@ -856,9 +999,9 @@ ${details}
856
999
  };
857
1000
  await stage("preflight", async (plan) => {
858
1001
  const npm = npmCommand();
859
- const { stdout } = await promisify3(execFile3)(npm.command, [...npm.prefix, "--version"], { timeout: 5e3 });
1002
+ const { stdout } = await promisify4(execFile5)(npm.command, [...npm.prefix, "--version"], { timeout: 5e3 });
860
1003
  if (!/^\d+\.\d+\.\d+\s*$/.test(stdout) || Number(stdout.split(".")[0]) !== release.npmMajor) throw new Error("npm \u7248\u672C\u672A\u77E5\u6216\u4E0D\u5728\u6B64\u53D1\u884C\u5DF2\u9A8C\u8BC1\u8303\u56F4\u5185");
861
- const probe = join3(home, `.write-probe-${randomUUID3()}`);
1004
+ const probe = join4(home, `.write-probe-${randomUUID3()}`);
862
1005
  try {
863
1006
  await atomicJSON(probe, { probe: true });
864
1007
  } finally {
@@ -888,19 +1031,19 @@ ${details}
888
1031
  await stage("install-runtime", async () => {
889
1032
  try {
890
1033
  await access(paths.python);
891
- const marker = JSON.parse(await readFile4(join3(paths.python, ".huohuo-owner.json"), "utf8"));
1034
+ const marker = JSON.parse(await readFile4(join4(paths.python, ".huohuo-owner.json"), "utf8"));
892
1035
  if (marker.owner !== release.python.sha256) throw new Error("\u53D7\u7BA1 Python \u5F52\u5C5E\u4E0D\u7B26");
893
- await access(join3(paths.python, release.python.executable));
1036
+ await access(join4(paths.python, release.python.executable));
894
1037
  return release.python.sha256;
895
1038
  } catch (e) {
896
1039
  if (e.code !== "ENOENT") throw e;
897
1040
  }
898
- await mkdir3(join3(home, "runtimes/python"), { recursive: true, mode: 448 });
1041
+ await mkdir3(join4(home, "runtimes/python"), { recursive: true, mode: 448 });
899
1042
  const staging = `${paths.python}.${randomUUID3()}.staging`;
900
1043
  try {
901
1044
  await extractArchive(paths.pythonArchive, staging, release.python.expandedBytes);
902
- await access(join3(staging, release.python.executable));
903
- await atomicJSON(join3(staging, ".huohuo-owner.json"), { format: 1, owner: release.python.sha256 });
1045
+ await access(join4(staging, release.python.executable));
1046
+ await atomicJSON(join4(staging, ".huohuo-owner.json"), { format: 1, owner: release.python.sha256 });
904
1047
  await rename3(staging, paths.python);
905
1048
  } finally {
906
1049
  await rm2(staging, { recursive: true, force: true });
@@ -909,30 +1052,30 @@ ${details}
909
1052
  });
910
1053
  await stage("install-dependencies", async () => {
911
1054
  await ownedDirectory(paths.directory, record.releaseDigest);
912
- await atomicJSON(join3(paths.directory, "package.json"), {
1055
+ await atomicJSON(join4(paths.directory, "package.json"), {
913
1056
  name: "huohuo-managed-release",
914
1057
  private: true,
915
1058
  dependencies: { "@huohuo/research-agent": `file:${paths.archive}` }
916
1059
  });
917
1060
  const npm = npmCommand();
918
- await withActivity("\u5B89\u88C5 npm \u4F9D\u8D56", () => execute4(npm.command, [...npm.prefix, "install", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund", "--prefer-offline", "--cache", join3(home, "cache/npm")], paths.directory));
919
- const runtime = JSON.parse(await readFile4(join3(paths.runtime, "package.json"), "utf8"));
1061
+ await withActivity("\u5B89\u88C5 npm \u4F9D\u8D56", () => execute5(npm.command, [...npm.prefix, "install", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund", "--prefer-offline", "--cache", join4(home, "cache/npm")], paths.directory));
1062
+ const runtime = JSON.parse(await readFile4(join4(paths.runtime, "package.json"), "utf8"));
920
1063
  if (runtime.name !== release.runtime.name || runtime.version !== release.version) throw new Error("\u8FD0\u884C\u5305\u8EAB\u4EFD\u4E0D\u7B26");
921
- const requirements = await readFile4(join3(paths.runtime, "sidecar/requirements.lock"));
1064
+ const requirements = await readFile4(join4(paths.runtime, "sidecar/requirements.lock"));
922
1065
  if (createHash2("sha256").update(requirements).digest("hex") !== release.requirementsSha256) throw new Error("Python \u4F9D\u8D56\u9501\u6458\u8981\u4E0D\u7B26");
923
- const assembly = await readFile4(join3(paths.runtime, "npm-shrinkwrap.json"));
1066
+ const assembly = await readFile4(join4(paths.runtime, "npm-shrinkwrap.json"));
924
1067
  if (createHash2("sha256").update(assembly).digest("hex") !== release.dsh.assemblySha256) throw new Error("DSH \u88C5\u914D\u9501\u6458\u8981\u4E0D\u7B26");
925
1068
  await ownedDirectory(paths.environment, `${release.python.sha256}:${release.requirementsSha256}`);
926
- const python = join3(paths.environment, process.platform === "win32" ? "Scripts/python.exe" : "bin/python");
927
- const wheels = join3(paths.directory, `wheels-${release.pythonDependencies.sha256}`);
1069
+ const python = join4(paths.environment, process.platform === "win32" ? "Scripts/python.exe" : "bin/python");
1070
+ const wheels = join4(paths.directory, `wheels-${release.pythonDependencies.sha256}`);
928
1071
  try {
929
- await access(join3(wheels, "requirements.txt"));
1072
+ await access(join4(wheels, "requirements.txt"));
930
1073
  } catch (e) {
931
1074
  if (e.code !== "ENOENT") throw e;
932
1075
  const staging = `${wheels}.${randomUUID3()}.staging`;
933
1076
  try {
934
- await extractArchive(join3(home, "cache", release.pythonDependencies.sha256 + ".tgz"), staging, release.pythonDependencies.expandedBytes);
935
- await access(join3(staging, "requirements.txt"));
1077
+ await extractArchive(join4(home, "cache", release.pythonDependencies.sha256 + ".tgz"), staging, release.pythonDependencies.expandedBytes);
1078
+ await access(join4(staging, "requirements.txt"));
936
1079
  await rename3(staging, wheels);
937
1080
  } finally {
938
1081
  await rm2(staging, { recursive: true, force: true });
@@ -940,7 +1083,7 @@ ${details}
940
1083
  }
941
1084
  let environmentReady = false;
942
1085
  try {
943
- const ready = JSON.parse(await readFile4(join3(paths.environment, ".dependencies-ready.json"), "utf8"));
1086
+ const ready = JSON.parse(await readFile4(join4(paths.environment, ".dependencies-ready.json"), "utf8"));
944
1087
  if (ready.sha256 !== release.pythonDependencies.sha256) throw new Error("\u5DF2\u6709\u73AF\u5883\u7ED1\u5B9A\u4E0D\u540C wheel \u4EA7\u7269\uFF1B\u4E0D\u80FD\u539F\u4F4D\u6539\u5199\u5171\u4EAB\u73AF\u5883");
945
1088
  await access(python);
946
1089
  environmentReady = true;
@@ -948,13 +1091,13 @@ ${details}
948
1091
  if (e.code !== "ENOENT") throw e;
949
1092
  }
950
1093
  if (!environmentReady) {
951
- await execute4(join3(paths.python, release.python.executable), ["-m", "venv", paths.environment], home);
1094
+ await execute5(join4(paths.python, release.python.executable), ["-m", "venv", paths.environment], home);
952
1095
  if (release.pythonDependencies.bundled && hooks.progress) {
953
- const lock = await readFile4(join3(wheels, "requirements.txt"), "utf8");
1096
+ const lock = await readFile4(join4(wheels, "requirements.txt"), "utf8");
954
1097
  const upstream = lock.split("\n").filter((line) => /^[\w.-]+\s+@\s+https:\/\//.test(line)).length;
955
1098
  if (upstream) hooks.progress?.(`Python \u4F9D\u8D56\u6E05\u5355\u6709 ${upstream} \u4E2A\u4E0A\u6E38\u6587\u4EF6\uFF0C\u4E0B\u65B9\u663E\u793A\u6B63\u5728\u5904\u7406\u7684\u6587\u4EF6`);
956
1099
  }
957
- await withActivity("\u5B89\u88C5 Python \u4F9D\u8D56", () => execute4(python, [
1100
+ await withActivity("\u5B89\u88C5 Python \u4F9D\u8D56", () => execute5(python, [
958
1101
  "-m",
959
1102
  "pip",
960
1103
  "--isolated",
@@ -966,11 +1109,15 @@ ${details}
966
1109
  "--only-binary=:all:",
967
1110
  ...release.pythonDependencies.bundled ? ["--no-cache-dir"] : [],
968
1111
  "-r",
969
- join3(wheels, "requirements.txt")
1112
+ join4(wheels, "requirements.txt")
970
1113
  ], home));
971
1114
  }
972
- await execute4(python, ["-m", "pip", "--isolated", "check"], home);
973
- await atomicJSON(join3(paths.environment, ".dependencies-ready.json"), { sha256: release.pythonDependencies.sha256 });
1115
+ await execute5(python, ["-m", "pip", "--isolated", "check"], home);
1116
+ if (process.platform === "win32") {
1117
+ hooks.progress?.("\u68C0\u67E5 Windows Python \u4E0E\u672C\u5730\u4F9D\u8D56\u8FD0\u884C\u5E93");
1118
+ await execute5(python, ["-c", 'import numpy,pandas,lxml.etree,pydantic_core,akshare; from py_mini_racer import MiniRacer; ctx=MiniRacer(); assert ctx.eval("1+1")==2; ctx.close(); print("Windows Python \u4F9D\u8D56\u52A0\u8F7D\u68C0\u67E5\u901A\u8FC7")'], home);
1119
+ }
1120
+ await atomicJSON(join4(paths.environment, ".dependencies-ready.json"), { sha256: release.pythonDependencies.sha256 });
974
1121
  return { python, runtime: release.runtime.sha256 };
975
1122
  });
976
1123
  if (programsOnly) {
@@ -980,11 +1127,11 @@ ${details}
980
1127
  return record;
981
1128
  }
982
1129
  await stage("assemble-profiles", async () => {
983
- await withActivity("\u914D\u7F6E\u5BF9\u8BDD\u4E0E\u540E\u53F0\u8FD0\u884C\u4F9D\u8D56", () => execute4(process.execPath, [join3(paths.runtime, "lib/distribution/managed.js"), "prepare", home], home));
1130
+ await withActivity("\u914D\u7F6E\u5BF9\u8BDD\u4E0E\u540E\u53F0\u8FD0\u884C\u4F9D\u8D56", () => execute5(process.execPath, [join4(paths.runtime, "lib/distribution/managed.js"), "prepare", home], home));
984
1131
  return { release: record.releaseDigest };
985
1132
  });
986
1133
  await stage("verify", async () => {
987
- await execute4(process.execPath, [join3(paths.runtime, "lib/distribution/managed.js"), "verify-live", home], home);
1134
+ await execute5(process.execPath, [join4(paths.runtime, "lib/distribution/managed.js"), "verify-live", home], home);
988
1135
  return { release: record.releaseDigest };
989
1136
  });
990
1137
  record.status = "ready";
@@ -997,14 +1144,14 @@ ${details}
997
1144
  // distribution/bootstrap/control.ts
998
1145
  import { createServer, request as httpRequest } from "node:http";
999
1146
  import { readFile as readFile5, rm as rm3 } from "node:fs/promises";
1000
- import { join as join4 } from "node:path";
1001
- import { execFile as execFile4 } from "node:child_process";
1002
- import { promisify as promisify4 } from "node:util";
1003
- var execute3 = promisify4(execFile4);
1147
+ import { join as join5 } from "node:path";
1148
+ import { execFile as execFile6 } from "node:child_process";
1149
+ import { promisify as promisify5 } from "node:util";
1150
+ var execute4 = promisify5(execFile6);
1004
1151
  async function control(home, releaseDigest, command) {
1005
1152
  let binding;
1006
1153
  try {
1007
- binding = JSON.parse(await readFile5(join4(home, "state/default-control.json"), "utf8"));
1154
+ binding = JSON.parse(await readFile5(join5(home, "state/default-control.json"), "utf8"));
1008
1155
  } catch (e) {
1009
1156
  if (e.code === "ENOENT") return false;
1010
1157
  throw new Error("\u672C\u673A\u8FD0\u884C\u8BB0\u5F55\u65E0\u6CD5\u6838\u9A8C");
@@ -1040,7 +1187,7 @@ async function control(home, releaseDigest, command) {
1040
1187
  req.end();
1041
1188
  });
1042
1189
  } catch (error) {
1043
- if (error.code === "ECONNREFUSED" && await isLockOwnerDead(join4(home, "state/default-run.lock"), binding.runOwner)) return false;
1190
+ if (error.code === "ECONNREFUSED" && await isLockOwnerDead(join5(home, "state/default-run.lock"), binding.runOwner)) return false;
1044
1191
  throw error;
1045
1192
  }
1046
1193
  return true;
@@ -1048,13 +1195,13 @@ async function control(home, releaseDigest, command) {
1048
1195
 
1049
1196
  // distribution/bootstrap/maintenance.ts
1050
1197
  import { access as access2, readFile as readFile6, readdir as readdir2, lstat as lstat2, rename as rename4, rm as rm4, realpath as realpath4 } from "node:fs/promises";
1051
- import { join as join5, resolve as resolve4 } from "node:path";
1198
+ import { join as join6, resolve as resolve4, sep as sep3 } from "node:path";
1052
1199
  import { randomUUID as randomUUID4 } from "node:crypto";
1053
1200
  import { setTimeout as delay } from "node:timers/promises";
1054
1201
  async function upgradeJournal(home) {
1055
1202
  try {
1056
- const value = JSON.parse(await readFile6(join5(home, "state/upgrade.json"), "utf8"));
1057
- if (value.format !== 1 || !/^[a-f0-9]{64}$/.test(value.from) || !/^[a-f0-9]{64}$/.test(value.to) || !["stopping", "backed-up", "schema-unchanged", "profiles", "migrated", "verified", "complete"].includes(value.stage) || !value.backup.startsWith(join5(home, "backups") + "/")) throw new Error("\u5347\u7EA7\u8BB0\u5F55\u65E0\u6548");
1203
+ const value = JSON.parse(await readFile6(join6(home, "state/upgrade.json"), "utf8"));
1204
+ if (value.format !== 1 || !/^[a-f0-9]{64}$/.test(value.from) || !/^[a-f0-9]{64}$/.test(value.to) || !["stopping", "backed-up", "schema-unchanged", "profiles", "migrated", "verified", "complete"].includes(value.stage) || !value.backup.startsWith(join6(home, "backups") + sep3)) throw new Error("\u5347\u7EA7\u8BB0\u5F55\u65E0\u6548");
1058
1205
  return value;
1059
1206
  } catch (error) {
1060
1207
  if (error.code === "ENOENT") return;
@@ -1078,7 +1225,7 @@ async function stopOwned(home, releaseDigest) {
1078
1225
  if (!await control(home, releaseDigest, "stop")) return;
1079
1226
  for (let i = 0; i < 100; i++) {
1080
1227
  try {
1081
- await access2(join5(home, "state/default-run.lock"));
1228
+ await access2(join6(home, "state/default-run.lock"));
1082
1229
  } catch (error) {
1083
1230
  if (error.code === "ENOENT") return;
1084
1231
  throw error;
@@ -1093,9 +1240,9 @@ async function backupBytes(directory) {
1093
1240
  for (const item of await readdir2(path, { withFileTypes: true })) {
1094
1241
  if (["node_modules", "cache", "python", "__pycache__"].includes(item.name)) continue;
1095
1242
  if (item.isSymbolicLink()) throw new Error("\u5907\u4EFD\u4E0D\u63A5\u7BA1\u672A\u77E5\u7684\u5B9E\u4F8B\u94FE\u63A5");
1096
- if (item.isDirectory()) await visit(join5(path, item.name));
1243
+ if (item.isDirectory()) await visit(join6(path, item.name));
1097
1244
  else {
1098
- const info = await lstat2(join5(path, item.name));
1245
+ const info = await lstat2(join6(path, item.name));
1099
1246
  bytes += info.size + 512;
1100
1247
  }
1101
1248
  }
@@ -1113,19 +1260,19 @@ async function upgrade(home, candidate, hooks = {}) {
1113
1260
  if (journal && journal.stage !== "complete" && journal.to !== digest(next)) throw new Error("\u5B58\u5728\u672A\u5B8C\u6210\u5347\u7EA7\uFF1B\u5FC5\u987B\u5148\u7EE7\u7EED\u539F\u56FA\u5B9A\u76EE\u6807");
1114
1261
  if (current.releaseDigest === digest(next)) {
1115
1262
  if (journal && journal.to === current.releaseDigest && journal.stage !== "complete") {
1116
- await (hooks.run ?? run)(process.execPath, [join5(installationPaths(home, next).runtime, "lib/distribution/managed.js"), "verify-live", home], home);
1263
+ await (hooks.run ?? run)(process.execPath, [join6(installationPaths(home, next).runtime, "lib/distribution/managed.js"), "verify-live", home], home);
1117
1264
  journal.stage = "complete";
1118
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1265
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1119
1266
  }
1120
1267
  hooks.progress?.("\u5DF2\u7ECF\u662F\u540C\u4E00\u53D1\u884C\uFF0C\u65E0\u9700\u8FC1\u79FB");
1121
1268
  return;
1122
1269
  }
1123
1270
  assertCompatibleUpgrade(current.release, next);
1124
1271
  if (journal?.stage === "complete") {
1125
- await withLock(join5(home, "state/install.lock"), async () => {
1126
- await rename4(join5(home, "state/upgrade.json"), join5(home, `state/upgrade-${randomUUID4()}.json`));
1272
+ await withLock(join6(home, "state/install.lock"), async () => {
1273
+ await rename4(join6(home, "state/upgrade.json"), join6(home, `state/upgrade-${randomUUID4()}.json`));
1127
1274
  try {
1128
- await rename4(join5(home, "state/upgrade-target.json"), join5(home, `state/upgrade-target-${randomUUID4()}.json`));
1275
+ await rename4(join6(home, "state/upgrade-target.json"), join6(home, `state/upgrade-target-${randomUUID4()}.json`));
1129
1276
  } catch (error) {
1130
1277
  if (error.code !== "ENOENT") throw error;
1131
1278
  }
@@ -1134,58 +1281,58 @@ async function upgrade(home, candidate, hooks = {}) {
1134
1281
  }
1135
1282
  const target = await install(home, next, hooks, true);
1136
1283
  if (target.releaseDigest !== digest(next)) throw new Error("\u5347\u7EA7\u6062\u590D\u76EE\u6807\u4E0E\u8BF7\u6C42\u4E0D\u540C\uFF1B\u4FDD\u7559\u539F\u56FA\u5B9A\u76EE\u6807");
1137
- const execute4 = hooks.run ?? run;
1138
- await withLock(join5(home, "state/install.lock"), async () => {
1284
+ const execute5 = hooks.run ?? run;
1285
+ await withLock(join6(home, "state/install.lock"), async () => {
1139
1286
  current = await readInstallation(home);
1140
1287
  if (journal && journal.from !== current.releaseDigest) throw new Error("\u5347\u7EA7\u671F\u95F4\u539F\u53D1\u884C\u7ED1\u5B9A\u53D8\u5316\uFF1B\u8BF7\u5148\u5BF9\u8D26");
1141
1288
  journal ??= {
1142
1289
  format: 1,
1143
1290
  from: current.releaseDigest,
1144
1291
  to: target.releaseDigest,
1145
- backup: join5(home, "backups", `upgrade-${target.releaseDigest.slice(0, 16)}-${randomUUID4()}`),
1292
+ backup: join6(home, "backups", `upgrade-${target.releaseDigest.slice(0, 16)}-${randomUUID4()}`),
1146
1293
  stage: "stopping"
1147
1294
  };
1148
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1295
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1149
1296
  await stopOwned(home, current.releaseDigest);
1150
- const releaseRun = await acquireLock(join5(home, "state/default-run.lock"));
1297
+ const releaseRun = await acquireLock(join6(home, "state/default-run.lock"));
1151
1298
  try {
1152
- const bytes = await backupBytes(join5(home, "instances/default"));
1299
+ const bytes = await backupBytes(join6(home, "instances/default"));
1153
1300
  const result = evaluateResources(
1154
- await (hooks.sample ?? sampleResources)([join5(home, "backups")]),
1301
+ await (hooks.sample ?? sampleResources)([join6(home, "backups")]),
1155
1302
  target.release.budget,
1156
- [{ path: join5(home, "backups"), bytes }],
1303
+ [{ path: join6(home, "backups"), bytes }],
1157
1304
  "install"
1158
1305
  );
1159
1306
  if (result.status === "unknown" || result.status === "blocked") throw new Error("\u4E00\u81F4\u6027\u5907\u4EFD\u8D44\u6E90\u4E0D\u8DB3\uFF1B\u539F\u6570\u636E\u4FDD\u7559");
1160
1307
  if (journal.stage === "stopping") {
1161
- await execute4(process.execPath, [join5(installationPaths(home, current.release).runtime, "lib/distribution/managed.js"), "backup", home], home);
1308
+ await execute5(process.execPath, [join6(installationPaths(home, current.release).runtime, "lib/distribution/managed.js"), "backup", home], home);
1162
1309
  journal.stage = "backed-up";
1163
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1310
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1164
1311
  }
1165
1312
  const runtime = installationPaths(home, target.release).runtime;
1166
- await execute4(process.execPath, [join5(runtime, "lib/distribution/managed.js"), "prepare-upgrade", home], home);
1313
+ await execute5(process.execPath, [join6(runtime, "lib/distribution/managed.js"), "prepare-upgrade", home], home);
1167
1314
  journal.stage = "profiles";
1168
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1315
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1169
1316
  if (current.release.schema.migrationsSha256 !== target.release.schema.migrationsSha256) {
1170
1317
  const space = evaluateResources(
1171
- await (hooks.sample ?? sampleResources)([join5(home, "instances/default")]),
1318
+ await (hooks.sample ?? sampleResources)([join6(home, "instances/default")]),
1172
1319
  target.release.budget,
1173
- [{ path: join5(home, "instances/default"), bytes: bytes + target.release.budget.installTemporaryBytes }],
1320
+ [{ path: join6(home, "instances/default"), bytes: bytes + target.release.budget.installTemporaryBytes }],
1174
1321
  "install"
1175
1322
  );
1176
1323
  if (space.status === "blocked" || space.status === "unknown") throw new Error("\u8FC1\u79FB\u524D\u8D44\u6E90\u4E0D\u8DB3\uFF1B\u4FDD\u7559\u5347\u7EA7\u65E5\u5FD7\u548C\u5F53\u524D\u6570\u636E");
1177
- await execute4(process.execPath, [join5(runtime, "lib/distribution/managed.js"), "migrate-upgrade", home], home);
1324
+ await execute5(process.execPath, [join6(runtime, "lib/distribution/managed.js"), "migrate-upgrade", home], home);
1178
1325
  }
1179
1326
  journal.stage = "migrated";
1180
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1181
- await execute4(process.execPath, [join5(runtime, "lib/distribution/managed.js"), "verify-upgrade", home], home);
1327
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1328
+ await execute5(process.execPath, [join6(runtime, "lib/distribution/managed.js"), "verify-upgrade", home], home);
1182
1329
  journal.stage = "verified";
1183
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1330
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1184
1331
  target.status = "ready";
1185
1332
  target.stage = "ready";
1186
- await atomicJSON(join5(home, "installation.json"), target);
1333
+ await atomicJSON(join6(home, "installation.json"), target);
1187
1334
  journal.stage = "complete";
1188
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1335
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1189
1336
  hooks.progress?.("\u5347\u7EA7\u9A8C\u8BC1\u901A\u8FC7\uFF0C\u5DF2\u5207\u6362\u53D1\u884C\u7ED1\u5B9A\uFF1B\u5907\u4EFD\u4FDD\u7559");
1190
1337
  } finally {
1191
1338
  await releaseRun();
@@ -1194,7 +1341,7 @@ async function upgrade(home, candidate, hooks = {}) {
1194
1341
  }
1195
1342
  async function removeOwned(path, expected) {
1196
1343
  try {
1197
- const marker = JSON.parse(await readFile6(join5(path, ".huohuo-owner.json"), "utf8"));
1344
+ const marker = JSON.parse(await readFile6(join6(path, ".huohuo-owner.json"), "utf8"));
1198
1345
  if (marker.format !== 1 || marker.owner !== expected) throw new Error("\u53D7\u7BA1\u76EE\u5F55\u5F52\u5C5E\u4E0D\u7B26\uFF0C\u672A\u5220\u9664");
1199
1346
  await rm4(path, { recursive: true });
1200
1347
  } catch (error) {
@@ -1203,25 +1350,25 @@ async function removeOwned(path, expected) {
1203
1350
  }
1204
1351
  async function uninstall(home, hooks = {}) {
1205
1352
  home = await realpath4(resolve4(home));
1206
- await withLock(join5(home, "state/install.lock"), async () => {
1353
+ await withLock(join6(home, "state/install.lock"), async () => {
1207
1354
  const current = await readInstallation(home);
1208
1355
  if (!current) throw new Error("\u6CA1\u6709\u53EF\u6838\u9A8C\u7684\u6301\u4E45\u5B89\u88C5");
1209
1356
  const journal = await upgradeJournal(home);
1210
1357
  if (journal && journal.stage !== "complete") throw new Error("\u5347\u7EA7\u5C1A\u672A\u5BF9\u8D26\uFF0C\u4E0D\u80FD\u5378\u8F7D\u7A0B\u5E8F");
1211
1358
  await stopOwned(home, current.releaseDigest);
1212
- const releaseRun = await acquireLock(join5(home, "state/default-run.lock"));
1359
+ const releaseRun = await acquireLock(join6(home, "state/default-run.lock"));
1213
1360
  try {
1214
1361
  const paths = installationPaths(home, current.release);
1215
- if ((await readdir2(join5(home, "instances"))).some((name) => name !== "default")) throw new Error("\u68C0\u6D4B\u5230\u5176\u4ED6\u5B9E\u4F8B\uFF1B\u5171\u4EAB\u8FD0\u884C\u73AF\u5883\u5F52\u5C5E\u9700\u5148\u6838\u67E5\uFF0C\u672A\u5220\u9664");
1362
+ if ((await readdir2(join6(home, "instances"))).some((name) => name !== "default")) throw new Error("\u68C0\u6D4B\u5230\u5176\u4ED6\u5B9E\u4F8B\uFF1B\u5171\u4EAB\u8FD0\u884C\u73AF\u5883\u5F52\u5C5E\u9700\u5148\u6838\u67E5\uFF0C\u672A\u5220\u9664");
1216
1363
  if (current.status !== "uninstalled") {
1217
- await (hooks.run ?? run)(process.execPath, [join5(paths.runtime, "lib/distribution/managed.js"), "uninstall-programs", home], home);
1364
+ await (hooks.run ?? run)(process.execPath, [join6(paths.runtime, "lib/distribution/managed.js"), "uninstall-programs", home], home);
1218
1365
  current.status = "uninstalled";
1219
1366
  current.stage = "uninstalled";
1220
- await atomicJSON(join5(home, "installation.json"), current);
1367
+ await atomicJSON(join6(home, "installation.json"), current);
1221
1368
  }
1222
1369
  await removeOwned(paths.directory, current.releaseDigest);
1223
1370
  await removeOwned(paths.environment, `${current.release.python.sha256}:${current.release.requirementsSha256}`);
1224
- if (!(await readdir2(join5(home, "releases"))).length) await removeOwned(paths.python, current.release.python.sha256);
1371
+ if (!(await readdir2(join6(home, "releases"))).length) await removeOwned(paths.python, current.release.python.sha256);
1225
1372
  hooks.progress?.(`\u5DF2\u5378\u8F7D\u53EF\u518D\u751F\u6210\u7A0B\u5E8F\uFF1B\u5B9E\u4F8B\u3001\u51ED\u636E\u3001\u4F1A\u8BDD\u548C\u5907\u4EFD\u4FDD\u7559\u5728 ${home}`);
1226
1373
  } finally {
1227
1374
  await releaseRun();
@@ -1232,7 +1379,7 @@ async function uninstall(home, hooks = {}) {
1232
1379
  // distribution/bootstrap/install-ui.ts
1233
1380
  import { createInterface } from "node:readline";
1234
1381
  import { homedir } from "node:os";
1235
- import { relative, sep as sep3 } from "node:path";
1382
+ import { relative, sep as sep4 } from "node:path";
1236
1383
  var gb = (bytes) => bytes === null ? "\u672A\u77E5" : `${(bytes / 1e9).toFixed(2)} GB`;
1237
1384
  var mb = (bytes) => bytes < 1e5 ? `${(bytes / 1e3).toFixed(1)} KB` : `${(bytes / 1e6).toFixed(1)} MB`;
1238
1385
  var memory = (bytes) => {
@@ -1242,7 +1389,7 @@ var memory = (bytes) => {
1242
1389
  function displayPath(path) {
1243
1390
  const local = relative(homedir(), path);
1244
1391
  if (!local) return "~";
1245
- return local === ".." || local.startsWith(`..${sep3}`) ? path : `~${sep3}${local}`;
1392
+ return local === ".." || local.startsWith(`..${sep4}`) ? path : `~${sep4}${local}`;
1246
1393
  }
1247
1394
  function installUI(assumeYes) {
1248
1395
  const interactive = process.stdin.isTTY && process.stderr.isTTY;
@@ -1345,7 +1492,7 @@ function installUI(assumeYes) {
1345
1492
 
1346
1493
  // distribution/bootstrap/status-ui.ts
1347
1494
  import { readFile as readFile7 } from "node:fs/promises";
1348
- import { join as join6 } from "node:path";
1495
+ import { join as join7 } from "node:path";
1349
1496
  import { setTimeout as delay2 } from "node:timers/promises";
1350
1497
  var mib = (bytes) => typeof bytes === "number" && Number.isFinite(bytes) ? `${Math.round(bytes / 2 ** 20)} MiB` : "\u672A\u77E5";
1351
1498
  var pressureLabels = { normal: "\u6B63\u5E38", high: "\u504F\u9AD8", critical: "\u4E25\u91CD", unknown: "\u672A\u77E5" };
@@ -1356,7 +1503,7 @@ async function statusLines(home) {
1356
1503
  if (installed.status !== "ready") return [title, "\u5B89\u88C5\u5C1A\u672A\u5B8C\u6210\u3002"];
1357
1504
  let admission;
1358
1505
  try {
1359
- admission = JSON.parse(await readFile7(join6(home, "state/default-admission.json"), "utf8"));
1506
+ admission = JSON.parse(await readFile7(join7(home, "state/default-admission.json"), "utf8"));
1360
1507
  } catch (error) {
1361
1508
  if (error.code !== "ENOENT") return [title, "\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u8D44\u6E90\u72B6\u6001\u3002"];
1362
1509
  }
@@ -1425,10 +1572,10 @@ async function showStatus(home, watch) {
1425
1572
  // distribution/bootstrap/assets.ts
1426
1573
  import { createRequire } from "node:module";
1427
1574
  import { readFile as readFile8 } from "node:fs/promises";
1428
- import { dirname as dirname5, join as join7 } from "node:path";
1575
+ import { dirname as dirname5, join as join8 } from "node:path";
1429
1576
  import { fileURLToPath } from "node:url";
1430
1577
  async function assetsDirectory(release, entryURL = import.meta.url) {
1431
- if (!release?.assetPackage) return join7(dirname5(fileURLToPath(entryURL)), "assets");
1578
+ if (!release?.assetPackage) return join8(dirname5(fileURLToPath(entryURL)), "assets");
1432
1579
  const expected = release.assetPackage;
1433
1580
  let file;
1434
1581
  try {
@@ -1440,7 +1587,7 @@ async function assetsDirectory(release, entryURL = import.meta.url) {
1440
1587
  if (manifest.name !== expected.name || manifest.version !== expected.version) {
1441
1588
  throw new Error("\u706B\u706B\u5165\u53E3\u4E0E\u5E73\u53F0\u8D44\u6E90\u5305\u7248\u672C\u4E0D\u4E00\u81F4\u3002\u8BF7\u91CD\u65B0\u8FD0\u884C\u540C\u4E00\u7248\u672C\u7684\u5B8C\u6574\u5B89\u88C5\u547D\u4EE4\u3002");
1442
1589
  }
1443
- return join7(dirname5(file), "assets");
1590
+ return join8(dirname5(file), "assets");
1444
1591
  }
1445
1592
 
1446
1593
  // distribution/bootstrap/entry.ts
@@ -1452,7 +1599,7 @@ function artifactSource(source, directory) {
1452
1599
  const sha256 = bundled?.[1] ?? artifact2?.sha256;
1453
1600
  if (!sha256) throw new Error("\u672C\u673A\u4EA7\u7269\u76EE\u5F55\u7F3A\u5C11\u8BE5\u56FA\u5B9A\u53D1\u884C\u8D44\u6E90");
1454
1601
  const root = directory ?? await assetsDirectory(source, import.meta.url);
1455
- const path = join8(root, sha256 + ".tgz");
1602
+ const path = join9(root, sha256 + ".tgz");
1456
1603
  const file = await open3(path).catch((error) => {
1457
1604
  if (error.code === "ENOENT" && bundled) throw new Error("\u7F3A\u5C11\u4E0A\u6B21\u5B89\u88C5\u4F7F\u7528\u7684\u6587\u4EF6\u3002\u8BF7\u7528\u4E0A\u6B21\u5B89\u88C5\u65F6\u7684\u7248\u672C\u7EE7\u7EED\u3002");
1458
1605
  throw error;
@@ -1461,7 +1608,7 @@ function artifactSource(source, directory) {
1461
1608
  };
1462
1609
  }
1463
1610
  async function main(args = process.argv.slice(2)) {
1464
- let home = join8(homedir2(), ".huohuo"), noOpen = false, assumeYes = false, watch = false, command = "start";
1611
+ let home = join9(homedir2(), ".huohuo"), noOpen = false, assumeYes = false, watch = false, command = "start";
1465
1612
  let releaseFile, artifactDirectory;
1466
1613
  for (let i = 0; i < args.length; i++) {
1467
1614
  if (args[i] === "--home") {
@@ -1476,7 +1623,7 @@ async function main(args = process.argv.slice(2)) {
1476
1623
  else if (args[i] === "--yes") assumeYes = true;
1477
1624
  else if (args[i] === "--watch") watch = true;
1478
1625
  else if (args[i] === "--version") {
1479
- const packageFile = join8(dirname6(fileURLToPath2(import.meta.url)), "package.json");
1626
+ const packageFile = join9(dirname6(fileURLToPath2(import.meta.url)), "package.json");
1480
1627
  const manifest = JSON.parse(await readFile9(packageFile, "utf8"));
1481
1628
  console.log(`${manifest.name} ${manifest.version}${manifest.private ? "\uFF08\u672C\u5730\u5019\u9009\uFF09" : ""}`);
1482
1629
  return;
@@ -1508,11 +1655,11 @@ async function main(args = process.argv.slice(2)) {
1508
1655
  };
1509
1656
  const ui = installUI(assumeYes);
1510
1657
  if (command === "upgrade") {
1511
- const candidate = selectRelease(JSON.parse(await readFile9(releaseFile ?? join8(dirname6(fileURLToPath2(import.meta.url)), "release.json"), "utf8")));
1658
+ const candidate = selectRelease(JSON.parse(await readFile9(releaseFile ?? join9(dirname6(fileURLToPath2(import.meta.url)), "release.json"), "utf8")));
1512
1659
  await upgrade(home, candidate, { fetcher: artifactSource(candidate, artifactDirectory), progress, ...ui });
1513
1660
  const current = await readInstallation(home);
1514
1661
  if (await control(home, current.releaseDigest, noOpen ? "status" : "open")) return;
1515
- await run(process.execPath, [join8(installationPaths(home, current.release).runtime, "lib/distribution/managed.js"), "start", home, ...noOpen ? ["--no-open"] : []], home);
1662
+ await run(process.execPath, [join9(installationPaths(home, current.release).runtime, "lib/distribution/managed.js"), "start", home, ...noOpen ? ["--no-open"] : []], home, process.env, () => control(home, current.releaseDigest, "stop"));
1516
1663
  return;
1517
1664
  }
1518
1665
  if (command === "uninstall") {
@@ -1530,7 +1677,7 @@ async function main(args = process.argv.slice(2)) {
1530
1677
  return;
1531
1678
  }
1532
1679
  const paths = installationPaths(home, record.release);
1533
- const targets = [home, paths.directory, paths.environment, join8(home, "instances/default")];
1680
+ const targets = [home, paths.directory, paths.environment, join9(home, "instances/default")];
1534
1681
  const resources = evaluateResources(await sampleResources(targets), record.release.budget, targets.map((path) => ({ path, bytes: 0 })), "start");
1535
1682
  console.log(JSON.stringify({
1536
1683
  home,
@@ -1551,7 +1698,7 @@ async function main(args = process.argv.slice(2)) {
1551
1698
  }
1552
1699
  const runtime = installationPaths(home, record.release).runtime;
1553
1700
  try {
1554
- await readFile9(join8(runtime, "lib/distribution/managed.js"));
1701
+ await readFile9(join9(runtime, "lib/distribution/managed.js"));
1555
1702
  } catch (e) {
1556
1703
  if (e.code !== "ENOENT") throw e;
1557
1704
  record = void 0;
@@ -1563,7 +1710,7 @@ async function main(args = process.argv.slice(2)) {
1563
1710
  if (!source) {
1564
1711
  let catalog = null;
1565
1712
  try {
1566
- catalog = JSON.parse(await readFile9(releaseFile ?? join8(dirname6(fileURLToPath2(import.meta.url)), "release.json"), "utf8"));
1713
+ catalog = JSON.parse(await readFile9(releaseFile ?? join9(dirname6(fileURLToPath2(import.meta.url)), "release.json"), "utf8"));
1567
1714
  } catch (e) {
1568
1715
  if (e.code !== "ENOENT") throw e;
1569
1716
  }
@@ -1573,7 +1720,7 @@ async function main(args = process.argv.slice(2)) {
1573
1720
  record = await install(home, source, { fetcher, progress, ...ui });
1574
1721
  }
1575
1722
  const installedHome = record.home;
1576
- await run(process.execPath, [join8(installationPaths(installedHome, record.release).runtime, "lib/distribution/managed.js"), "start", installedHome, ...noOpen ? ["--no-open"] : []], installedHome);
1723
+ await run(process.execPath, [join9(installationPaths(installedHome, record.release).runtime, "lib/distribution/managed.js"), "start", installedHome, ...noOpen ? ["--no-open"] : []], installedHome, process.env, () => control(installedHome, record.releaseDigest, "stop"));
1577
1724
  }
1578
1725
  if (process.argv[1] && await realpath5(resolve5(process.argv[1])).catch(() => "") === await realpath5(fileURLToPath2(import.meta.url))) {
1579
1726
  main().catch((error) => {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@pab-iprg/huohuo",
3
- "version": "1.0.0-preview.6",
3
+ "version": "1.0.0-preview.7",
4
4
  "type": "module",
5
- "description": "火火双平台安装入口(实验预览)",
5
+ "description": "火火多平台安装入口(实验预览)",
6
6
  "bin": {
7
7
  "huohuo": "./cli.js"
8
8
  },
@@ -15,8 +15,9 @@
15
15
  "README.md"
16
16
  ],
17
17
  "optionalDependencies": {
18
- "@pab-iprg/huohuo-assets-darwin-arm64": "1.0.0-preview.6",
19
- "@pab-iprg/huohuo-assets-linux-x64": "1.0.0-preview.6"
18
+ "@pab-iprg/huohuo-assets-darwin-arm64": "1.0.0-preview.7",
19
+ "@pab-iprg/huohuo-assets-linux-x64": "1.0.0-preview.7",
20
+ "@pab-iprg/huohuo-assets-win32-x64": "1.0.0-preview.7"
20
21
  },
21
22
  "publishConfig": {
22
23
  "access": "public",
package/release.json CHANGED
@@ -18,12 +18,12 @@
18
18
  "runtime": {
19
19
  "name": "@huohuo/research-agent",
20
20
  "version": "1.0.0",
21
- "url": "https://bundled.invalid/3d87538021deec2fa15adb57558f3abf453a405879b947adecf4253dd0ec5f28.tgz",
21
+ "url": "https://bundled.invalid/32e96855b2579f293877c67dc6c5645b2b1fb4e9300f70f3e7a783a6d67a4120.tgz",
22
22
  "bundled": true,
23
- "sha256": "3d87538021deec2fa15adb57558f3abf453a405879b947adecf4253dd0ec5f28",
24
- "integrity": "sha512-czI1E1L+ILNCh+tf/CdX1NXIBwETHnJDqXixNppcdzbdrsKXRJ6XkUKJbZK/AX1q4m6gdfETgqb8XXuVK9oKnQ==",
25
- "downloadBytes": 2610631,
26
- "expandedBytes": 8328828
23
+ "sha256": "32e96855b2579f293877c67dc6c5645b2b1fb4e9300f70f3e7a783a6d67a4120",
24
+ "integrity": "sha512-6IAa0KnmqVFw7Z6ogyyLlaeOCHCFPJ7VxMIz7nzemffiZwBYGt8+SvNjkk4CWKzjtJppYQQl2NAPooqZ6keteg==",
25
+ "downloadBytes": 2613970,
26
+ "expandedBytes": 8341044
27
27
  },
28
28
  "python": {
29
29
  "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20261026/cpython-3.13.18%2B20261026-aarch64-apple-darwin-install_only_stripped.tar.gz",
@@ -99,7 +99,7 @@
99
99
  },
100
100
  "assetPackage": {
101
101
  "name": "@pab-iprg/huohuo-assets-darwin-arm64",
102
- "version": "1.0.0-preview.6"
102
+ "version": "1.0.0-preview.7"
103
103
  }
104
104
  },
105
105
  {
@@ -118,12 +118,12 @@
118
118
  "runtime": {
119
119
  "name": "@huohuo/research-agent",
120
120
  "version": "1.0.0",
121
- "url": "https://bundled.invalid/2c2e63ab7a4890ef198f2f0e3b3152788a158fabeb1828a20e9820416eb6d01a.tgz",
121
+ "url": "https://bundled.invalid/79cbf018a4363d256fee64f07dc3cd8e434ef1e89ba1dcfdcbdf4c2b0a424f65.tgz",
122
122
  "bundled": true,
123
- "sha256": "2c2e63ab7a4890ef198f2f0e3b3152788a158fabeb1828a20e9820416eb6d01a",
124
- "integrity": "sha512-JkcJGHcTpa2Db/YkeUuAzzyx0B118eP5CKBEhf7oQiGBm7uY5bbndj4q1IaeNJMhJlJRYD1YAm/qxPk1tI/5aw==",
125
- "downloadBytes": 2610643,
126
- "expandedBytes": 8328846
123
+ "sha256": "79cbf018a4363d256fee64f07dc3cd8e434ef1e89ba1dcfdcbdf4c2b0a424f65",
124
+ "integrity": "sha512-8KMx3WCkkFxvcD6XQcV7N9FNTgU/Q5SNezCkyJizOnlT4lt4bqWdbxlBfhRcTiVmaP2/3PjsyzFDB1KEaaev3g==",
125
+ "downloadBytes": 2613972,
126
+ "expandedBytes": 8341062
127
127
  },
128
128
  "python": {
129
129
  "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20261026/cpython-3.13.18%2B20261026-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
@@ -198,7 +198,105 @@
198
198
  },
199
199
  "assetPackage": {
200
200
  "name": "@pab-iprg/huohuo-assets-linux-x64",
201
- "version": "1.0.0-preview.6"
201
+ "version": "1.0.0-preview.7"
202
+ }
203
+ },
204
+ {
205
+ "format": 1,
206
+ "bootstrapFormat": 1,
207
+ "version": "1.0.0",
208
+ "nodeMajor": 24,
209
+ "npmMajor": 11,
210
+ "qualification": "controlled-acceptance-only",
211
+ "platform": {
212
+ "os": "win32",
213
+ "arch": "x64",
214
+ "minimumOsRelease": "10.0.19045"
215
+ },
216
+ "runtime": {
217
+ "name": "@huohuo/research-agent",
218
+ "version": "1.0.0",
219
+ "url": "https://bundled.invalid/32f67fca6cae53021035f6b972953335a657357907620c1fa5a618290cc552eb.tgz",
220
+ "bundled": true,
221
+ "sha256": "32f67fca6cae53021035f6b972953335a657357907620c1fa5a618290cc552eb",
222
+ "integrity": "sha512-NVB0sYN/W8BWmBu6ShAl75t8jncWzSprwQ5NfS1k6M6KzJtqY+oEY/zQYZz500W6/na2yzCJC6TKmkxCugXfwA==",
223
+ "downloadBytes": 2613974,
224
+ "expandedBytes": 8341060
225
+ },
226
+ "python": {
227
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20261026/cpython-3.13.18%2B20261026-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
228
+ "sha256": "7369f7a35f261222a6ac8b3a87c1c88eeebee8af945d85346202591a64038dd3",
229
+ "downloadBytes": 22023022,
230
+ "expandedBytes": 65192448,
231
+ "redirectHosts": [
232
+ "release-assets.githubusercontent.com"
233
+ ],
234
+ "executable": "python/python.exe",
235
+ "license": "python/LICENSE.txt",
236
+ "build": "cpython-3.13.18+20261026-x86_64-pc-windows-msvc-install_only_stripped.tar.gz"
237
+ },
238
+ "pythonDependencies": {
239
+ "url": "https://bundled.invalid/fd54d77024b4e1a82b58144bbdc4c130ba576e857789439dc473b7cb37ca5aa5.tgz",
240
+ "bundled": true,
241
+ "sha256": "fd54d77024b4e1a82b58144bbdc4c130ba576e857789439dc473b7cb37ca5aa5",
242
+ "downloadBytes": 14578,
243
+ "expandedBytes": 51200,
244
+ "upstreamWheelCount": 40
245
+ },
246
+ "dsh": {
247
+ "version": "0.2.1-rc.2",
248
+ "assemblySha256": "08cf11de70e49981b4891c0aaa2a5e7b872a95b6aa07557bb629713fa8f9e249"
249
+ },
250
+ "requirementsSha256": "acb5dea55d39bda017e8c63c70fcfcd5dfb74eb987376d4612161257a9fd0900",
251
+ "profilesSha256": "fb3540e0d604f26280128a21f97313575848f6348733a221f7e230233fdb8e2d",
252
+ "budget": {
253
+ "minimumEffectiveMemoryBytes": 4294967296,
254
+ "startupHeadroomBytes": 536870912,
255
+ "heavyJobHeadroomBytes": 1073741824,
256
+ "processTreeBudgetBytes": 3221225472,
257
+ "diskReserveBytes": 1073741824,
258
+ "recoverySamples": 3,
259
+ "sampleIntervalMs": 2000,
260
+ "sampleMaxAgeMs": 10000,
261
+ "installTemporaryBytes": 1073741824,
262
+ "dependenciesBytes": 4294967296,
263
+ "backupBytes": 1073741824
264
+ },
265
+ "schema": {
266
+ "minimum": 1,
267
+ "maximum": 1,
268
+ "migrationsSha256": "ba029fdbffa5320e61393b484d08accafce60e9aa8dacde5b6212fb79dd9146b",
269
+ "files": {
270
+ "001_init.sql": "b42b6192d872f0f65cfbbf9d0c7fb442d3aa49584a6cf321d3bcc641b31b70da",
271
+ "002_contact_decision.sql": "af82cb9975a09fa09c0f970b70d04a7b55a960d72e0853f4573d571efaa46f0a",
272
+ "003_schedules.sql": "2937f7be2693ce9b410ee9a73bd10c5e819c5081a10a6b3993c33f37ad42295f",
273
+ "004_condition_state.sql": "f081d250dd525ad57a450bd6bca59a75f8fb1428177d670ae9fa142ed79721d6",
274
+ "005_execution_handoff.sql": "ebf2e5c4890b7a73ae1ffb57a70bb15183f48a6c1fa2acf3a48b95ccb3d992b3",
275
+ "006_entity_confirmation.sql": "d25ee376e7f6dd55df2edeb68d7591ef5fbaa34eeb3eef662cd76e183526d3c0",
276
+ "007_research_tool_calls.sql": "df9c7d8d25b44b151f743e2d5167b34ae89c64e67423d8af554364fd792da942",
277
+ "008_followup_notifications.sql": "98865d811879afd679846087a5f01ee2db371dcb90381a9c400c940a5c1c4b37",
278
+ "008_investment.sql": "71cecf6215552c725797059efdcb501247e1225c7d9f662de606266d3a947e61",
279
+ "009_notification_channels.sql": "572424d739203a8514ae59920a0ad69cf83f278479b48d48becb88bddda25d9a",
280
+ "010_strategies.sql": "af023fab201db6bd2e15e2348125d112bc21cf3a59d8afd0a2d616fc05cee209",
281
+ "011_strategy_run_subjects.sql": "53a1c216ed775a06549dcc201f9494ceabe308bee6dc52a214b685cca2f2fe8d",
282
+ "012_strategy_schedule.sql": "ccddd81bb70d2c86356e6f1ad09132e7397837d17e926b515c8e281a362bdd58",
283
+ "014_industry_chain.sql": "b656efffe746d14ca350ea1c9ecfcce2d98b5753e4b2f862c75d0456a062fd4a",
284
+ "015_source_heads.sql": "652bf637892edbbe969ce5ed4ce45a4a0c43d03de395fce1b828fc4c0169f885",
285
+ "016_legacy_transfer.sql": "b6e3c214b103487d741226b0f83525e0cacbb0c440bd1935d0e963dc9b9bfd07",
286
+ "017_unified_strategy_work.sql": "8d055ca58f3e73482fb16c630c733ae93197244b37e4509cc90c7dec3bf88d60",
287
+ "018_personalization.sql": "7f62a4eb74707fb071f56d6057b6d6517ed7648af23ebe290f3be15d260a103c",
288
+ "019_position_risk.sql": "3c461fa435b352127fd4ee4e7ed3de2c924561a3727dce1535d7d3a47c436ca9",
289
+ "020_notification_tests.sql": "3eb0d72eadf04b074208dbe0ef6a72ee614ec11a15143108a2471568b7b4fb22",
290
+ "021_notification_priority.sql": "f742b4c4b796e918bfc6a08bac8c5ae206382075925acf7b1c430b033f5177e6",
291
+ "022_research_job_priority.sql": "fcb0c0af025806faab7cd403097a9d2c55e07331ab0a78b2926c1927a87f4ed4",
292
+ "023_adaptive_notifications.sql": "80630e2dad73844d48b265fbc33648b01d8321a6fd9a0587ef4dc01f362aadec",
293
+ "023_notification_attention.sql": "5dd85fb001192558211c7b69e3452984c52119f215750ed6283b296d3dccc842",
294
+ "024_t_trade_watch.sql": "aa38470ddab00e0cbca07ce6d5cc31399ba2b6e40c8d8d888e88ec093a9f89ef"
295
+ }
296
+ },
297
+ "assetPackage": {
298
+ "name": "@pab-iprg/huohuo-assets-win32-x64",
299
+ "version": "1.0.0-preview.7"
202
300
  }
203
301
  }
204
302
  ]