@pab-iprg/huohuo 1.0.0-preview.5 → 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 +317 -164
  3. package/package.json +5 -4
  4. package/release.json +112 -14
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) {
@@ -425,6 +554,17 @@ function linuxAvailableMemory(processAvailable, meminfo) {
425
554
  const hostAvailable = Number(match[1]) * 1024;
426
555
  return Number.isSafeInteger(hostAvailable) && hostAvailable >= 0 ? Math.min(processAvailable, hostAvailable) : null;
427
556
  }
557
+ function darwinAvailableMemory(processAvailable, effective, pressure, vmstat, constrained = false) {
558
+ if (processAvailable === null || !Number.isSafeInteger(processAvailable) || processAvailable <= 0) return null;
559
+ if (pressure !== "normal" || constrained || effective === null || !Number.isSafeInteger(effective) || effective <= 0) return processAvailable;
560
+ const pageSize = Number(/page size of (\d+) bytes/.exec(vmstat)?.[1]);
561
+ const counts = ["Pages free", "Pages purgeable", "Pages speculative", "File-backed pages"].map((label) => Number(new RegExp(`^${label}:\\s+(\\d+)\\.\\s*$`, "m").exec(vmstat)?.[1]));
562
+ if (![pageSize, ...counts].every(Number.isSafeInteger) || pageSize <= 0 || counts.some((n) => n < 0)) return processAvailable;
563
+ const [free, purgeable, speculative, fileBacked] = counts;
564
+ const estimate = pageSize * (free + Math.max(purgeable, Math.max(0, fileBacked - speculative)));
565
+ if (!Number.isSafeInteger(estimate) || estimate < 0 || estimate > effective) return processAvailable;
566
+ return Math.min(effective, Math.max(processAvailable, estimate));
567
+ }
428
568
  async function sampleResources(paths) {
429
569
  const limit = constrainedMemory();
430
570
  let effectiveMemoryBytes = Math.min(totalmem(), limit || Infinity);
@@ -438,19 +578,14 @@ async function sampleResources(paths) {
438
578
  if (!(effectiveMemoryBytes > 0)) effectiveMemoryBytes = null;
439
579
  if (process.platform === "darwin") {
440
580
  try {
441
- 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 });
442
582
  pressure = { 1: "normal", 2: "high", 4: "critical" }[Number(stdout.trim())] ?? "unknown";
443
583
  } catch {
444
584
  }
445
585
  if (pressure === "normal" && availableMemoryBytes !== null && effectiveMemoryBytes !== null) {
446
586
  try {
447
- const { stdout } = await execute2("/usr/bin/vm_stat", [], { timeout: 2e3 });
448
- const pageBytes = Number(/page size of (\d+) bytes/.exec(stdout)?.[1]);
449
- const pageCounts = ["free", "purgeable"].map((kind) => Number(new RegExp(`^Pages ${kind}:\\s+(\\d+)\\.`, "m").exec(stdout)?.[1]));
450
- const reclaimable = pageBytes * pageCounts.reduce((sum, pages) => sum + pages, 0);
451
- if (Number.isSafeInteger(reclaimable) && reclaimable > 0) {
452
- availableMemoryBytes = Math.min(effectiveMemoryBytes, Math.max(availableMemoryBytes, reclaimable));
453
- }
587
+ const { stdout } = await execute3("/usr/bin/vm_stat", [], { timeout: 2e3 });
588
+ availableMemoryBytes = darwinAvailableMemory(availableMemoryBytes, effectiveMemoryBytes, pressure, stdout, limit > 0);
454
589
  } catch {
455
590
  }
456
591
  }
@@ -464,6 +599,16 @@ async function sampleResources(paths) {
464
599
  } catch {
465
600
  }
466
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
+ }
467
612
  const disks = [];
468
613
  for (const path of [...new Set(paths.map((p) => resolve2(p)))]) {
469
614
  try {
@@ -538,10 +683,10 @@ function evaluateResources(sample, policy, costs, phase) {
538
683
 
539
684
  // runtime/npm.mjs
540
685
  import { existsSync } from "node:fs";
541
- import { dirname as dirname4, join as join2 } from "node:path";
686
+ import { dirname as dirname4, join as join3 } from "node:path";
542
687
  function npmCommand() {
543
688
  if (process.platform !== "win32") return { command: "npm", prefix: [] };
544
- 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");
545
690
  if (existsSync(cli)) return { command: process.execPath, prefix: [cli] };
546
691
  return { command: process.env.ComSpec ?? "cmd.exe", prefix: ["/d", "/s", "/c", "npm"] };
547
692
  }
@@ -596,29 +741,33 @@ async function ownedDirectory(path, owner) {
596
741
  await mkdir3(resolve3(path, ".."), { recursive: true, mode: 448 });
597
742
  try {
598
743
  await mkdir3(path, { mode: 448 });
599
- await atomicJSON(join3(path, ".huohuo-owner.json"), { format: 1, owner });
744
+ await atomicJSON(join4(path, ".huohuo-owner.json"), { format: 1, owner });
600
745
  } catch (e) {
601
746
  if (e.code !== "EEXIST") throw e;
602
- 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"));
603
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");
604
749
  }
605
750
  }
606
- async function run(command, args, cwd, env = process.env) {
751
+ async function run(command, args, cwd, env = process.env, onInterrupt) {
607
752
  await new Promise((done, fail) => {
608
753
  const child = spawn(command, args, { cwd, env, stdio: "inherit", detached: process.platform !== "win32" });
609
754
  let interrupted = false;
610
755
  let timer;
611
756
  const kill = (signal) => {
612
- if (!child.pid) return;
613
- try {
614
- process.kill(process.platform === "win32" ? child.pid : -child.pid, signal);
615
- } catch {
616
- }
757
+ interruptProcessTree(child, signal);
617
758
  };
618
759
  const stop = () => {
760
+ if (interrupted) return;
619
761
  interrupted = true;
620
- kill("SIGTERM");
621
- 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
+ }
622
771
  };
623
772
  process.on("SIGINT", stop);
624
773
  process.on("SIGTERM", stop);
@@ -633,14 +782,14 @@ async function run(command, args, cwd, env = process.env) {
633
782
  });
634
783
  child.once("close", (code) => {
635
784
  cleanup();
636
- 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}` }));
637
786
  });
638
787
  });
639
788
  }
640
789
  async function readInstallation(home, recordFile = "installation.json") {
641
790
  let record;
642
791
  try {
643
- record = JSON.parse(await readFile4(join3(home, recordFile), "utf8"));
792
+ record = JSON.parse(await readFile4(join4(home, recordFile), "utf8"));
644
793
  } catch (e) {
645
794
  if (e.code === "ENOENT") return;
646
795
  throw new Error("\u5B89\u88C5\u8BB0\u5F55\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u672A\u91CD\u5EFA\u5B9E\u4F8B", { cause: e });
@@ -652,15 +801,15 @@ async function readInstallation(home, recordFile = "installation.json") {
652
801
  }
653
802
  function installationPaths(home, release) {
654
803
  const id = `${release.version}-${digest(release).slice(0, 16)}`;
655
- const directory = join3(home, "releases", id);
804
+ const directory = join4(home, "releases", id);
656
805
  const dependencyBundle = release.pythonDependencies.bundled ? `-${release.pythonDependencies.sha256.slice(0, 16)}` : "";
657
806
  return {
658
807
  directory,
659
- runtime: join3(directory, "node_modules/@huohuo/research-agent"),
660
- python: join3(home, "runtimes/python", release.python.sha256),
661
- environment: join3(home, "environments", `${release.python.sha256.slice(0, 16)}-${release.requirementsSha256.slice(0, 16)}${dependencyBundle}`),
662
- archive: join3(home, "cache", release.runtime.sha256 + ".tgz"),
663
- 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")
664
813
  };
665
814
  }
666
815
  function versionAtLeast(actual, minimum) {
@@ -682,20 +831,20 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
682
831
  home = resolve3(home);
683
832
  await mkdir3(home, { recursive: true, mode: 448 });
684
833
  home = await realpath3(home);
685
- return withLock(join3(home, "state/install.lock"), async () => {
834
+ return withLock(join4(home, "state/install.lock"), async () => {
686
835
  if (!programsOnly) {
687
836
  try {
688
- await access(join3(home, "state/default-run.lock"));
837
+ await access(join4(home, "state/default-run.lock"));
689
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");
690
839
  } catch (e) {
691
840
  if (e.code !== "ENOENT") throw e;
692
841
  }
693
842
  }
694
843
  const recordFile = programsOnly ? "state/upgrade-target.json" : "installation.json";
695
- const recordPath = join3(home, recordFile);
844
+ const recordPath = join4(home, recordFile);
696
845
  let record = await readInstallation(home, recordFile);
697
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");
698
- 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");
699
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");
700
849
  const bundleDirectory = hooks.bundleDirectory;
701
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")) {
@@ -712,12 +861,12 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
712
861
  const oldAssets = bundled(record.release);
713
862
  const available = await Promise.all(oldAssets.map(async (artifact2) => {
714
863
  const file = artifact2.sha256 + ".tgz";
715
- 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));
716
865
  }));
717
866
  if (available.includes(false)) {
718
867
  const next = validateRelease(candidate);
719
868
  const nextAssets = bundled(next);
720
- 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)) {
721
870
  record = void 0;
722
871
  hooks.progress?.("resume-current-package");
723
872
  }
@@ -731,12 +880,12 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
731
880
  record ??= { format: 1, release, releaseDigest: digest(release), home, status: "installing", stage: "preflight", stages: {} };
732
881
  await atomicJSON(recordPath, record);
733
882
  const paths = installationPaths(home, release);
734
- const execute4 = hooks.run ?? run;
883
+ const execute5 = hooks.run ?? run;
735
884
  const sample = hooks.sample ?? sampleResources;
736
885
  const files = [
737
886
  { name: "\u706B\u706B\u8FD0\u884C\u5305", artifact: release.runtime, path: paths.archive },
738
887
  { name: "Python \u8FD0\u884C\u73AF\u5883", artifact: release.python, path: paths.pythonArchive },
739
- { 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") }
740
889
  ];
741
890
  const withActivity = async (label, action) => {
742
891
  hooks.progress?.(label);
@@ -747,7 +896,7 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
747
896
  if (timer) clearInterval(timer);
748
897
  }
749
898
  };
750
- const cached = async (path, artifact2) => {
899
+ const cached2 = async (path, artifact2) => {
751
900
  try {
752
901
  await verifyArtifact(path, artifact2);
753
902
  return true;
@@ -758,22 +907,22 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
758
907
  };
759
908
  const installed = async () => {
760
909
  try {
761
- const owner = JSON.parse(await readFile4(join3(paths.python, ".huohuo-owner.json"), "utf8"));
762
- 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));
763
912
  const python = owner.owner === release.python.sha256;
764
- const root = join3(home, "instances/default");
765
- const initialization = JSON.parse(await readFile4(join3(root, "initialization.json"), "utf8"));
766
- const dependencies = JSON.parse(await readFile4(join3(paths.environment, ".dependencies-ready.json"), "utf8"));
767
- const worker = JSON.parse(await readFile4(join3(root, "worker-install.json"), "utf8"));
768
- const chat = JSON.parse(await readFile4(join3(root, "dsh/profiles/huohuo-chat/installed.json"), "utf8"));
769
- 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"));
770
- 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"));
771
920
  return { python, dependencies: initialization.package_sha256 === release.runtime.sha256 && initialization.status === "complete" && dependencies.sha256 === release.pythonDependencies.sha256 && worker.complete === true && chat.complete === true };
772
921
  } catch (error) {
773
922
  if (error.code !== "ENOENT") throw error;
774
923
  try {
775
- const owner = JSON.parse(await readFile4(join3(paths.python, ".huohuo-owner.json"), "utf8"));
776
- 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));
777
926
  return { python: owner.owner === release.python.sha256, dependencies: false };
778
927
  } catch {
779
928
  return { python: false, dependencies: false };
@@ -781,19 +930,19 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
781
930
  }
782
931
  };
783
932
  const check = async () => {
784
- 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()]);
785
934
  const remainingDependencies = inventory.dependencies ? 0 : release.budget.dependenciesBytes;
786
935
  const costs = [
787
- { 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) },
788
937
  { path: paths.directory, bytes: inventory.dependencies ? 0 : release.runtime.expandedBytes + release.budget.installTemporaryBytes },
789
938
  { path: paths.python, bytes: inventory.python ? 0 : release.python.expandedBytes },
790
939
  { path: paths.environment, bytes: 0 },
791
- { path: join3(home, "instances/default"), bytes: 0 },
940
+ { path: join4(home, "instances/default"), bytes: 0 },
792
941
  { path: paths.directory, bytes: inventory.dependencies ? 0 : release.pythonDependencies.expandedBytes },
793
- { path: join3(home, "backups"), bytes: release.budget.backupBytes }
942
+ { path: join4(home, "backups"), bytes: release.budget.backupBytes }
794
943
  ];
795
944
  const observation = await sample(costs.map((c) => c.path));
796
- const dependencyPaths = [paths.directory, paths.environment, join3(home, "instances/default")];
945
+ const dependencyPaths = [paths.directory, paths.environment, join4(home, "instances/default")];
797
946
  const volumes = /* @__PURE__ */ new Set();
798
947
  for (const path of dependencyPaths) {
799
948
  const device = diskBudgets(observation, [{ path, bytes: 0 }], 0)[0].device;
@@ -803,7 +952,7 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
803
952
  }
804
953
  }
805
954
  const result = evaluateResources(observation, release.budget, costs, "install");
806
- await atomicJSON(join3(home, "state/resources.json"), result);
955
+ await atomicJSON(join4(home, "state/resources.json"), result);
807
956
  if (result.status === "unknown" || result.status === "blocked") {
808
957
  const details = result.reasons.map((reason) => `- ${reason}`).join("\n");
809
958
  throw Object.assign(new Error(`\u706B\u706B\u6682\u65F6\u65E0\u6CD5\u7EE7\u7EED\u5B89\u88C5\u3002
@@ -850,9 +999,9 @@ ${details}
850
999
  };
851
1000
  await stage("preflight", async (plan) => {
852
1001
  const npm = npmCommand();
853
- 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 });
854
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");
855
- const probe = join3(home, `.write-probe-${randomUUID3()}`);
1004
+ const probe = join4(home, `.write-probe-${randomUUID3()}`);
856
1005
  try {
857
1006
  await atomicJSON(probe, { probe: true });
858
1007
  } finally {
@@ -882,19 +1031,19 @@ ${details}
882
1031
  await stage("install-runtime", async () => {
883
1032
  try {
884
1033
  await access(paths.python);
885
- 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"));
886
1035
  if (marker.owner !== release.python.sha256) throw new Error("\u53D7\u7BA1 Python \u5F52\u5C5E\u4E0D\u7B26");
887
- await access(join3(paths.python, release.python.executable));
1036
+ await access(join4(paths.python, release.python.executable));
888
1037
  return release.python.sha256;
889
1038
  } catch (e) {
890
1039
  if (e.code !== "ENOENT") throw e;
891
1040
  }
892
- await mkdir3(join3(home, "runtimes/python"), { recursive: true, mode: 448 });
1041
+ await mkdir3(join4(home, "runtimes/python"), { recursive: true, mode: 448 });
893
1042
  const staging = `${paths.python}.${randomUUID3()}.staging`;
894
1043
  try {
895
1044
  await extractArchive(paths.pythonArchive, staging, release.python.expandedBytes);
896
- await access(join3(staging, release.python.executable));
897
- 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 });
898
1047
  await rename3(staging, paths.python);
899
1048
  } finally {
900
1049
  await rm2(staging, { recursive: true, force: true });
@@ -903,30 +1052,30 @@ ${details}
903
1052
  });
904
1053
  await stage("install-dependencies", async () => {
905
1054
  await ownedDirectory(paths.directory, record.releaseDigest);
906
- await atomicJSON(join3(paths.directory, "package.json"), {
1055
+ await atomicJSON(join4(paths.directory, "package.json"), {
907
1056
  name: "huohuo-managed-release",
908
1057
  private: true,
909
1058
  dependencies: { "@huohuo/research-agent": `file:${paths.archive}` }
910
1059
  });
911
1060
  const npm = npmCommand();
912
- 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));
913
- 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"));
914
1063
  if (runtime.name !== release.runtime.name || runtime.version !== release.version) throw new Error("\u8FD0\u884C\u5305\u8EAB\u4EFD\u4E0D\u7B26");
915
- const requirements = await readFile4(join3(paths.runtime, "sidecar/requirements.lock"));
1064
+ const requirements = await readFile4(join4(paths.runtime, "sidecar/requirements.lock"));
916
1065
  if (createHash2("sha256").update(requirements).digest("hex") !== release.requirementsSha256) throw new Error("Python \u4F9D\u8D56\u9501\u6458\u8981\u4E0D\u7B26");
917
- const assembly = await readFile4(join3(paths.runtime, "npm-shrinkwrap.json"));
1066
+ const assembly = await readFile4(join4(paths.runtime, "npm-shrinkwrap.json"));
918
1067
  if (createHash2("sha256").update(assembly).digest("hex") !== release.dsh.assemblySha256) throw new Error("DSH \u88C5\u914D\u9501\u6458\u8981\u4E0D\u7B26");
919
1068
  await ownedDirectory(paths.environment, `${release.python.sha256}:${release.requirementsSha256}`);
920
- const python = join3(paths.environment, process.platform === "win32" ? "Scripts/python.exe" : "bin/python");
921
- 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}`);
922
1071
  try {
923
- await access(join3(wheels, "requirements.txt"));
1072
+ await access(join4(wheels, "requirements.txt"));
924
1073
  } catch (e) {
925
1074
  if (e.code !== "ENOENT") throw e;
926
1075
  const staging = `${wheels}.${randomUUID3()}.staging`;
927
1076
  try {
928
- await extractArchive(join3(home, "cache", release.pythonDependencies.sha256 + ".tgz"), staging, release.pythonDependencies.expandedBytes);
929
- 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"));
930
1079
  await rename3(staging, wheels);
931
1080
  } finally {
932
1081
  await rm2(staging, { recursive: true, force: true });
@@ -934,7 +1083,7 @@ ${details}
934
1083
  }
935
1084
  let environmentReady = false;
936
1085
  try {
937
- 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"));
938
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");
939
1088
  await access(python);
940
1089
  environmentReady = true;
@@ -942,13 +1091,13 @@ ${details}
942
1091
  if (e.code !== "ENOENT") throw e;
943
1092
  }
944
1093
  if (!environmentReady) {
945
- 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);
946
1095
  if (release.pythonDependencies.bundled && hooks.progress) {
947
- const lock = await readFile4(join3(wheels, "requirements.txt"), "utf8");
1096
+ const lock = await readFile4(join4(wheels, "requirements.txt"), "utf8");
948
1097
  const upstream = lock.split("\n").filter((line) => /^[\w.-]+\s+@\s+https:\/\//.test(line)).length;
949
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`);
950
1099
  }
951
- await withActivity("\u5B89\u88C5 Python \u4F9D\u8D56", () => execute4(python, [
1100
+ await withActivity("\u5B89\u88C5 Python \u4F9D\u8D56", () => execute5(python, [
952
1101
  "-m",
953
1102
  "pip",
954
1103
  "--isolated",
@@ -960,11 +1109,15 @@ ${details}
960
1109
  "--only-binary=:all:",
961
1110
  ...release.pythonDependencies.bundled ? ["--no-cache-dir"] : [],
962
1111
  "-r",
963
- join3(wheels, "requirements.txt")
1112
+ join4(wheels, "requirements.txt")
964
1113
  ], home));
965
1114
  }
966
- await execute4(python, ["-m", "pip", "--isolated", "check"], home);
967
- 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 });
968
1121
  return { python, runtime: release.runtime.sha256 };
969
1122
  });
970
1123
  if (programsOnly) {
@@ -974,11 +1127,11 @@ ${details}
974
1127
  return record;
975
1128
  }
976
1129
  await stage("assemble-profiles", async () => {
977
- 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));
978
1131
  return { release: record.releaseDigest };
979
1132
  });
980
1133
  await stage("verify", async () => {
981
- 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);
982
1135
  return { release: record.releaseDigest };
983
1136
  });
984
1137
  record.status = "ready";
@@ -991,14 +1144,14 @@ ${details}
991
1144
  // distribution/bootstrap/control.ts
992
1145
  import { createServer, request as httpRequest } from "node:http";
993
1146
  import { readFile as readFile5, rm as rm3 } from "node:fs/promises";
994
- import { join as join4 } from "node:path";
995
- import { execFile as execFile4 } from "node:child_process";
996
- import { promisify as promisify4 } from "node:util";
997
- 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);
998
1151
  async function control(home, releaseDigest, command) {
999
1152
  let binding;
1000
1153
  try {
1001
- 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"));
1002
1155
  } catch (e) {
1003
1156
  if (e.code === "ENOENT") return false;
1004
1157
  throw new Error("\u672C\u673A\u8FD0\u884C\u8BB0\u5F55\u65E0\u6CD5\u6838\u9A8C");
@@ -1034,7 +1187,7 @@ async function control(home, releaseDigest, command) {
1034
1187
  req.end();
1035
1188
  });
1036
1189
  } catch (error) {
1037
- 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;
1038
1191
  throw error;
1039
1192
  }
1040
1193
  return true;
@@ -1042,13 +1195,13 @@ async function control(home, releaseDigest, command) {
1042
1195
 
1043
1196
  // distribution/bootstrap/maintenance.ts
1044
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";
1045
- import { join as join5, resolve as resolve4 } from "node:path";
1198
+ import { join as join6, resolve as resolve4, sep as sep3 } from "node:path";
1046
1199
  import { randomUUID as randomUUID4 } from "node:crypto";
1047
1200
  import { setTimeout as delay } from "node:timers/promises";
1048
1201
  async function upgradeJournal(home) {
1049
1202
  try {
1050
- const value = JSON.parse(await readFile6(join5(home, "state/upgrade.json"), "utf8"));
1051
- 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");
1052
1205
  return value;
1053
1206
  } catch (error) {
1054
1207
  if (error.code === "ENOENT") return;
@@ -1072,7 +1225,7 @@ async function stopOwned(home, releaseDigest) {
1072
1225
  if (!await control(home, releaseDigest, "stop")) return;
1073
1226
  for (let i = 0; i < 100; i++) {
1074
1227
  try {
1075
- await access2(join5(home, "state/default-run.lock"));
1228
+ await access2(join6(home, "state/default-run.lock"));
1076
1229
  } catch (error) {
1077
1230
  if (error.code === "ENOENT") return;
1078
1231
  throw error;
@@ -1087,9 +1240,9 @@ async function backupBytes(directory) {
1087
1240
  for (const item of await readdir2(path, { withFileTypes: true })) {
1088
1241
  if (["node_modules", "cache", "python", "__pycache__"].includes(item.name)) continue;
1089
1242
  if (item.isSymbolicLink()) throw new Error("\u5907\u4EFD\u4E0D\u63A5\u7BA1\u672A\u77E5\u7684\u5B9E\u4F8B\u94FE\u63A5");
1090
- if (item.isDirectory()) await visit(join5(path, item.name));
1243
+ if (item.isDirectory()) await visit(join6(path, item.name));
1091
1244
  else {
1092
- const info = await lstat2(join5(path, item.name));
1245
+ const info = await lstat2(join6(path, item.name));
1093
1246
  bytes += info.size + 512;
1094
1247
  }
1095
1248
  }
@@ -1107,19 +1260,19 @@ async function upgrade(home, candidate, hooks = {}) {
1107
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");
1108
1261
  if (current.releaseDigest === digest(next)) {
1109
1262
  if (journal && journal.to === current.releaseDigest && journal.stage !== "complete") {
1110
- 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);
1111
1264
  journal.stage = "complete";
1112
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1265
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1113
1266
  }
1114
1267
  hooks.progress?.("\u5DF2\u7ECF\u662F\u540C\u4E00\u53D1\u884C\uFF0C\u65E0\u9700\u8FC1\u79FB");
1115
1268
  return;
1116
1269
  }
1117
1270
  assertCompatibleUpgrade(current.release, next);
1118
1271
  if (journal?.stage === "complete") {
1119
- await withLock(join5(home, "state/install.lock"), async () => {
1120
- 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`));
1121
1274
  try {
1122
- 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`));
1123
1276
  } catch (error) {
1124
1277
  if (error.code !== "ENOENT") throw error;
1125
1278
  }
@@ -1128,58 +1281,58 @@ async function upgrade(home, candidate, hooks = {}) {
1128
1281
  }
1129
1282
  const target = await install(home, next, hooks, true);
1130
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");
1131
- const execute4 = hooks.run ?? run;
1132
- await withLock(join5(home, "state/install.lock"), async () => {
1284
+ const execute5 = hooks.run ?? run;
1285
+ await withLock(join6(home, "state/install.lock"), async () => {
1133
1286
  current = await readInstallation(home);
1134
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");
1135
1288
  journal ??= {
1136
1289
  format: 1,
1137
1290
  from: current.releaseDigest,
1138
1291
  to: target.releaseDigest,
1139
- backup: join5(home, "backups", `upgrade-${target.releaseDigest.slice(0, 16)}-${randomUUID4()}`),
1292
+ backup: join6(home, "backups", `upgrade-${target.releaseDigest.slice(0, 16)}-${randomUUID4()}`),
1140
1293
  stage: "stopping"
1141
1294
  };
1142
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1295
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1143
1296
  await stopOwned(home, current.releaseDigest);
1144
- const releaseRun = await acquireLock(join5(home, "state/default-run.lock"));
1297
+ const releaseRun = await acquireLock(join6(home, "state/default-run.lock"));
1145
1298
  try {
1146
- const bytes = await backupBytes(join5(home, "instances/default"));
1299
+ const bytes = await backupBytes(join6(home, "instances/default"));
1147
1300
  const result = evaluateResources(
1148
- await (hooks.sample ?? sampleResources)([join5(home, "backups")]),
1301
+ await (hooks.sample ?? sampleResources)([join6(home, "backups")]),
1149
1302
  target.release.budget,
1150
- [{ path: join5(home, "backups"), bytes }],
1303
+ [{ path: join6(home, "backups"), bytes }],
1151
1304
  "install"
1152
1305
  );
1153
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");
1154
1307
  if (journal.stage === "stopping") {
1155
- 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);
1156
1309
  journal.stage = "backed-up";
1157
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1310
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1158
1311
  }
1159
1312
  const runtime = installationPaths(home, target.release).runtime;
1160
- 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);
1161
1314
  journal.stage = "profiles";
1162
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1315
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1163
1316
  if (current.release.schema.migrationsSha256 !== target.release.schema.migrationsSha256) {
1164
1317
  const space = evaluateResources(
1165
- await (hooks.sample ?? sampleResources)([join5(home, "instances/default")]),
1318
+ await (hooks.sample ?? sampleResources)([join6(home, "instances/default")]),
1166
1319
  target.release.budget,
1167
- [{ path: join5(home, "instances/default"), bytes: bytes + target.release.budget.installTemporaryBytes }],
1320
+ [{ path: join6(home, "instances/default"), bytes: bytes + target.release.budget.installTemporaryBytes }],
1168
1321
  "install"
1169
1322
  );
1170
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");
1171
- 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);
1172
1325
  }
1173
1326
  journal.stage = "migrated";
1174
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1175
- 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);
1176
1329
  journal.stage = "verified";
1177
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1330
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1178
1331
  target.status = "ready";
1179
1332
  target.stage = "ready";
1180
- await atomicJSON(join5(home, "installation.json"), target);
1333
+ await atomicJSON(join6(home, "installation.json"), target);
1181
1334
  journal.stage = "complete";
1182
- await atomicJSON(join5(home, "state/upgrade.json"), journal);
1335
+ await atomicJSON(join6(home, "state/upgrade.json"), journal);
1183
1336
  hooks.progress?.("\u5347\u7EA7\u9A8C\u8BC1\u901A\u8FC7\uFF0C\u5DF2\u5207\u6362\u53D1\u884C\u7ED1\u5B9A\uFF1B\u5907\u4EFD\u4FDD\u7559");
1184
1337
  } finally {
1185
1338
  await releaseRun();
@@ -1188,7 +1341,7 @@ async function upgrade(home, candidate, hooks = {}) {
1188
1341
  }
1189
1342
  async function removeOwned(path, expected) {
1190
1343
  try {
1191
- 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"));
1192
1345
  if (marker.format !== 1 || marker.owner !== expected) throw new Error("\u53D7\u7BA1\u76EE\u5F55\u5F52\u5C5E\u4E0D\u7B26\uFF0C\u672A\u5220\u9664");
1193
1346
  await rm4(path, { recursive: true });
1194
1347
  } catch (error) {
@@ -1197,25 +1350,25 @@ async function removeOwned(path, expected) {
1197
1350
  }
1198
1351
  async function uninstall(home, hooks = {}) {
1199
1352
  home = await realpath4(resolve4(home));
1200
- await withLock(join5(home, "state/install.lock"), async () => {
1353
+ await withLock(join6(home, "state/install.lock"), async () => {
1201
1354
  const current = await readInstallation(home);
1202
1355
  if (!current) throw new Error("\u6CA1\u6709\u53EF\u6838\u9A8C\u7684\u6301\u4E45\u5B89\u88C5");
1203
1356
  const journal = await upgradeJournal(home);
1204
1357
  if (journal && journal.stage !== "complete") throw new Error("\u5347\u7EA7\u5C1A\u672A\u5BF9\u8D26\uFF0C\u4E0D\u80FD\u5378\u8F7D\u7A0B\u5E8F");
1205
1358
  await stopOwned(home, current.releaseDigest);
1206
- const releaseRun = await acquireLock(join5(home, "state/default-run.lock"));
1359
+ const releaseRun = await acquireLock(join6(home, "state/default-run.lock"));
1207
1360
  try {
1208
1361
  const paths = installationPaths(home, current.release);
1209
- 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");
1210
1363
  if (current.status !== "uninstalled") {
1211
- 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);
1212
1365
  current.status = "uninstalled";
1213
1366
  current.stage = "uninstalled";
1214
- await atomicJSON(join5(home, "installation.json"), current);
1367
+ await atomicJSON(join6(home, "installation.json"), current);
1215
1368
  }
1216
1369
  await removeOwned(paths.directory, current.releaseDigest);
1217
1370
  await removeOwned(paths.environment, `${current.release.python.sha256}:${current.release.requirementsSha256}`);
1218
- 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);
1219
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}`);
1220
1373
  } finally {
1221
1374
  await releaseRun();
@@ -1226,7 +1379,7 @@ async function uninstall(home, hooks = {}) {
1226
1379
  // distribution/bootstrap/install-ui.ts
1227
1380
  import { createInterface } from "node:readline";
1228
1381
  import { homedir } from "node:os";
1229
- import { relative, sep as sep3 } from "node:path";
1382
+ import { relative, sep as sep4 } from "node:path";
1230
1383
  var gb = (bytes) => bytes === null ? "\u672A\u77E5" : `${(bytes / 1e9).toFixed(2)} GB`;
1231
1384
  var mb = (bytes) => bytes < 1e5 ? `${(bytes / 1e3).toFixed(1)} KB` : `${(bytes / 1e6).toFixed(1)} MB`;
1232
1385
  var memory = (bytes) => {
@@ -1236,7 +1389,7 @@ var memory = (bytes) => {
1236
1389
  function displayPath(path) {
1237
1390
  const local = relative(homedir(), path);
1238
1391
  if (!local) return "~";
1239
- return local === ".." || local.startsWith(`..${sep3}`) ? path : `~${sep3}${local}`;
1392
+ return local === ".." || local.startsWith(`..${sep4}`) ? path : `~${sep4}${local}`;
1240
1393
  }
1241
1394
  function installUI(assumeYes) {
1242
1395
  const interactive = process.stdin.isTTY && process.stderr.isTTY;
@@ -1339,7 +1492,7 @@ function installUI(assumeYes) {
1339
1492
 
1340
1493
  // distribution/bootstrap/status-ui.ts
1341
1494
  import { readFile as readFile7 } from "node:fs/promises";
1342
- import { join as join6 } from "node:path";
1495
+ import { join as join7 } from "node:path";
1343
1496
  import { setTimeout as delay2 } from "node:timers/promises";
1344
1497
  var mib = (bytes) => typeof bytes === "number" && Number.isFinite(bytes) ? `${Math.round(bytes / 2 ** 20)} MiB` : "\u672A\u77E5";
1345
1498
  var pressureLabels = { normal: "\u6B63\u5E38", high: "\u504F\u9AD8", critical: "\u4E25\u91CD", unknown: "\u672A\u77E5" };
@@ -1350,7 +1503,7 @@ async function statusLines(home) {
1350
1503
  if (installed.status !== "ready") return [title, "\u5B89\u88C5\u5C1A\u672A\u5B8C\u6210\u3002"];
1351
1504
  let admission;
1352
1505
  try {
1353
- 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"));
1354
1507
  } catch (error) {
1355
1508
  if (error.code !== "ENOENT") return [title, "\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u8D44\u6E90\u72B6\u6001\u3002"];
1356
1509
  }
@@ -1419,10 +1572,10 @@ async function showStatus(home, watch) {
1419
1572
  // distribution/bootstrap/assets.ts
1420
1573
  import { createRequire } from "node:module";
1421
1574
  import { readFile as readFile8 } from "node:fs/promises";
1422
- import { dirname as dirname5, join as join7 } from "node:path";
1575
+ import { dirname as dirname5, join as join8 } from "node:path";
1423
1576
  import { fileURLToPath } from "node:url";
1424
1577
  async function assetsDirectory(release, entryURL = import.meta.url) {
1425
- if (!release?.assetPackage) return join7(dirname5(fileURLToPath(entryURL)), "assets");
1578
+ if (!release?.assetPackage) return join8(dirname5(fileURLToPath(entryURL)), "assets");
1426
1579
  const expected = release.assetPackage;
1427
1580
  let file;
1428
1581
  try {
@@ -1434,7 +1587,7 @@ async function assetsDirectory(release, entryURL = import.meta.url) {
1434
1587
  if (manifest.name !== expected.name || manifest.version !== expected.version) {
1435
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");
1436
1589
  }
1437
- return join7(dirname5(file), "assets");
1590
+ return join8(dirname5(file), "assets");
1438
1591
  }
1439
1592
 
1440
1593
  // distribution/bootstrap/entry.ts
@@ -1446,7 +1599,7 @@ function artifactSource(source, directory) {
1446
1599
  const sha256 = bundled?.[1] ?? artifact2?.sha256;
1447
1600
  if (!sha256) throw new Error("\u672C\u673A\u4EA7\u7269\u76EE\u5F55\u7F3A\u5C11\u8BE5\u56FA\u5B9A\u53D1\u884C\u8D44\u6E90");
1448
1601
  const root = directory ?? await assetsDirectory(source, import.meta.url);
1449
- const path = join8(root, sha256 + ".tgz");
1602
+ const path = join9(root, sha256 + ".tgz");
1450
1603
  const file = await open3(path).catch((error) => {
1451
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");
1452
1605
  throw error;
@@ -1455,7 +1608,7 @@ function artifactSource(source, directory) {
1455
1608
  };
1456
1609
  }
1457
1610
  async function main(args = process.argv.slice(2)) {
1458
- 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";
1459
1612
  let releaseFile, artifactDirectory;
1460
1613
  for (let i = 0; i < args.length; i++) {
1461
1614
  if (args[i] === "--home") {
@@ -1470,7 +1623,7 @@ async function main(args = process.argv.slice(2)) {
1470
1623
  else if (args[i] === "--yes") assumeYes = true;
1471
1624
  else if (args[i] === "--watch") watch = true;
1472
1625
  else if (args[i] === "--version") {
1473
- const packageFile = join8(dirname6(fileURLToPath2(import.meta.url)), "package.json");
1626
+ const packageFile = join9(dirname6(fileURLToPath2(import.meta.url)), "package.json");
1474
1627
  const manifest = JSON.parse(await readFile9(packageFile, "utf8"));
1475
1628
  console.log(`${manifest.name} ${manifest.version}${manifest.private ? "\uFF08\u672C\u5730\u5019\u9009\uFF09" : ""}`);
1476
1629
  return;
@@ -1502,11 +1655,11 @@ async function main(args = process.argv.slice(2)) {
1502
1655
  };
1503
1656
  const ui = installUI(assumeYes);
1504
1657
  if (command === "upgrade") {
1505
- 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")));
1506
1659
  await upgrade(home, candidate, { fetcher: artifactSource(candidate, artifactDirectory), progress, ...ui });
1507
1660
  const current = await readInstallation(home);
1508
1661
  if (await control(home, current.releaseDigest, noOpen ? "status" : "open")) return;
1509
- 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"));
1510
1663
  return;
1511
1664
  }
1512
1665
  if (command === "uninstall") {
@@ -1524,7 +1677,7 @@ async function main(args = process.argv.slice(2)) {
1524
1677
  return;
1525
1678
  }
1526
1679
  const paths = installationPaths(home, record.release);
1527
- const targets = [home, paths.directory, paths.environment, join8(home, "instances/default")];
1680
+ const targets = [home, paths.directory, paths.environment, join9(home, "instances/default")];
1528
1681
  const resources = evaluateResources(await sampleResources(targets), record.release.budget, targets.map((path) => ({ path, bytes: 0 })), "start");
1529
1682
  console.log(JSON.stringify({
1530
1683
  home,
@@ -1545,7 +1698,7 @@ async function main(args = process.argv.slice(2)) {
1545
1698
  }
1546
1699
  const runtime = installationPaths(home, record.release).runtime;
1547
1700
  try {
1548
- await readFile9(join8(runtime, "lib/distribution/managed.js"));
1701
+ await readFile9(join9(runtime, "lib/distribution/managed.js"));
1549
1702
  } catch (e) {
1550
1703
  if (e.code !== "ENOENT") throw e;
1551
1704
  record = void 0;
@@ -1557,7 +1710,7 @@ async function main(args = process.argv.slice(2)) {
1557
1710
  if (!source) {
1558
1711
  let catalog = null;
1559
1712
  try {
1560
- 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"));
1561
1714
  } catch (e) {
1562
1715
  if (e.code !== "ENOENT") throw e;
1563
1716
  }
@@ -1567,7 +1720,7 @@ async function main(args = process.argv.slice(2)) {
1567
1720
  record = await install(home, source, { fetcher, progress, ...ui });
1568
1721
  }
1569
1722
  const installedHome = record.home;
1570
- 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"));
1571
1724
  }
1572
1725
  if (process.argv[1] && await realpath5(resolve5(process.argv[1])).catch(() => "") === await realpath5(fileURLToPath2(import.meta.url))) {
1573
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.5",
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.5",
19
- "@pab-iprg/huohuo-assets-linux-x64": "1.0.0-preview.5"
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/45b0bbe6c53759c5e1f50fbd506ea97ec74adf1f29b3ade98793210f3b66dfbc.tgz",
21
+ "url": "https://bundled.invalid/32e96855b2579f293877c67dc6c5645b2b1fb4e9300f70f3e7a783a6d67a4120.tgz",
22
22
  "bundled": true,
23
- "sha256": "45b0bbe6c53759c5e1f50fbd506ea97ec74adf1f29b3ade98793210f3b66dfbc",
24
- "integrity": "sha512-bFFRkVFn/4OgqU6CneroGm+qy0tP8hXaHZ4U0C6tW3DHGxvBX8c0gn0UTRYIRxrQUQ2QbcXbvcgAG7MsADw26A==",
25
- "downloadBytes": 2609884,
26
- "expandedBytes": 8326138
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",
@@ -50,7 +50,7 @@
50
50
  "assemblySha256": "08cf11de70e49981b4891c0aaa2a5e7b872a95b6aa07557bb629713fa8f9e249"
51
51
  },
52
52
  "requirementsSha256": "57042fff45cee66fe33712472e2e5b88f03a084068ca20242588b2e18b3e167b",
53
- "profilesSha256": "aae1b5ee7f19d24d384a0f57de3e62ea9a1501e316193266d630f773dac058f1",
53
+ "profilesSha256": "fb3540e0d604f26280128a21f97313575848f6348733a221f7e230233fdb8e2d",
54
54
  "budget": {
55
55
  "minimumEffectiveMemoryBytes": 34359738368,
56
56
  "startupHeadroomBytes": 134217728,
@@ -99,7 +99,7 @@
99
99
  },
100
100
  "assetPackage": {
101
101
  "name": "@pab-iprg/huohuo-assets-darwin-arm64",
102
- "version": "1.0.0-preview.5"
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/4bab6aed6c844ab711d0a91b27c2e108ca8d4f4de1f4b30f7668b73f922d8500.tgz",
121
+ "url": "https://bundled.invalid/79cbf018a4363d256fee64f07dc3cd8e434ef1e89ba1dcfdcbdf4c2b0a424f65.tgz",
122
122
  "bundled": true,
123
- "sha256": "4bab6aed6c844ab711d0a91b27c2e108ca8d4f4de1f4b30f7668b73f922d8500",
124
- "integrity": "sha512-Iu0NMfgVLZmPXcxcUfYbW48X0uvHFDUzpQWhPP6unz06v2G1f178m10ehU0pxNzJaUXkwg/p8OH5/DAeuungRg==",
125
- "downloadBytes": 2609889,
126
- "expandedBytes": 8326156
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",
@@ -150,7 +150,7 @@
150
150
  "assemblySha256": "08cf11de70e49981b4891c0aaa2a5e7b872a95b6aa07557bb629713fa8f9e249"
151
151
  },
152
152
  "requirementsSha256": "d4ff8161886fc286e6f8ddfcd070ac5812528971affe7b87baa03662dd242d45",
153
- "profilesSha256": "aae1b5ee7f19d24d384a0f57de3e62ea9a1501e316193266d630f773dac058f1",
153
+ "profilesSha256": "fb3540e0d604f26280128a21f97313575848f6348733a221f7e230233fdb8e2d",
154
154
  "budget": {
155
155
  "minimumEffectiveMemoryBytes": 8589934592,
156
156
  "startupHeadroomBytes": 1073741824,
@@ -198,7 +198,105 @@
198
198
  },
199
199
  "assetPackage": {
200
200
  "name": "@pab-iprg/huohuo-assets-linux-x64",
201
- "version": "1.0.0-preview.5"
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
  ]