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

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,24 @@ 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
+ }
428
+ function darwinAvailableMemory(processAvailable, effective, pressure, vmstat, constrained = false) {
429
+ if (processAvailable === null || !Number.isSafeInteger(processAvailable) || processAvailable <= 0) return null;
430
+ if (pressure !== "normal" || constrained || effective === null || !Number.isSafeInteger(effective) || effective <= 0) return processAvailable;
431
+ const pageSize = Number(/page size of (\d+) bytes/.exec(vmstat)?.[1]);
432
+ const counts = ["Pages free", "Pages purgeable", "Pages speculative", "File-backed pages"].map((label) => Number(new RegExp(`^${label}:\\s+(\\d+)\\.\\s*$`, "m").exec(vmstat)?.[1]));
433
+ if (![pageSize, ...counts].every(Number.isSafeInteger) || pageSize <= 0 || counts.some((n) => n < 0)) return processAvailable;
434
+ const [free, purgeable, speculative, fileBacked] = counts;
435
+ const estimate = pageSize * (free + Math.max(purgeable, Math.max(0, fileBacked - speculative)));
436
+ if (!Number.isSafeInteger(estimate) || estimate < 0 || estimate > effective) return processAvailable;
437
+ return Math.min(effective, Math.max(processAvailable, estimate));
438
+ }
421
439
  async function sampleResources(paths) {
422
440
  const limit = constrainedMemory();
423
441
  let effectiveMemoryBytes = Math.min(totalmem(), limit || Infinity);
@@ -438,28 +456,14 @@ async function sampleResources(paths) {
438
456
  if (pressure === "normal" && availableMemoryBytes !== null && effectiveMemoryBytes !== null) {
439
457
  try {
440
458
  const { stdout } = await execute2("/usr/bin/vm_stat", [], { timeout: 2e3 });
441
- const pageBytes = Number(/page size of (\d+) bytes/.exec(stdout)?.[1]);
442
- const pageCounts = ["free", "purgeable"].map((kind) => Number(new RegExp(`^Pages ${kind}:\\s+(\\d+)\\.`, "m").exec(stdout)?.[1]));
443
- const reclaimable = pageBytes * pageCounts.reduce((sum, pages) => sum + pages, 0);
444
- if (Number.isSafeInteger(reclaimable) && reclaimable > 0) {
445
- availableMemoryBytes = Math.min(effectiveMemoryBytes, Math.max(availableMemoryBytes, reclaimable));
446
- }
459
+ availableMemoryBytes = darwinAvailableMemory(availableMemoryBytes, effectiveMemoryBytes, pressure, stdout, limit > 0);
447
460
  } catch {
448
461
  }
449
462
  }
450
463
  } else if (process.platform === "linux") {
451
464
  try {
452
465
  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
- }
466
+ availableMemoryBytes = linuxAvailableMemory(availableMemoryBytes, info);
463
467
  const psi = await readFile3("/proc/pressure/memory", "utf8");
464
468
  const full = /^full avg10=(\d+(?:\.\d+)?)/m.exec(psi);
465
469
  if (full) pressure = Number(full[1]) > 0 ? "high" : "normal";
@@ -548,6 +552,50 @@ function npmCommand() {
548
552
  return { command: process.env.ComSpec ?? "cmd.exe", prefix: ["/d", "/s", "/c", "npm"] };
549
553
  }
550
554
 
555
+ // distribution/bootstrap/catalog.ts
556
+ function hostPlatform() {
557
+ const host = { os: process.platform, arch: process.arch };
558
+ if (host.os === "linux") {
559
+ const report = process.report?.getReport();
560
+ host.libc = report?.header?.glibcVersionRuntime ? "glibc" : "unknown";
561
+ }
562
+ return host;
563
+ }
564
+ function validateCatalog(input) {
565
+ const catalog = input;
566
+ if (!catalog || catalog.kind !== "huohuo-release-catalog" || catalog.format !== 1 || !Array.isArray(catalog.releases) || !catalog.releases.length || catalog.releases.length > 6) {
567
+ throw new Error("\u5E73\u53F0\u53D1\u884C\u7D22\u5F15\u65E0\u6548\uFF1B\u8BF7\u4F7F\u7528\u5B8C\u6574\u7684\u706B\u706B\u5B89\u88C5\u5305");
568
+ }
569
+ const platforms = /* @__PURE__ */ new Set();
570
+ for (const candidate of catalog.releases) {
571
+ const release = validateRelease(candidate);
572
+ const platform = `${release.platform.os}-${release.platform.arch}`;
573
+ 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");
574
+ 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");
575
+ platforms.add(platform);
576
+ }
577
+ return catalog;
578
+ }
579
+ function validateReleaseSource(input) {
580
+ if (input && typeof input === "object" && ("kind" in input || "releases" in input)) return validateCatalog(input);
581
+ return validateRelease(input);
582
+ }
583
+ function selectRelease(input, host = hostPlatform()) {
584
+ const source = validateReleaseSource(input);
585
+ const releases = "releases" in source ? source.releases : [source];
586
+ const selected = releases.find((release) => release.platform.os === host.os && release.platform.arch === host.arch);
587
+ if (!selected) {
588
+ 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`);
589
+ }
590
+ assertLibcCompatible(selected, host);
591
+ return selected;
592
+ }
593
+ function assertLibcCompatible(release, host = hostPlatform()) {
594
+ if (release.platform.libc && host.libc !== release.platform.libc) {
595
+ 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");
596
+ }
597
+ }
598
+
551
599
  // distribution/bootstrap/install.ts
552
600
  var digest = (value) => createHash2("sha256").update(JSON.stringify(value)).digest("hex");
553
601
  async function ownedDirectory(path, owner) {
@@ -622,13 +670,20 @@ function installationPaths(home, release) {
622
670
  };
623
671
  }
624
672
  function versionAtLeast(actual, minimum) {
673
+ if (![actual, minimum].every((value) => /^\d+(?:\.\d+)*$/.test(value))) return false;
625
674
  const a = actual.split(".").map(Number), b = minimum.split(".").map(Number);
626
- if ([...a, ...b].some((n) => !Number.isInteger(n))) return false;
675
+ if ([...a, ...b].some((n) => !Number.isSafeInteger(n))) return false;
627
676
  for (let i = 0; i < Math.max(a.length, b.length); i++) {
628
677
  if ((a[i] ?? 0) !== (b[i] ?? 0)) return (a[i] ?? 0) > (b[i] ?? 0);
629
678
  }
630
679
  return true;
631
680
  }
681
+ function osReleaseAtLeast(actual, minimum, platform = process.platform) {
682
+ if (platform !== "linux") return versionAtLeast(actual, minimum);
683
+ const numericRelease = (value) => /^(\d+(?:\.\d+)*)(?:-[A-Za-z0-9][A-Za-z0-9.+_-]*)?$/.exec(value)?.[1];
684
+ const a = numericRelease(actual), b = numericRelease(minimum);
685
+ return a !== void 0 && b !== void 0 && versionAtLeast(a, b);
686
+ }
632
687
  async function install(home, candidate, hooks = {}, programsOnly = false) {
633
688
  home = resolve3(home);
634
689
  await mkdir3(home, { recursive: true, mode: 448 });
@@ -675,7 +730,8 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
675
730
  }
676
731
  }
677
732
  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) {
733
+ assertLibcCompatible(release);
734
+ 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
735
  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
736
  }
681
737
  record ??= { format: 1, release, releaseDigest: digest(release), home, status: "installing", stage: "preflight", stages: {} };
@@ -1175,8 +1231,19 @@ async function uninstall(home, hooks = {}) {
1175
1231
 
1176
1232
  // distribution/bootstrap/install-ui.ts
1177
1233
  import { createInterface } from "node:readline";
1234
+ import { homedir } from "node:os";
1235
+ import { relative, sep as sep3 } from "node:path";
1178
1236
  var gb = (bytes) => bytes === null ? "\u672A\u77E5" : `${(bytes / 1e9).toFixed(2)} GB`;
1179
1237
  var mb = (bytes) => bytes < 1e5 ? `${(bytes / 1e3).toFixed(1)} KB` : `${(bytes / 1e6).toFixed(1)} MB`;
1238
+ var memory = (bytes) => {
1239
+ if (bytes === null) return "\u672A\u77E5";
1240
+ return bytes < 2 ** 30 ? `${Math.floor(bytes / 2 ** 20)} MiB` : `${Number((bytes / 2 ** 30).toFixed(2))} GiB`;
1241
+ };
1242
+ function displayPath(path) {
1243
+ const local = relative(homedir(), path);
1244
+ if (!local) return "~";
1245
+ return local === ".." || local.startsWith(`..${sep3}`) ? path : `~${sep3}${local}`;
1246
+ }
1180
1247
  function installUI(assumeYes) {
1181
1248
  const interactive = process.stdin.isTTY && process.stderr.isTTY;
1182
1249
  const output = process.stderr;
@@ -1192,24 +1259,46 @@ function installUI(assumeYes) {
1192
1259
  clear();
1193
1260
  output.write(text + "\n");
1194
1261
  };
1262
+ const heading = (text) => line(interactive && process.env.TERM !== "dumb" && process.env.NO_COLOR === void 0 ? `\x1B[1m${text}\x1B[22m` : text);
1195
1263
  return {
1196
1264
  async confirm(plan) {
1197
1265
  const { sample, policy, disks } = plan.resources;
1198
- line(`\u5B89\u88C5\u76EE\u5F55 ${plan.home}`);
1266
+ const narrow = interactive && output.columns > 0 && output.columns < 48;
1267
+ line("");
1268
+ heading("\u706B\u706B\u5B89\u88C5 \xB7 \u786E\u8BA4\u8BA1\u5212");
1269
+ line(` \u5B89\u88C5\u5230 ${displayPath(plan.home)}`);
1270
+ line("");
1271
+ heading("\u672C\u673A\u8D44\u6E90");
1199
1272
  for (const [index, disk] of disks.entries()) {
1200
1273
  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)}`);
1274
+ if (disks.length > 1) line(` \u78C1\u76D8 ${index + 1} ${displayPath(location)}`);
1275
+ const need = `\u9700\u7559 ${gb(disk.requiredBytes)}`;
1276
+ const free = `\u53EF\u7528 ${gb(disk.availableBytes)}`;
1277
+ line(narrow ? ` \u78C1\u76D8 ${need}
1278
+ ${free}` : ` \u78C1\u76D8 ${need} \xB7 ${free}`);
1202
1279
  }
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");
1280
+ const available = `\u53EF\u7528 ${memory(sample.availableMemoryBytes)}`;
1281
+ const total = `\u603B\u91CF ${memory(sample.effectiveMemoryBytes)}`;
1282
+ line(narrow ? ` \u5185\u5B58 ${available}
1283
+ ${total}` : ` \u5185\u5B58 ${available} \xB7 ${total}`);
1284
+ line(` \u542F\u52A8\u9700\u7559 ${memory(policy.startupHeadroomBytes)}`);
1285
+ line(` \u6700\u4F4E\u5BB9\u91CF ${memory(policy.minimumEffectiveMemoryBytes)}`);
1286
+ if (policy.observedInstallRssBytes) line(` \u5386\u53F2\u5B89\u88C5\u5CF0\u503C\u7EA6 ${memory(policy.observedInstallRssBytes)}`);
1287
+ for (const reason of plan.resources.reasons) line(` \u63D0\u793A ${reason}`);
1288
+ line("");
1289
+ heading("\u4E0B\u8F7D\u8BA1\u5212");
1290
+ const bytes = plan.downloads.reduce((total2, file) => total2 + file.size, 0);
1291
+ 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");
1292
+ const local = [plan.bundled ? `\u968F\u5305 ${plan.bundled} \u9879` : "", plan.cached ? `\u590D\u7528\u7F13\u5B58 ${plan.cached} \u9879` : ""].filter(Boolean);
1293
+ if (local.length) line(` ${local.join(" \xB7 ")}`);
1294
+ for (const file of plan.downloads) {
1295
+ line(` ${file.name} ${mb(file.size)}`);
1296
+ line(` \u6765\u6E90 ${file.host}`);
1297
+ }
1298
+ if (plan.downloads.length || plan.bundled) line(` \u4FDD\u5B58\u5230 ${displayPath(plan.home)}/cache`);
1299
+ if (plan.upstreamWheelCount !== void 0) line(` Python \u4E0A\u6E38\u6587\u4EF6 ${plan.upstreamWheelCount} \u9879`);
1300
+ line(" npm \u548C Python \u4F9D\u8D56\u4ECD\u53EF\u80FD\u8054\u7F51");
1301
+ line("");
1213
1302
  if (assumeYes) return true;
1214
1303
  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
1304
  const terminal = createInterface({ input: process.stdin, output, terminal: true });
@@ -1333,6 +1422,27 @@ async function showStatus(home, watch) {
1333
1422
  }
1334
1423
  }
1335
1424
 
1425
+ // distribution/bootstrap/assets.ts
1426
+ import { createRequire } from "node:module";
1427
+ import { readFile as readFile8 } from "node:fs/promises";
1428
+ import { dirname as dirname5, join as join7 } from "node:path";
1429
+ import { fileURLToPath } from "node:url";
1430
+ async function assetsDirectory(release, entryURL = import.meta.url) {
1431
+ if (!release?.assetPackage) return join7(dirname5(fileURLToPath(entryURL)), "assets");
1432
+ const expected = release.assetPackage;
1433
+ let file;
1434
+ try {
1435
+ file = createRequire(entryURL).resolve(`${expected.name}/package.json`);
1436
+ } catch {
1437
+ 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");
1438
+ }
1439
+ const manifest = JSON.parse(await readFile8(file, "utf8"));
1440
+ if (manifest.name !== expected.name || manifest.version !== expected.version) {
1441
+ throw new Error("\u706B\u706B\u5165\u53E3\u4E0E\u5E73\u53F0\u8D44\u6E90\u5305\u7248\u672C\u4E0D\u4E00\u81F4\u3002\u8BF7\u91CD\u65B0\u8FD0\u884C\u540C\u4E00\u7248\u672C\u7684\u5B8C\u6574\u5B89\u88C5\u547D\u4EE4\u3002");
1442
+ }
1443
+ return join7(dirname5(file), "assets");
1444
+ }
1445
+
1336
1446
  // distribution/bootstrap/entry.ts
1337
1447
  function artifactSource(source, directory) {
1338
1448
  return async (url, options) => {
@@ -1341,8 +1451,8 @@ function artifactSource(source, directory) {
1341
1451
  const artifact2 = source && [source.runtime, source.python, source.pythonDependencies].find((item) => item.url === String(url));
1342
1452
  const sha256 = bundled?.[1] ?? artifact2?.sha256;
1343
1453
  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");
1454
+ const root = directory ?? await assetsDirectory(source, import.meta.url);
1455
+ const path = join8(root, sha256 + ".tgz");
1346
1456
  const file = await open3(path).catch((error) => {
1347
1457
  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
1458
  throw error;
@@ -1351,7 +1461,7 @@ function artifactSource(source, directory) {
1351
1461
  };
1352
1462
  }
1353
1463
  async function main(args = process.argv.slice(2)) {
1354
- let home = join7(homedir(), ".huohuo"), noOpen = false, assumeYes = false, watch = false, command = "start";
1464
+ let home = join8(homedir2(), ".huohuo"), noOpen = false, assumeYes = false, watch = false, command = "start";
1355
1465
  let releaseFile, artifactDirectory;
1356
1466
  for (let i = 0; i < args.length; i++) {
1357
1467
  if (args[i] === "--home") {
@@ -1366,8 +1476,8 @@ async function main(args = process.argv.slice(2)) {
1366
1476
  else if (args[i] === "--yes") assumeYes = true;
1367
1477
  else if (args[i] === "--watch") watch = true;
1368
1478
  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"));
1479
+ const packageFile = join8(dirname6(fileURLToPath2(import.meta.url)), "package.json");
1480
+ const manifest = JSON.parse(await readFile9(packageFile, "utf8"));
1371
1481
  console.log(`${manifest.name} ${manifest.version}${manifest.private ? "\uFF08\u672C\u5730\u5019\u9009\uFF09" : ""}`);
1372
1482
  return;
1373
1483
  } else if (["start", "status", "doctor", "stop", "upgrade", "uninstall"].includes(args[i])) command = args[i];
@@ -1398,11 +1508,11 @@ async function main(args = process.argv.slice(2)) {
1398
1508
  };
1399
1509
  const ui = installUI(assumeYes);
1400
1510
  if (command === "upgrade") {
1401
- const candidate = JSON.parse(await readFile8(releaseFile ?? join7(dirname5(fileURLToPath(import.meta.url)), "release.json"), "utf8"));
1511
+ const candidate = selectRelease(JSON.parse(await readFile9(releaseFile ?? join8(dirname6(fileURLToPath2(import.meta.url)), "release.json"), "utf8")));
1402
1512
  await upgrade(home, candidate, { fetcher: artifactSource(candidate, artifactDirectory), progress, ...ui });
1403
1513
  const current = await readInstallation(home);
1404
1514
  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);
1515
+ await run(process.execPath, [join8(installationPaths(home, current.release).runtime, "lib/distribution/managed.js"), "start", home, ...noOpen ? ["--no-open"] : []], home);
1406
1516
  return;
1407
1517
  }
1408
1518
  if (command === "uninstall") {
@@ -1420,7 +1530,7 @@ async function main(args = process.argv.slice(2)) {
1420
1530
  return;
1421
1531
  }
1422
1532
  const paths = installationPaths(home, record.release);
1423
- const targets = [home, paths.directory, paths.environment, join7(home, "instances/default")];
1533
+ const targets = [home, paths.directory, paths.environment, join8(home, "instances/default")];
1424
1534
  const resources = evaluateResources(await sampleResources(targets), record.release.budget, targets.map((path) => ({ path, bytes: 0 })), "start");
1425
1535
  console.log(JSON.stringify({
1426
1536
  home,
@@ -1441,30 +1551,31 @@ async function main(args = process.argv.slice(2)) {
1441
1551
  }
1442
1552
  const runtime = installationPaths(home, record.release).runtime;
1443
1553
  try {
1444
- await readFile8(join7(runtime, "lib/distribution/managed.js"));
1554
+ await readFile9(join8(runtime, "lib/distribution/managed.js"));
1445
1555
  } catch (e) {
1446
1556
  if (e.code !== "ENOENT") throw e;
1447
1557
  record = void 0;
1448
1558
  }
1449
1559
  }
1450
1560
  if (!record || record.status !== "ready") {
1451
- let catalog = null;
1452
- if (!record || ["preflight", "download"].includes(record.stage)) {
1561
+ const persisted = await readInstallation(home);
1562
+ let source = persisted?.release;
1563
+ if (!source) {
1564
+ let catalog = null;
1453
1565
  try {
1454
- catalog = JSON.parse(await readFile8(releaseFile ?? join7(dirname5(fileURLToPath(import.meta.url)), "release.json"), "utf8"));
1566
+ catalog = JSON.parse(await readFile9(releaseFile ?? join8(dirname6(fileURLToPath2(import.meta.url)), "release.json"), "utf8"));
1455
1567
  } catch (e) {
1456
1568
  if (e.code !== "ENOENT") throw e;
1457
1569
  }
1570
+ source = selectRelease(catalog);
1458
1571
  }
1459
- const source = (await readInstallation(home))?.release ?? catalog;
1460
1572
  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 });
1573
+ record = await install(home, source, { fetcher, progress, ...ui });
1463
1574
  }
1464
1575
  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);
1576
+ await run(process.execPath, [join8(installationPaths(installedHome, record.release).runtime, "lib/distribution/managed.js"), "start", installedHome, ...noOpen ? ["--no-open"] : []], installedHome);
1466
1577
  }
1467
- if (process.argv[1] && await realpath5(resolve5(process.argv[1])).catch(() => "") === await realpath5(fileURLToPath(import.meta.url))) {
1578
+ if (process.argv[1] && await realpath5(resolve5(process.argv[1])).catch(() => "") === await realpath5(fileURLToPath2(import.meta.url))) {
1468
1579
  main().catch((error) => {
1469
1580
  console.error(error instanceof Error ? error.message : "\u5B89\u88C5\u6216\u542F\u52A8\u5931\u8D25");
1470
1581
  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.6",
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.6",
19
+ "@pab-iprg/huohuo-assets-linux-x64": "1.0.0-preview.6"
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/3d87538021deec2fa15adb57558f3abf453a405879b947adecf4253dd0ec5f28.tgz",
22
+ "bundled": true,
23
+ "sha256": "3d87538021deec2fa15adb57558f3abf453a405879b947adecf4253dd0ec5f28",
24
+ "integrity": "sha512-czI1E1L+ILNCh+tf/CdX1NXIBwETHnJDqXixNppcdzbdrsKXRJ6XkUKJbZK/AX1q4m6gdfETgqb8XXuVK9oKnQ==",
25
+ "downloadBytes": 2610631,
26
+ "expandedBytes": 8328828
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": "fb3540e0d604f26280128a21f97313575848f6348733a221f7e230233fdb8e2d",
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.6"
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/2c2e63ab7a4890ef198f2f0e3b3152788a158fabeb1828a20e9820416eb6d01a.tgz",
122
+ "bundled": true,
123
+ "sha256": "2c2e63ab7a4890ef198f2f0e3b3152788a158fabeb1828a20e9820416eb6d01a",
124
+ "integrity": "sha512-JkcJGHcTpa2Db/YkeUuAzzyx0B118eP5CKBEhf7oQiGBm7uY5bbndj4q1IaeNJMhJlJRYD1YAm/qxPk1tI/5aw==",
125
+ "downloadBytes": 2610643,
126
+ "expandedBytes": 8328846
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": "fb3540e0d604f26280128a21f97313575848f6348733a221f7e230233fdb8e2d",
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.6"
202
+ }
93
203
  }
94
- }
204
+ ]
95
205
  }