@pab-iprg/huohuo 1.0.0-preview.4 → 1.0.0-preview.5

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.
package/README.md CHANGED
@@ -1,16 +1,12 @@
1
- # 火火安装入口
1
+ # 火火轻量入口(本地开发候选)
2
2
 
3
- 入口在设备预检后安装 DSH 和火火运行环境。npm 预览包内置火火运行文件,Python 运行时和依赖从固定上游地址获取并校验摘要。
3
+ 此目录是 `@pab-iprg/huohuo` 的私有构建模板。入口代码只使用 Node 内置模块;npm 按 OS/CPU 选择同版本的静态资源包,资源包不含安装脚本。设备预检前只准备入口和对应平台的压缩资源,不安装 DSH 或 Python。源码具备某个平台的选择逻辑,不代表该平台已完成实机验收。
4
4
 
5
- 构建时需要已完成平台测量与摘要绑定的 `release.json`。没有发行清单时入口明确停止,不从不可信页面选择资源,也不使用虚构的最低配置。
5
+ 构建时需要已完成平台测量与摘要绑定的 `release.json`,可以是旧版单平台清单,也可以是包含多个同版本发行的索引。首次安装与显式升级只选择本机系统/CPU 对应的发行,再按需下载运行包、独立 Python 和固定 wheels;其他平台的大包不下载。Linux 索引条目目前只接受 glibc,未收录的平台提前提示。没有完整发行清单时明确停止。
6
6
 
7
- 发布后的预览入口为 `npx --yes @pab-iprg/huohuo@next`。首版验收基线为 macOS Apple Silicon、Node 24.8.0 和 npm 11.6.0。默认持久目录为 `~/.huohuo`,普通重开保留已安装版本,安装中断后继续原目标。
7
+ 运行 npx 前仍需安装兼容的 Node/npm(当前入口要求 Node 24.8+,具体 Node 主版本由所选发行固定)。运行环境在用户目录中准备,不替换系统 Python;已校验的下载缓存会复用。Python 和 wheels 在构建端选定版本及 SHA;npm 预览包携带下载清单和构建好的 jsonpath wheel,其余 wheel 按固定上游 URL 下载并校验,不在用户机器编译。仍兼容旧的完整离线 wheel 包;npm 依赖按运行包锁文件安装。
8
8
 
9
- 安装前会列出磁盘预留量、内存要求、固定下载项和缓存位置。输入 `y` 继续,直接回车取消。自动化环境可用 `npx --yes @pab-iprg/huohuo@next --yes` 显式同意安装。固定资源显示实际接收量,下载失败可重跑并复用已经校验的文件。
10
-
11
- 发行清单可为同一份固定资源配置备用 HTTPS 地址。确认安装后会探测这些地址,优先从响应较快的可用地址下载,并核对同一个 SHA-256。只有一个已核验来源时,网络中断会自动重试一次。npm 和 pip 仍会按各自的依赖清单安装。
12
-
13
- 命令支持 `huohuo [start|status|doctor|stop|upgrade|uninstall] [--home 目录] [--no-open]`。在另一个终端运行 `npx --yes @pab-iprg/huohuo@next status --watch` 可打开实时状态看板,按 Ctrl+C 退出看板。
9
+ 当前开发入口:`huohuo [start|status|doctor|stop|upgrade|uninstall] [--home 目录] [--no-open]`。`status --watch` 持续显示进度;安装前展示资源计划,`--yes` 可显式确认。默认持久目录 `~/.huohuo`;普通重开保留已安装版本,安装中断继续原目标。npx 缓存保存入口和所选平台资源;再次解析最新入口可能联网。
14
10
 
15
11
  显式升级检查旧迁移不可变、追加迁移顺序、执行库格式与 DSH 会话兼容范围,先一致性备份再迁移和验证,成功后切换;不自动恢复旧备份覆盖新数据。默认卸载保留实例、凭据、会话和备份,不提供直接彻底删除业务数据的快捷入口。
16
12
 
package/cli.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // distribution/bootstrap/entry.ts
4
- import { open as open3, readFile as readFile8, realpath as realpath5 } from "node:fs/promises";
4
+ import { open as open3, readFile as readFile9, realpath as realpath5 } from "node:fs/promises";
5
5
  import { Readable } from "node:stream";
6
- import { homedir } from "node:os";
7
- import { dirname as dirname5, join as join7, resolve as resolve5 } from "node:path";
8
- import { fileURLToPath } from "node:url";
6
+ import { homedir as homedir2 } from "node:os";
7
+ import { dirname as dirname6, join as join8, resolve as resolve5 } from "node:path";
8
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
9
9
 
10
10
  // distribution/bootstrap/install.ts
11
11
  import { readFile as readFile4, mkdir as mkdir3, access, rename as rename3, rm as rm2, realpath as realpath3, readdir } from "node:fs/promises";
@@ -149,7 +149,7 @@ function artifact(a) {
149
149
  }
150
150
  function validateRelease(input) {
151
151
  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) || !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 || ![
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 || ![
153
153
  "minimumEffectiveMemoryBytes",
154
154
  "startupHeadroomBytes",
155
155
  "heavyJobHeadroomBytes",
@@ -364,8 +364,8 @@ async function extractArchive(archive, root, expandedLimit) {
364
364
  }
365
365
  } else if (kind === "1" || kind === "2") {
366
366
  if (size || !linkText || linkText.startsWith("/") || linkText.includes("\\")) throw new Error("\u538B\u7F29\u5305\u94FE\u63A5\u65E0\u6548");
367
- const relative = kind === "2" ? posix.normalize(posix.join(posix.dirname(name), linkText)) : linkText;
368
- links.push({ target, source: safePath(root, relative), symbolic: kind === "2", text: linkText });
367
+ const relative2 = kind === "2" ? posix.normalize(posix.join(posix.dirname(name), linkText)) : linkText;
368
+ links.push({ target, source: safePath(root, relative2), symbolic: kind === "2", text: linkText });
369
369
  } else throw new Error(`\u538B\u7F29\u5305\u6761\u76EE\u7C7B\u578B\u4E0D\u53D7\u652F\u6301\uFF1A${kind}`);
370
370
  }
371
371
  if (size % 512) await take(512 - size % 512);
@@ -418,6 +418,13 @@ async function existingParent(path) {
418
418
  current = parent;
419
419
  }
420
420
  }
421
+ function linuxAvailableMemory(processAvailable, meminfo) {
422
+ if (processAvailable === null || !Number.isSafeInteger(processAvailable) || processAvailable <= 0) return null;
423
+ const match = /^MemAvailable:\s+(\d+) kB$/m.exec(meminfo);
424
+ if (!match) return processAvailable;
425
+ const hostAvailable = Number(match[1]) * 1024;
426
+ return Number.isSafeInteger(hostAvailable) && hostAvailable >= 0 ? Math.min(processAvailable, hostAvailable) : null;
427
+ }
421
428
  async function sampleResources(paths) {
422
429
  const limit = constrainedMemory();
423
430
  let effectiveMemoryBytes = Math.min(totalmem(), limit || Infinity);
@@ -450,16 +457,7 @@ async function sampleResources(paths) {
450
457
  } else if (process.platform === "linux") {
451
458
  try {
452
459
  const info = await readFile3("/proc/meminfo", "utf8");
453
- const available = /^MemAvailable:\s+(\d+) kB$/m.exec(info);
454
- if (available) availableMemoryBytes = Math.min(availableMemoryBytes ?? Infinity, Number(available[1]) * 1024);
455
- if (limit) {
456
- try {
457
- const current = Number((await readFile3("/sys/fs/cgroup/memory.current", "utf8")).trim());
458
- if (Number.isFinite(current)) availableMemoryBytes = Math.min(availableMemoryBytes ?? Infinity, Math.max(0, limit - current));
459
- } catch {
460
- availableMemoryBytes = null;
461
- }
462
- }
460
+ availableMemoryBytes = linuxAvailableMemory(availableMemoryBytes, info);
463
461
  const psi = await readFile3("/proc/pressure/memory", "utf8");
464
462
  const full = /^full avg10=(\d+(?:\.\d+)?)/m.exec(psi);
465
463
  if (full) pressure = Number(full[1]) > 0 ? "high" : "normal";
@@ -548,6 +546,50 @@ function npmCommand() {
548
546
  return { command: process.env.ComSpec ?? "cmd.exe", prefix: ["/d", "/s", "/c", "npm"] };
549
547
  }
550
548
 
549
+ // distribution/bootstrap/catalog.ts
550
+ function hostPlatform() {
551
+ const host = { os: process.platform, arch: process.arch };
552
+ if (host.os === "linux") {
553
+ const report = process.report?.getReport();
554
+ host.libc = report?.header?.glibcVersionRuntime ? "glibc" : "unknown";
555
+ }
556
+ return host;
557
+ }
558
+ function validateCatalog(input) {
559
+ const catalog = input;
560
+ if (!catalog || catalog.kind !== "huohuo-release-catalog" || catalog.format !== 1 || !Array.isArray(catalog.releases) || !catalog.releases.length || catalog.releases.length > 6) {
561
+ throw new Error("\u5E73\u53F0\u53D1\u884C\u7D22\u5F15\u65E0\u6548\uFF1B\u8BF7\u4F7F\u7528\u5B8C\u6574\u7684\u706B\u706B\u5B89\u88C5\u5305");
562
+ }
563
+ const platforms = /* @__PURE__ */ new Set();
564
+ for (const candidate of catalog.releases) {
565
+ const release = validateRelease(candidate);
566
+ const platform = `${release.platform.os}-${release.platform.arch}`;
567
+ if (release.version !== catalog.version || platforms.has(platform)) throw new Error("\u5E73\u53F0\u53D1\u884C\u7D22\u5F15\u542B\u6DF7\u5408\u7248\u672C\u6216\u91CD\u590D\u5E73\u53F0");
568
+ if (release.platform.os === "linux" && release.platform.libc !== "glibc") throw new Error("Linux \u5E73\u53F0\u53D1\u884C\u5FC5\u987B\u660E\u786E glibc \u8FD0\u884C\u73AF\u5883");
569
+ platforms.add(platform);
570
+ }
571
+ return catalog;
572
+ }
573
+ function validateReleaseSource(input) {
574
+ if (input && typeof input === "object" && ("kind" in input || "releases" in input)) return validateCatalog(input);
575
+ return validateRelease(input);
576
+ }
577
+ function selectRelease(input, host = hostPlatform()) {
578
+ const source = validateReleaseSource(input);
579
+ const releases = "releases" in source ? source.releases : [source];
580
+ const selected = releases.find((release) => release.platform.os === host.os && release.platform.arch === host.arch);
581
+ if (!selected) {
582
+ throw new Error(`\u8FD9\u4E2A\u5B89\u88C5\u5305\u5C1A\u672A\u63D0\u4F9B ${host.os}-${host.arch} \u7248\u672C\u3002\u5DF2\u6536\u5F55\uFF1A${releases.map((release) => `${release.platform.os}-${release.platform.arch}`).join("\u3001")}\u3002\u8BF7\u4F7F\u7528\u9002\u914D\u5F53\u524D\u8BBE\u5907\u7684\u53D1\u884C\u7248\u672C\uFF1B\u672A\u4E0B\u8F7D\u6216\u4FEE\u6539\u73B0\u6709\u5B89\u88C5\u3002`);
583
+ }
584
+ assertLibcCompatible(selected, host);
585
+ return selected;
586
+ }
587
+ function assertLibcCompatible(release, host = hostPlatform()) {
588
+ if (release.platform.libc && host.libc !== release.platform.libc) {
589
+ throw new Error("\u8FD9\u4E2A Linux \u5B89\u88C5\u5305\u9700\u8981 glibc \u73AF\u5883\uFF0C\u5F53\u524D\u7CFB\u7EDF\u65E0\u6CD5\u786E\u8BA4\u517C\u5BB9\uFF08\u4F8B\u5982 Alpine/musl\uFF09\u3002\u8BF7\u4F7F\u7528\u5DF2\u9A8C\u8BC1\u7684 glibc \u7CFB\u7EDF\uFF1B\u672A\u4E0B\u8F7D\u6216\u4FEE\u6539\u73B0\u6709\u5B89\u88C5\u3002");
590
+ }
591
+ }
592
+
551
593
  // distribution/bootstrap/install.ts
552
594
  var digest = (value) => createHash2("sha256").update(JSON.stringify(value)).digest("hex");
553
595
  async function ownedDirectory(path, owner) {
@@ -622,13 +664,20 @@ function installationPaths(home, release) {
622
664
  };
623
665
  }
624
666
  function versionAtLeast(actual, minimum) {
667
+ if (![actual, minimum].every((value) => /^\d+(?:\.\d+)*$/.test(value))) return false;
625
668
  const a = actual.split(".").map(Number), b = minimum.split(".").map(Number);
626
- if ([...a, ...b].some((n) => !Number.isInteger(n))) return false;
669
+ if ([...a, ...b].some((n) => !Number.isSafeInteger(n))) return false;
627
670
  for (let i = 0; i < Math.max(a.length, b.length); i++) {
628
671
  if ((a[i] ?? 0) !== (b[i] ?? 0)) return (a[i] ?? 0) > (b[i] ?? 0);
629
672
  }
630
673
  return true;
631
674
  }
675
+ function osReleaseAtLeast(actual, minimum, platform = process.platform) {
676
+ if (platform !== "linux") return versionAtLeast(actual, minimum);
677
+ const numericRelease = (value) => /^(\d+(?:\.\d+)*)(?:-[A-Za-z0-9][A-Za-z0-9.+_-]*)?$/.exec(value)?.[1];
678
+ const a = numericRelease(actual), b = numericRelease(minimum);
679
+ return a !== void 0 && b !== void 0 && versionAtLeast(a, b);
680
+ }
632
681
  async function install(home, candidate, hooks = {}, programsOnly = false) {
633
682
  home = resolve3(home);
634
683
  await mkdir3(home, { recursive: true, mode: 448 });
@@ -675,7 +724,8 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
675
724
  }
676
725
  }
677
726
  const release = record?.release ?? validateRelease(candidate);
678
- if (release.platform.os !== process.platform || release.platform.arch !== process.arch || !versionAtLeast(osRelease(), release.platform.minimumOsRelease) || !versionAtLeast(process.versions.node, "24.8.0") || Number(process.versions.node.split(".")[0]) !== release.nodeMajor) {
727
+ assertLibcCompatible(release);
728
+ if (release.platform.os !== process.platform || release.platform.arch !== process.arch || !osReleaseAtLeast(osRelease(), release.platform.minimumOsRelease) || !versionAtLeast(process.versions.node, "24.8.0") || Number(process.versions.node.split(".")[0]) !== release.nodeMajor) {
679
729
  throw new Error("\u6B64\u53D1\u884C\u5C1A\u672A\u9A8C\u8BC1\u5F53\u524D OS/CPU/Node \u7EC4\u5408\uFF1B\u672A\u5F00\u59CB\u91CD\u4E0B\u8F7D");
680
730
  }
681
731
  record ??= { format: 1, release, releaseDigest: digest(release), home, status: "installing", stage: "preflight", stages: {} };
@@ -1175,8 +1225,19 @@ async function uninstall(home, hooks = {}) {
1175
1225
 
1176
1226
  // distribution/bootstrap/install-ui.ts
1177
1227
  import { createInterface } from "node:readline";
1228
+ import { homedir } from "node:os";
1229
+ import { relative, sep as sep3 } from "node:path";
1178
1230
  var gb = (bytes) => bytes === null ? "\u672A\u77E5" : `${(bytes / 1e9).toFixed(2)} GB`;
1179
1231
  var mb = (bytes) => bytes < 1e5 ? `${(bytes / 1e3).toFixed(1)} KB` : `${(bytes / 1e6).toFixed(1)} MB`;
1232
+ var memory = (bytes) => {
1233
+ if (bytes === null) return "\u672A\u77E5";
1234
+ return bytes < 2 ** 30 ? `${Math.floor(bytes / 2 ** 20)} MiB` : `${Number((bytes / 2 ** 30).toFixed(2))} GiB`;
1235
+ };
1236
+ function displayPath(path) {
1237
+ const local = relative(homedir(), path);
1238
+ if (!local) return "~";
1239
+ return local === ".." || local.startsWith(`..${sep3}`) ? path : `~${sep3}${local}`;
1240
+ }
1180
1241
  function installUI(assumeYes) {
1181
1242
  const interactive = process.stdin.isTTY && process.stderr.isTTY;
1182
1243
  const output = process.stderr;
@@ -1192,24 +1253,46 @@ function installUI(assumeYes) {
1192
1253
  clear();
1193
1254
  output.write(text + "\n");
1194
1255
  };
1256
+ const heading = (text) => line(interactive && process.env.TERM !== "dumb" && process.env.NO_COLOR === void 0 ? `\x1B[1m${text}\x1B[22m` : text);
1195
1257
  return {
1196
1258
  async confirm(plan) {
1197
1259
  const { sample, policy, disks } = plan.resources;
1198
- line(`\u5B89\u88C5\u76EE\u5F55 ${plan.home}`);
1260
+ const narrow = interactive && output.columns > 0 && output.columns < 48;
1261
+ line("");
1262
+ heading("\u706B\u706B\u5B89\u88C5 \xB7 \u786E\u8BA4\u8BA1\u5212");
1263
+ line(` \u5B89\u88C5\u5230 ${displayPath(plan.home)}`);
1264
+ line("");
1265
+ heading("\u672C\u673A\u8D44\u6E90");
1199
1266
  for (const [index, disk] of disks.entries()) {
1200
1267
  const location = sample.disks.find((item) => item.device === disk.device)?.paths[0] ?? plan.home;
1201
- line(`\u78C1\u76D8 ${index + 1}\uFF08${location}\uFF09 \u5B89\u88C5\u9884\u7559 ${gb(disk.requiredBytes)}\uFF0C\u5F53\u524D\u53EF\u7528 ${gb(disk.availableBytes)}`);
1268
+ if (disks.length > 1) line(` \u78C1\u76D8 ${index + 1} ${displayPath(location)}`);
1269
+ const need = `\u9700\u7559 ${gb(disk.requiredBytes)}`;
1270
+ const free = `\u53EF\u7528 ${gb(disk.availableBytes)}`;
1271
+ line(narrow ? ` \u78C1\u76D8 ${need}
1272
+ ${free}` : ` \u78C1\u76D8 ${need} \xB7 ${free}`);
1273
+ }
1274
+ const available = `\u53EF\u7528 ${memory(sample.availableMemoryBytes)}`;
1275
+ const total = `\u603B\u91CF ${memory(sample.effectiveMemoryBytes)}`;
1276
+ line(narrow ? ` \u5185\u5B58 ${available}
1277
+ ${total}` : ` \u5185\u5B58 ${available} \xB7 ${total}`);
1278
+ line(` \u542F\u52A8\u9700\u7559 ${memory(policy.startupHeadroomBytes)}`);
1279
+ line(` \u6700\u4F4E\u5BB9\u91CF ${memory(policy.minimumEffectiveMemoryBytes)}`);
1280
+ if (policy.observedInstallRssBytes) line(` \u5386\u53F2\u5B89\u88C5\u5CF0\u503C\u7EA6 ${memory(policy.observedInstallRssBytes)}`);
1281
+ for (const reason of plan.resources.reasons) line(` \u63D0\u793A ${reason}`);
1282
+ line("");
1283
+ heading("\u4E0B\u8F7D\u8BA1\u5212");
1284
+ const bytes = plan.downloads.reduce((total2, file) => total2 + file.size, 0);
1285
+ line(plan.downloads.length ? ` \u56FA\u5B9A\u8D44\u6E90\u5F85\u4E0B\u8F7D ${plan.downloads.length} \u9879 \xB7 ${mb(bytes)}` : " \u56FA\u5B9A\u8D44\u6E90\u65E0\u9700\u8054\u7F51\u4E0B\u8F7D");
1286
+ const local = [plan.bundled ? `\u968F\u5305 ${plan.bundled} \u9879` : "", plan.cached ? `\u590D\u7528\u7F13\u5B58 ${plan.cached} \u9879` : ""].filter(Boolean);
1287
+ if (local.length) line(` ${local.join(" \xB7 ")}`);
1288
+ for (const file of plan.downloads) {
1289
+ line(` ${file.name} ${mb(file.size)}`);
1290
+ line(` \u6765\u6E90 ${file.host}`);
1202
1291
  }
1203
- line(`\u5185\u5B58 \u672C\u673A\u5BB9\u91CF ${gb(sample.effectiveMemoryBytes)}\uFF0C\u5F53\u524D\u53EF\u7528 ${gb(sample.availableMemoryBytes)}`);
1204
- line(`\u5185\u5B58\u8981\u6C42 \u6700\u4F4E\u5BB9\u91CF ${gb(policy.minimumEffectiveMemoryBytes)}\uFF0C\u542F\u52A8\u9700\u7559\u51FA ${gb(policy.startupHeadroomBytes)}`);
1205
- line(policy.observedInstallRssBytes ? `\u5B89\u88C5\u5185\u5B58\u53C2\u8003 \u540C\u5E73\u53F0\u5386\u53F2\u8FDB\u7A0B\u5185\u5B58\u5CF0\u503C ${gb(policy.observedInstallRssBytes)}\uFF0C\u5B9E\u9645\u5360\u7528\u4F1A\u53D8\u5316` : "\u5B89\u88C5\u5185\u5B58\u5CF0\u503C\u968F\u4F9D\u8D56\u548C\u672C\u673A\u72B6\u6001\u53D8\u5316\uFF0C\u5F53\u524D\u6E05\u5355\u6682\u65E0\u5B9E\u6D4B\u503C");
1206
- for (const reason of plan.resources.reasons) line(reason);
1207
- const bytes = plan.downloads.reduce((total, file) => total + file.size, 0);
1208
- line(`\u56FA\u5B9A\u8D44\u6E90 \u8054\u7F51\u4E0B\u8F7D ${plan.downloads.length} \u9879\uFF08${mb(bytes)}\uFF09\uFF0C\u968F\u5305 ${plan.bundled} \u9879\uFF0C\u7F13\u5B58 ${plan.cached} \u9879`);
1209
- for (const file of plan.downloads) line(` ${file.name} ${mb(file.size)} ${file.host}`);
1210
- line(`\u56FA\u5B9A\u8D44\u6E90\u4FDD\u5B58\u81F3 ${plan.home}/cache`);
1211
- if (plan.upstreamWheelCount !== void 0) line(`Python \u4F9D\u8D56\u6E05\u5355 ${plan.upstreamWheelCount} \u9879\u56FA\u5B9A\u4E0A\u6E38\u6587\u4EF6\uFF0C\u5DF2\u5B89\u88C5\u9879\u53EF\u80FD\u8DF3\u8FC7\u3002`);
1212
- line("\u540E\u7EED\u8FD8\u4F1A\u5B89\u88C5 npm \u548C Python \u4F9D\u8D56\uFF0C\u5B9E\u9645\u8054\u7F51\u4E0B\u8F7D\u91CF\u53D6\u51B3\u4E8E\u672C\u673A\u7F13\u5B58\u548C\u4F9D\u8D56\u89E3\u6790\u7ED3\u679C\u3002");
1292
+ if (plan.downloads.length || plan.bundled) line(` \u4FDD\u5B58\u5230 ${displayPath(plan.home)}/cache`);
1293
+ if (plan.upstreamWheelCount !== void 0) line(` Python \u4E0A\u6E38\u6587\u4EF6 ${plan.upstreamWheelCount} \u9879`);
1294
+ line(" npm \u548C Python \u4F9D\u8D56\u4ECD\u53EF\u80FD\u8054\u7F51");
1295
+ line("");
1213
1296
  if (assumeYes) return true;
1214
1297
  if (!interactive) throw new Error("\u9700\u8981\u786E\u8BA4\u5B89\u88C5\u3002\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C\uFF0C\u6216\u5728\u706B\u706B\u547D\u4EE4\u672B\u5C3E\u52A0\u4E0A --yes\u3002");
1215
1298
  const terminal = createInterface({ input: process.stdin, output, terminal: true });
@@ -1333,6 +1416,27 @@ async function showStatus(home, watch) {
1333
1416
  }
1334
1417
  }
1335
1418
 
1419
+ // distribution/bootstrap/assets.ts
1420
+ import { createRequire } from "node:module";
1421
+ import { readFile as readFile8 } from "node:fs/promises";
1422
+ import { dirname as dirname5, join as join7 } from "node:path";
1423
+ import { fileURLToPath } from "node:url";
1424
+ async function assetsDirectory(release, entryURL = import.meta.url) {
1425
+ if (!release?.assetPackage) return join7(dirname5(fileURLToPath(entryURL)), "assets");
1426
+ const expected = release.assetPackage;
1427
+ let file;
1428
+ try {
1429
+ file = createRequire(entryURL).resolve(`${expected.name}/package.json`);
1430
+ } catch {
1431
+ throw new Error("\u6CA1\u6709\u627E\u5230\u5F53\u524D\u8BBE\u5907\u7684\u706B\u706B\u8D44\u6E90\u5305\u3002\u8BF7\u91CD\u65B0\u6267\u884C\u5B8C\u6574\u5B89\u88C5\u547D\u4EE4\uFF0C\u5E76\u786E\u4FDD\u6CA1\u6709\u8DF3\u8FC7 npm \u53EF\u9009\u4F9D\u8D56\u3002");
1432
+ }
1433
+ const manifest = JSON.parse(await readFile8(file, "utf8"));
1434
+ if (manifest.name !== expected.name || manifest.version !== expected.version) {
1435
+ 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
+ }
1437
+ return join7(dirname5(file), "assets");
1438
+ }
1439
+
1336
1440
  // distribution/bootstrap/entry.ts
1337
1441
  function artifactSource(source, directory) {
1338
1442
  return async (url, options) => {
@@ -1341,8 +1445,8 @@ function artifactSource(source, directory) {
1341
1445
  const artifact2 = source && [source.runtime, source.python, source.pythonDependencies].find((item) => item.url === String(url));
1342
1446
  const sha256 = bundled?.[1] ?? artifact2?.sha256;
1343
1447
  if (!sha256) throw new Error("\u672C\u673A\u4EA7\u7269\u76EE\u5F55\u7F3A\u5C11\u8BE5\u56FA\u5B9A\u53D1\u884C\u8D44\u6E90");
1344
- const root = directory ?? join7(dirname5(fileURLToPath(import.meta.url)), "assets");
1345
- const path = join7(root, sha256 + ".tgz");
1448
+ const root = directory ?? await assetsDirectory(source, import.meta.url);
1449
+ const path = join8(root, sha256 + ".tgz");
1346
1450
  const file = await open3(path).catch((error) => {
1347
1451
  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");
1348
1452
  throw error;
@@ -1351,7 +1455,7 @@ function artifactSource(source, directory) {
1351
1455
  };
1352
1456
  }
1353
1457
  async function main(args = process.argv.slice(2)) {
1354
- let home = join7(homedir(), ".huohuo"), noOpen = false, assumeYes = false, watch = false, command = "start";
1458
+ let home = join8(homedir2(), ".huohuo"), noOpen = false, assumeYes = false, watch = false, command = "start";
1355
1459
  let releaseFile, artifactDirectory;
1356
1460
  for (let i = 0; i < args.length; i++) {
1357
1461
  if (args[i] === "--home") {
@@ -1366,8 +1470,8 @@ async function main(args = process.argv.slice(2)) {
1366
1470
  else if (args[i] === "--yes") assumeYes = true;
1367
1471
  else if (args[i] === "--watch") watch = true;
1368
1472
  else if (args[i] === "--version") {
1369
- const packageFile = join7(dirname5(fileURLToPath(import.meta.url)), "package.json");
1370
- const manifest = JSON.parse(await readFile8(packageFile, "utf8"));
1473
+ const packageFile = join8(dirname6(fileURLToPath2(import.meta.url)), "package.json");
1474
+ const manifest = JSON.parse(await readFile9(packageFile, "utf8"));
1371
1475
  console.log(`${manifest.name} ${manifest.version}${manifest.private ? "\uFF08\u672C\u5730\u5019\u9009\uFF09" : ""}`);
1372
1476
  return;
1373
1477
  } else if (["start", "status", "doctor", "stop", "upgrade", "uninstall"].includes(args[i])) command = args[i];
@@ -1398,11 +1502,11 @@ async function main(args = process.argv.slice(2)) {
1398
1502
  };
1399
1503
  const ui = installUI(assumeYes);
1400
1504
  if (command === "upgrade") {
1401
- const candidate = JSON.parse(await readFile8(releaseFile ?? join7(dirname5(fileURLToPath(import.meta.url)), "release.json"), "utf8"));
1505
+ const candidate = selectRelease(JSON.parse(await readFile9(releaseFile ?? join8(dirname6(fileURLToPath2(import.meta.url)), "release.json"), "utf8")));
1402
1506
  await upgrade(home, candidate, { fetcher: artifactSource(candidate, artifactDirectory), progress, ...ui });
1403
1507
  const current = await readInstallation(home);
1404
1508
  if (await control(home, current.releaseDigest, noOpen ? "status" : "open")) return;
1405
- await run(process.execPath, [join7(installationPaths(home, current.release).runtime, "lib/distribution/managed.js"), "start", home, ...noOpen ? ["--no-open"] : []], home);
1509
+ await run(process.execPath, [join8(installationPaths(home, current.release).runtime, "lib/distribution/managed.js"), "start", home, ...noOpen ? ["--no-open"] : []], home);
1406
1510
  return;
1407
1511
  }
1408
1512
  if (command === "uninstall") {
@@ -1420,7 +1524,7 @@ async function main(args = process.argv.slice(2)) {
1420
1524
  return;
1421
1525
  }
1422
1526
  const paths = installationPaths(home, record.release);
1423
- const targets = [home, paths.directory, paths.environment, join7(home, "instances/default")];
1527
+ const targets = [home, paths.directory, paths.environment, join8(home, "instances/default")];
1424
1528
  const resources = evaluateResources(await sampleResources(targets), record.release.budget, targets.map((path) => ({ path, bytes: 0 })), "start");
1425
1529
  console.log(JSON.stringify({
1426
1530
  home,
@@ -1441,30 +1545,31 @@ async function main(args = process.argv.slice(2)) {
1441
1545
  }
1442
1546
  const runtime = installationPaths(home, record.release).runtime;
1443
1547
  try {
1444
- await readFile8(join7(runtime, "lib/distribution/managed.js"));
1548
+ await readFile9(join8(runtime, "lib/distribution/managed.js"));
1445
1549
  } catch (e) {
1446
1550
  if (e.code !== "ENOENT") throw e;
1447
1551
  record = void 0;
1448
1552
  }
1449
1553
  }
1450
1554
  if (!record || record.status !== "ready") {
1451
- let catalog = null;
1452
- if (!record || ["preflight", "download"].includes(record.stage)) {
1555
+ const persisted = await readInstallation(home);
1556
+ let source = persisted?.release;
1557
+ if (!source) {
1558
+ let catalog = null;
1453
1559
  try {
1454
- catalog = JSON.parse(await readFile8(releaseFile ?? join7(dirname5(fileURLToPath(import.meta.url)), "release.json"), "utf8"));
1560
+ catalog = JSON.parse(await readFile9(releaseFile ?? join8(dirname6(fileURLToPath2(import.meta.url)), "release.json"), "utf8"));
1455
1561
  } catch (e) {
1456
1562
  if (e.code !== "ENOENT") throw e;
1457
1563
  }
1564
+ source = selectRelease(catalog);
1458
1565
  }
1459
- const source = (await readInstallation(home))?.release ?? catalog;
1460
1566
  const fetcher = artifactSource(source, artifactDirectory);
1461
- const bundleDirectory = !releaseFile && !artifactDirectory ? join7(dirname5(fileURLToPath(import.meta.url)), "assets") : void 0;
1462
- record = await install(home, catalog, { fetcher, progress, bundleDirectory, ...ui });
1567
+ record = await install(home, source, { fetcher, progress, ...ui });
1463
1568
  }
1464
1569
  const installedHome = record.home;
1465
- await run(process.execPath, [join7(installationPaths(installedHome, record.release).runtime, "lib/distribution/managed.js"), "start", installedHome, ...noOpen ? ["--no-open"] : []], installedHome);
1570
+ await run(process.execPath, [join8(installationPaths(installedHome, record.release).runtime, "lib/distribution/managed.js"), "start", installedHome, ...noOpen ? ["--no-open"] : []], installedHome);
1466
1571
  }
1467
- if (process.argv[1] && await realpath5(resolve5(process.argv[1])).catch(() => "") === await realpath5(fileURLToPath(import.meta.url))) {
1572
+ if (process.argv[1] && await realpath5(resolve5(process.argv[1])).catch(() => "") === await realpath5(fileURLToPath2(import.meta.url))) {
1468
1573
  main().catch((error) => {
1469
1574
  console.error(error instanceof Error ? error.message : "\u5B89\u88C5\u6216\u542F\u52A8\u5931\u8D25");
1470
1575
  if (error?.code !== "INSTALL_CANCELLED") process.exitCode = 1;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@pab-iprg/huohuo",
3
- "version": "1.0.0-preview.4",
3
+ "version": "1.0.0-preview.5",
4
4
  "type": "module",
5
- "description": "火火投研伙伴安装入口",
5
+ "description": "火火双平台安装入口(实验预览)",
6
6
  "bin": {
7
7
  "huohuo": "./cli.js"
8
8
  },
@@ -12,11 +12,14 @@
12
12
  "files": [
13
13
  "cli.js",
14
14
  "release.json",
15
- "README.md",
16
- "assets"
15
+ "README.md"
17
16
  ],
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"
20
+ },
18
21
  "publishConfig": {
19
22
  "access": "public",
20
- "tag": "next"
23
+ "tag": "preview"
21
24
  }
22
25
  }
package/release.json CHANGED
@@ -1,95 +1,205 @@
1
1
  {
2
+ "kind": "huohuo-release-catalog",
2
3
  "format": 1,
3
- "bootstrapFormat": 1,
4
4
  "version": "1.0.0",
5
- "nodeMajor": 24,
6
- "npmMajor": 11,
7
- "qualification": "qualified",
8
- "platform": {
9
- "os": "darwin",
10
- "arch": "arm64",
11
- "minimumOsRelease": "25.6.0"
12
- },
13
- "runtime": {
14
- "name": "@huohuo/research-agent",
15
- "version": "1.0.0",
16
- "url": "https://bundled.invalid/550095205f2f92e0641b286f15fbb01ece0b29339ecf7c5ee18539a2b7f5def8.tgz",
17
- "sha256": "550095205f2f92e0641b286f15fbb01ece0b29339ecf7c5ee18539a2b7f5def8",
18
- "integrity": "sha512-AI87lAFcPyGM5qtQ4B2Ji3CJWE10twn48t0tSnuEeIxlVAKVEJBWRe7aSCkOKwLvpeai8CRts50oEMh0juDWrA==",
19
- "downloadBytes": 2548683,
20
- "expandedBytes": 8255144,
21
- "bundled": true
22
- },
23
- "python": {
24
- "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",
25
- "sha256": "8392b6ea51d94391befb2c31ebe75463f045d0b69dfcf5e1bb3234e1f5fa3405",
26
- "downloadBytes": 25265774,
27
- "expandedBytes": 66020864,
28
- "redirectHosts": [
29
- "release-assets.githubusercontent.com"
30
- ],
31
- "executable": "python/bin/python3",
32
- "build": "cpython-3.13.18+20261026-aarch64-apple-darwin-install_only_stripped.tar.gz",
33
- "license": "python/lib/python3.13/LICENSE.txt"
34
- },
35
- "pythonDependencies": {
36
- "url": "https://bundled.invalid/4f008fe4491617729554e056ad59eee686ccc5d95c9f1d5259da705bf054ce21.tgz",
37
- "sha256": "4f008fe4491617729554e056ad59eee686ccc5d95c9f1d5259da705bf054ce21",
38
- "downloadBytes": 10235,
39
- "expandedBytes": 15007,
40
- "bundled": true,
41
- "upstreamWheelCount": 39
42
- },
43
- "dsh": {
44
- "version": "0.2.1-rc.2",
45
- "assemblySha256": "08cf11de70e49981b4891c0aaa2a5e7b872a95b6aa07557bb629713fa8f9e249"
46
- },
47
- "requirementsSha256": "57042fff45cee66fe33712472e2e5b88f03a084068ca20242588b2e18b3e167b",
48
- "profilesSha256": "bdd628f3a599a69a9835db9904fb55d36c57e0b3486b4f3f7dbec8d8a47f36f0",
49
- "budget": {
50
- "minimumEffectiveMemoryBytes": 34359738368,
51
- "startupHeadroomBytes": 134217728,
52
- "heavyJobHeadroomBytes": 1073741824,
53
- "diskReserveBytes": 1073741824,
54
- "recoverySamples": 3,
55
- "processTreeBudgetBytes": 17179869184,
56
- "sampleIntervalMs": 2000,
57
- "sampleMaxAgeMs": 10000,
58
- "installTemporaryBytes": 1073741824,
59
- "dependenciesBytes": 4294967296,
60
- "backupBytes": 0,
61
- "observedInstallRssBytes": 1904771072
62
- },
63
- "schema": {
64
- "minimum": 1,
65
- "maximum": 1,
66
- "migrationsSha256": "ba029fdbffa5320e61393b484d08accafce60e9aa8dacde5b6212fb79dd9146b",
67
- "files": {
68
- "001_init.sql": "b42b6192d872f0f65cfbbf9d0c7fb442d3aa49584a6cf321d3bcc641b31b70da",
69
- "002_contact_decision.sql": "af82cb9975a09fa09c0f970b70d04a7b55a960d72e0853f4573d571efaa46f0a",
70
- "003_schedules.sql": "2937f7be2693ce9b410ee9a73bd10c5e819c5081a10a6b3993c33f37ad42295f",
71
- "004_condition_state.sql": "f081d250dd525ad57a450bd6bca59a75f8fb1428177d670ae9fa142ed79721d6",
72
- "005_execution_handoff.sql": "ebf2e5c4890b7a73ae1ffb57a70bb15183f48a6c1fa2acf3a48b95ccb3d992b3",
73
- "006_entity_confirmation.sql": "d25ee376e7f6dd55df2edeb68d7591ef5fbaa34eeb3eef662cd76e183526d3c0",
74
- "007_research_tool_calls.sql": "df9c7d8d25b44b151f743e2d5167b34ae89c64e67423d8af554364fd792da942",
75
- "008_followup_notifications.sql": "98865d811879afd679846087a5f01ee2db371dcb90381a9c400c940a5c1c4b37",
76
- "008_investment.sql": "71cecf6215552c725797059efdcb501247e1225c7d9f662de606266d3a947e61",
77
- "009_notification_channels.sql": "572424d739203a8514ae59920a0ad69cf83f278479b48d48becb88bddda25d9a",
78
- "010_strategies.sql": "af023fab201db6bd2e15e2348125d112bc21cf3a59d8afd0a2d616fc05cee209",
79
- "011_strategy_run_subjects.sql": "53a1c216ed775a06549dcc201f9494ceabe308bee6dc52a214b685cca2f2fe8d",
80
- "012_strategy_schedule.sql": "ccddd81bb70d2c86356e6f1ad09132e7397837d17e926b515c8e281a362bdd58",
81
- "014_industry_chain.sql": "b656efffe746d14ca350ea1c9ecfcce2d98b5753e4b2f862c75d0456a062fd4a",
82
- "015_source_heads.sql": "652bf637892edbbe969ce5ed4ce45a4a0c43d03de395fce1b828fc4c0169f885",
83
- "016_legacy_transfer.sql": "b6e3c214b103487d741226b0f83525e0cacbb0c440bd1935d0e963dc9b9bfd07",
84
- "017_unified_strategy_work.sql": "8d055ca58f3e73482fb16c630c733ae93197244b37e4509cc90c7dec3bf88d60",
85
- "018_personalization.sql": "7f62a4eb74707fb071f56d6057b6d6517ed7648af23ebe290f3be15d260a103c",
86
- "019_position_risk.sql": "3c461fa435b352127fd4ee4e7ed3de2c924561a3727dce1535d7d3a47c436ca9",
87
- "020_notification_tests.sql": "3eb0d72eadf04b074208dbe0ef6a72ee614ec11a15143108a2471568b7b4fb22",
88
- "021_notification_priority.sql": "f742b4c4b796e918bfc6a08bac8c5ae206382075925acf7b1c430b033f5177e6",
89
- "022_research_job_priority.sql": "fcb0c0af025806faab7cd403097a9d2c55e07331ab0a78b2926c1927a87f4ed4",
90
- "023_adaptive_notifications.sql": "80630e2dad73844d48b265fbc33648b01d8321a6fd9a0587ef4dc01f362aadec",
91
- "023_notification_attention.sql": "5dd85fb001192558211c7b69e3452984c52119f215750ed6283b296d3dccc842",
92
- "024_t_trade_watch.sql": "aa38470ddab00e0cbca07ce6d5cc31399ba2b6e40c8d8d888e88ec093a9f89ef"
5
+ "releases": [
6
+ {
7
+ "format": 1,
8
+ "bootstrapFormat": 1,
9
+ "version": "1.0.0",
10
+ "nodeMajor": 24,
11
+ "npmMajor": 11,
12
+ "qualification": "controlled-acceptance-only",
13
+ "platform": {
14
+ "os": "darwin",
15
+ "arch": "arm64",
16
+ "minimumOsRelease": "25.6.0"
17
+ },
18
+ "runtime": {
19
+ "name": "@huohuo/research-agent",
20
+ "version": "1.0.0",
21
+ "url": "https://bundled.invalid/45b0bbe6c53759c5e1f50fbd506ea97ec74adf1f29b3ade98793210f3b66dfbc.tgz",
22
+ "bundled": true,
23
+ "sha256": "45b0bbe6c53759c5e1f50fbd506ea97ec74adf1f29b3ade98793210f3b66dfbc",
24
+ "integrity": "sha512-bFFRkVFn/4OgqU6CneroGm+qy0tP8hXaHZ4U0C6tW3DHGxvBX8c0gn0UTRYIRxrQUQ2QbcXbvcgAG7MsADw26A==",
25
+ "downloadBytes": 2609884,
26
+ "expandedBytes": 8326138
27
+ },
28
+ "python": {
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",
30
+ "sha256": "8392b6ea51d94391befb2c31ebe75463f045d0b69dfcf5e1bb3234e1f5fa3405",
31
+ "downloadBytes": 25265774,
32
+ "expandedBytes": 66020864,
33
+ "redirectHosts": [
34
+ "release-assets.githubusercontent.com"
35
+ ],
36
+ "executable": "python/bin/python3",
37
+ "build": "cpython-3.13.18+20261026-aarch64-apple-darwin-install_only_stripped.tar.gz",
38
+ "license": "python/lib/python3.13/LICENSE.txt"
39
+ },
40
+ "pythonDependencies": {
41
+ "url": "https://bundled.invalid/61bbfd92aa43f2e1ae8e339a03e88efb145ae3266f00ef8fc979c8bc1ed502a5.tgz",
42
+ "bundled": true,
43
+ "sha256": "61bbfd92aa43f2e1ae8e339a03e88efb145ae3266f00ef8fc979c8bc1ed502a5",
44
+ "downloadBytes": 10215,
45
+ "expandedBytes": 20480,
46
+ "upstreamWheelCount": 39
47
+ },
48
+ "dsh": {
49
+ "version": "0.2.1-rc.2",
50
+ "assemblySha256": "08cf11de70e49981b4891c0aaa2a5e7b872a95b6aa07557bb629713fa8f9e249"
51
+ },
52
+ "requirementsSha256": "57042fff45cee66fe33712472e2e5b88f03a084068ca20242588b2e18b3e167b",
53
+ "profilesSha256": "aae1b5ee7f19d24d384a0f57de3e62ea9a1501e316193266d630f773dac058f1",
54
+ "budget": {
55
+ "minimumEffectiveMemoryBytes": 34359738368,
56
+ "startupHeadroomBytes": 134217728,
57
+ "heavyJobHeadroomBytes": 1073741824,
58
+ "diskReserveBytes": 1073741824,
59
+ "recoverySamples": 3,
60
+ "processTreeBudgetBytes": 17179869184,
61
+ "sampleIntervalMs": 2000,
62
+ "sampleMaxAgeMs": 10000,
63
+ "installTemporaryBytes": 1073741824,
64
+ "dependenciesBytes": 4294967296,
65
+ "backupBytes": 0,
66
+ "observedInstallRssBytes": 1904771072
67
+ },
68
+ "schema": {
69
+ "minimum": 1,
70
+ "maximum": 1,
71
+ "migrationsSha256": "ba029fdbffa5320e61393b484d08accafce60e9aa8dacde5b6212fb79dd9146b",
72
+ "files": {
73
+ "001_init.sql": "b42b6192d872f0f65cfbbf9d0c7fb442d3aa49584a6cf321d3bcc641b31b70da",
74
+ "002_contact_decision.sql": "af82cb9975a09fa09c0f970b70d04a7b55a960d72e0853f4573d571efaa46f0a",
75
+ "003_schedules.sql": "2937f7be2693ce9b410ee9a73bd10c5e819c5081a10a6b3993c33f37ad42295f",
76
+ "004_condition_state.sql": "f081d250dd525ad57a450bd6bca59a75f8fb1428177d670ae9fa142ed79721d6",
77
+ "005_execution_handoff.sql": "ebf2e5c4890b7a73ae1ffb57a70bb15183f48a6c1fa2acf3a48b95ccb3d992b3",
78
+ "006_entity_confirmation.sql": "d25ee376e7f6dd55df2edeb68d7591ef5fbaa34eeb3eef662cd76e183526d3c0",
79
+ "007_research_tool_calls.sql": "df9c7d8d25b44b151f743e2d5167b34ae89c64e67423d8af554364fd792da942",
80
+ "008_followup_notifications.sql": "98865d811879afd679846087a5f01ee2db371dcb90381a9c400c940a5c1c4b37",
81
+ "008_investment.sql": "71cecf6215552c725797059efdcb501247e1225c7d9f662de606266d3a947e61",
82
+ "009_notification_channels.sql": "572424d739203a8514ae59920a0ad69cf83f278479b48d48becb88bddda25d9a",
83
+ "010_strategies.sql": "af023fab201db6bd2e15e2348125d112bc21cf3a59d8afd0a2d616fc05cee209",
84
+ "011_strategy_run_subjects.sql": "53a1c216ed775a06549dcc201f9494ceabe308bee6dc52a214b685cca2f2fe8d",
85
+ "012_strategy_schedule.sql": "ccddd81bb70d2c86356e6f1ad09132e7397837d17e926b515c8e281a362bdd58",
86
+ "014_industry_chain.sql": "b656efffe746d14ca350ea1c9ecfcce2d98b5753e4b2f862c75d0456a062fd4a",
87
+ "015_source_heads.sql": "652bf637892edbbe969ce5ed4ce45a4a0c43d03de395fce1b828fc4c0169f885",
88
+ "016_legacy_transfer.sql": "b6e3c214b103487d741226b0f83525e0cacbb0c440bd1935d0e963dc9b9bfd07",
89
+ "017_unified_strategy_work.sql": "8d055ca58f3e73482fb16c630c733ae93197244b37e4509cc90c7dec3bf88d60",
90
+ "018_personalization.sql": "7f62a4eb74707fb071f56d6057b6d6517ed7648af23ebe290f3be15d260a103c",
91
+ "019_position_risk.sql": "3c461fa435b352127fd4ee4e7ed3de2c924561a3727dce1535d7d3a47c436ca9",
92
+ "020_notification_tests.sql": "3eb0d72eadf04b074208dbe0ef6a72ee614ec11a15143108a2471568b7b4fb22",
93
+ "021_notification_priority.sql": "f742b4c4b796e918bfc6a08bac8c5ae206382075925acf7b1c430b033f5177e6",
94
+ "022_research_job_priority.sql": "fcb0c0af025806faab7cd403097a9d2c55e07331ab0a78b2926c1927a87f4ed4",
95
+ "023_adaptive_notifications.sql": "80630e2dad73844d48b265fbc33648b01d8321a6fd9a0587ef4dc01f362aadec",
96
+ "023_notification_attention.sql": "5dd85fb001192558211c7b69e3452984c52119f215750ed6283b296d3dccc842",
97
+ "024_t_trade_watch.sql": "aa38470ddab00e0cbca07ce6d5cc31399ba2b6e40c8d8d888e88ec093a9f89ef"
98
+ }
99
+ },
100
+ "assetPackage": {
101
+ "name": "@pab-iprg/huohuo-assets-darwin-arm64",
102
+ "version": "1.0.0-preview.5"
103
+ }
104
+ },
105
+ {
106
+ "format": 1,
107
+ "bootstrapFormat": 1,
108
+ "version": "1.0.0",
109
+ "nodeMajor": 24,
110
+ "npmMajor": 11,
111
+ "qualification": "controlled-acceptance-only",
112
+ "platform": {
113
+ "os": "linux",
114
+ "arch": "x64",
115
+ "minimumOsRelease": "6.8.0",
116
+ "libc": "glibc"
117
+ },
118
+ "runtime": {
119
+ "name": "@huohuo/research-agent",
120
+ "version": "1.0.0",
121
+ "url": "https://bundled.invalid/4bab6aed6c844ab711d0a91b27c2e108ca8d4f4de1f4b30f7668b73f922d8500.tgz",
122
+ "bundled": true,
123
+ "sha256": "4bab6aed6c844ab711d0a91b27c2e108ca8d4f4de1f4b30f7668b73f922d8500",
124
+ "integrity": "sha512-Iu0NMfgVLZmPXcxcUfYbW48X0uvHFDUzpQWhPP6unz06v2G1f178m10ehU0pxNzJaUXkwg/p8OH5/DAeuungRg==",
125
+ "downloadBytes": 2609889,
126
+ "expandedBytes": 8326156
127
+ },
128
+ "python": {
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",
130
+ "sha256": "df15fe60d3ff55bf9dcf25824b5572756bb8816ff93865a273d3301d7066a8b7",
131
+ "downloadBytes": 35395906,
132
+ "expandedBytes": 102403072,
133
+ "redirectHosts": [
134
+ "release-assets.githubusercontent.com"
135
+ ],
136
+ "executable": "python/bin/python3",
137
+ "build": "cpython-3.13.18+20261026-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
138
+ "license": "python/lib/python3.13/LICENSE.txt"
139
+ },
140
+ "pythonDependencies": {
141
+ "url": "https://bundled.invalid/7e42648979228aa347ad334e4d40271a2c3dd78b76ebf6c5c557b0fd8fe36ec2.tgz",
142
+ "bundled": true,
143
+ "sha256": "7e42648979228aa347ad334e4d40271a2c3dd78b76ebf6c5c557b0fd8fe36ec2",
144
+ "downloadBytes": 10367,
145
+ "expandedBytes": 20480,
146
+ "upstreamWheelCount": 40
147
+ },
148
+ "dsh": {
149
+ "version": "0.2.1-rc.2",
150
+ "assemblySha256": "08cf11de70e49981b4891c0aaa2a5e7b872a95b6aa07557bb629713fa8f9e249"
151
+ },
152
+ "requirementsSha256": "d4ff8161886fc286e6f8ddfcd070ac5812528971affe7b87baa03662dd242d45",
153
+ "profilesSha256": "aae1b5ee7f19d24d384a0f57de3e62ea9a1501e316193266d630f773dac058f1",
154
+ "budget": {
155
+ "minimumEffectiveMemoryBytes": 8589934592,
156
+ "startupHeadroomBytes": 1073741824,
157
+ "heavyJobHeadroomBytes": 1073741824,
158
+ "processTreeBudgetBytes": 4294967296,
159
+ "diskReserveBytes": 1073741824,
160
+ "recoverySamples": 3,
161
+ "sampleIntervalMs": 2000,
162
+ "sampleMaxAgeMs": 10000,
163
+ "installTemporaryBytes": 1073741824,
164
+ "dependenciesBytes": 4294967296,
165
+ "backupBytes": 1073741824
166
+ },
167
+ "schema": {
168
+ "minimum": 1,
169
+ "maximum": 1,
170
+ "migrationsSha256": "ba029fdbffa5320e61393b484d08accafce60e9aa8dacde5b6212fb79dd9146b",
171
+ "files": {
172
+ "001_init.sql": "b42b6192d872f0f65cfbbf9d0c7fb442d3aa49584a6cf321d3bcc641b31b70da",
173
+ "002_contact_decision.sql": "af82cb9975a09fa09c0f970b70d04a7b55a960d72e0853f4573d571efaa46f0a",
174
+ "003_schedules.sql": "2937f7be2693ce9b410ee9a73bd10c5e819c5081a10a6b3993c33f37ad42295f",
175
+ "004_condition_state.sql": "f081d250dd525ad57a450bd6bca59a75f8fb1428177d670ae9fa142ed79721d6",
176
+ "005_execution_handoff.sql": "ebf2e5c4890b7a73ae1ffb57a70bb15183f48a6c1fa2acf3a48b95ccb3d992b3",
177
+ "006_entity_confirmation.sql": "d25ee376e7f6dd55df2edeb68d7591ef5fbaa34eeb3eef662cd76e183526d3c0",
178
+ "007_research_tool_calls.sql": "df9c7d8d25b44b151f743e2d5167b34ae89c64e67423d8af554364fd792da942",
179
+ "008_followup_notifications.sql": "98865d811879afd679846087a5f01ee2db371dcb90381a9c400c940a5c1c4b37",
180
+ "008_investment.sql": "71cecf6215552c725797059efdcb501247e1225c7d9f662de606266d3a947e61",
181
+ "009_notification_channels.sql": "572424d739203a8514ae59920a0ad69cf83f278479b48d48becb88bddda25d9a",
182
+ "010_strategies.sql": "af023fab201db6bd2e15e2348125d112bc21cf3a59d8afd0a2d616fc05cee209",
183
+ "011_strategy_run_subjects.sql": "53a1c216ed775a06549dcc201f9494ceabe308bee6dc52a214b685cca2f2fe8d",
184
+ "012_strategy_schedule.sql": "ccddd81bb70d2c86356e6f1ad09132e7397837d17e926b515c8e281a362bdd58",
185
+ "014_industry_chain.sql": "b656efffe746d14ca350ea1c9ecfcce2d98b5753e4b2f862c75d0456a062fd4a",
186
+ "015_source_heads.sql": "652bf637892edbbe969ce5ed4ce45a4a0c43d03de395fce1b828fc4c0169f885",
187
+ "016_legacy_transfer.sql": "b6e3c214b103487d741226b0f83525e0cacbb0c440bd1935d0e963dc9b9bfd07",
188
+ "017_unified_strategy_work.sql": "8d055ca58f3e73482fb16c630c733ae93197244b37e4509cc90c7dec3bf88d60",
189
+ "018_personalization.sql": "7f62a4eb74707fb071f56d6057b6d6517ed7648af23ebe290f3be15d260a103c",
190
+ "019_position_risk.sql": "3c461fa435b352127fd4ee4e7ed3de2c924561a3727dce1535d7d3a47c436ca9",
191
+ "020_notification_tests.sql": "3eb0d72eadf04b074208dbe0ef6a72ee614ec11a15143108a2471568b7b4fb22",
192
+ "021_notification_priority.sql": "f742b4c4b796e918bfc6a08bac8c5ae206382075925acf7b1c430b033f5177e6",
193
+ "022_research_job_priority.sql": "fcb0c0af025806faab7cd403097a9d2c55e07331ab0a78b2926c1927a87f4ed4",
194
+ "023_adaptive_notifications.sql": "80630e2dad73844d48b265fbc33648b01d8321a6fd9a0587ef4dc01f362aadec",
195
+ "023_notification_attention.sql": "5dd85fb001192558211c7b69e3452984c52119f215750ed6283b296d3dccc842",
196
+ "024_t_trade_watch.sql": "aa38470ddab00e0cbca07ce6d5cc31399ba2b6e40c8d8d888e88ec093a9f89ef"
197
+ }
198
+ },
199
+ "assetPackage": {
200
+ "name": "@pab-iprg/huohuo-assets-linux-x64",
201
+ "version": "1.0.0-preview.5"
202
+ }
93
203
  }
94
- }
204
+ ]
95
205
  }