@pab-iprg/huohuo 1.0.0-preview.3 → 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,12 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 火火轻量入口(本地开发候选)
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
7
|
+
运行 npx 前仍需安装兼容的 Node/npm(当前入口要求 Node 24.8+,具体 Node 主版本由所选发行固定)。运行环境在用户目录中准备,不替换系统 Python;已校验的下载缓存会复用。Python 和 wheels 在构建端选定版本及 SHA;npm 预览包携带下载清单和构建好的 jsonpath wheel,其余 wheel 按固定上游 URL 下载并校验,不在用户机器编译。仍兼容旧的完整离线 wheel 包;npm 依赖按运行包锁文件安装。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
当前开发入口:`huohuo [start|status|doctor|stop|upgrade|uninstall] [--home 目录] [--no-open]`。`status --watch` 持续显示进度;安装前展示资源计划,`--yes` 可显式确认。默认持久目录 `~/.huohuo`;普通重开保留已安装版本,安装中断继续原目标。npx 缓存保存入口和所选平台资源;再次解析最新入口可能联网。
|
|
10
10
|
|
|
11
11
|
显式升级检查旧迁移不可变、追加迁移顺序、执行库格式与 DSH 会话兼容范围,先一致性备份再迁移和验证,成功后切换;不自动恢复旧备份覆盖新数据。默认卸载保留实例、凭据、会话和备份,不提供直接彻底删除业务数据的快捷入口。
|
|
12
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
|
|
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
|
|
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";
|
|
@@ -132,20 +132,24 @@ var hash = /^[a-f0-9]{64}$/;
|
|
|
132
132
|
function integer(n) {
|
|
133
133
|
return Number.isSafeInteger(n) && Number(n) >= 0;
|
|
134
134
|
}
|
|
135
|
-
function
|
|
136
|
-
if (
|
|
137
|
-
if (a.redirectHosts !== void 0 && (!Array.isArray(a.redirectHosts) || a.redirectHosts.length > 4 || !a.redirectHosts.every((s) => typeof s === "string" && /^[a-z0-9.-]+$/.test(s)))) return false;
|
|
138
|
-
if (a.bundled !== void 0) return a.bundled === true && a.url === `https://bundled.invalid/${a.sha256}.tgz` && a.redirectHosts === void 0;
|
|
135
|
+
function httpsURL(value) {
|
|
136
|
+
if (typeof value !== "string") return false;
|
|
139
137
|
try {
|
|
140
|
-
const u = new URL(
|
|
138
|
+
const u = new URL(value);
|
|
141
139
|
return u.protocol === "https:" && !u.username && !u.password && !u.hash;
|
|
142
140
|
} catch {
|
|
143
141
|
return false;
|
|
144
142
|
}
|
|
145
143
|
}
|
|
144
|
+
function artifact(a) {
|
|
145
|
+
if (!a || !hash.test(a.sha256) || !integer(a.downloadBytes) || !integer(a.expandedBytes) || !a.downloadBytes || !a.expandedBytes) return false;
|
|
146
|
+
if (a.redirectHosts !== void 0 && (!Array.isArray(a.redirectHosts) || a.redirectHosts.length > 4 || !a.redirectHosts.every((s) => typeof s === "string" && /^[a-z0-9.-]+$/.test(s)))) return false;
|
|
147
|
+
if (a.bundled !== void 0) return a.bundled === true && a.url === `https://bundled.invalid/${a.sha256}.tgz` && a.redirectHosts === void 0 && a.mirrors === void 0;
|
|
148
|
+
return httpsURL(a.url) && (a.mirrors === void 0 || Array.isArray(a.mirrors) && a.mirrors.length <= 3 && a.mirrors.every(httpsURL));
|
|
149
|
+
}
|
|
146
150
|
function validateRelease(input) {
|
|
147
151
|
const r = input;
|
|
148
|
-
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.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 || ![
|
|
149
153
|
"minimumEffectiveMemoryBytes",
|
|
150
154
|
"startupHeadroomBytes",
|
|
151
155
|
"heavyJobHeadroomBytes",
|
|
@@ -157,7 +161,7 @@ function validateRelease(input) {
|
|
|
157
161
|
"processTreeBudgetBytes",
|
|
158
162
|
"sampleIntervalMs",
|
|
159
163
|
"sampleMaxAgeMs"
|
|
160
|
-
].every((k) => integer(r.budget[k])) || r.budget.startupHeadroomBytes <= 0 || r.budget.minimumEffectiveMemoryBytes <= 0 || r.budget.diskReserveBytes <= 0 || r.budget.recoverySamples < 2 || r.budget.heavyJobHeadroomBytes < r.budget.startupHeadroomBytes || r.budget.processTreeBudgetBytes <= 0 || r.budget.sampleIntervalMs < 1e3 || r.budget.sampleMaxAgeMs < r.budget.sampleIntervalMs * 2 || r.budget.sampleMaxAgeMs > 3e4 || !r.schema || !integer(r.schema.minimum) || !integer(r.schema.maximum) || r.schema.minimum > r.schema.maximum) {
|
|
164
|
+
].every((k) => integer(r.budget[k])) || r.budget.observedInstallRssBytes !== void 0 && (!integer(r.budget.observedInstallRssBytes) || r.budget.observedInstallRssBytes <= 0) || r.budget.startupHeadroomBytes <= 0 || r.budget.minimumEffectiveMemoryBytes <= 0 || r.budget.diskReserveBytes <= 0 || r.budget.recoverySamples < 2 || r.budget.heavyJobHeadroomBytes < r.budget.startupHeadroomBytes || r.budget.processTreeBudgetBytes <= 0 || r.budget.sampleIntervalMs < 1e3 || r.budget.sampleMaxAgeMs < r.budget.sampleIntervalMs * 2 || r.budget.sampleMaxAgeMs > 3e4 || !r.schema || !integer(r.schema.minimum) || !integer(r.schema.maximum) || r.schema.minimum > r.schema.maximum) {
|
|
161
165
|
throw new Error("\u53D1\u884C\u6E05\u5355\u65E0\u6548\u6216\u5C1A\u672A\u5B8C\u6210\u5E73\u53F0\u3001\u6458\u8981\u4E0E\u8D44\u6E90\u6D4B\u91CF\uFF1B\u4E0D\u80FD\u5B89\u88C5");
|
|
162
166
|
}
|
|
163
167
|
if (!hash.test(r.schema.migrationsSha256)) throw new Error("\u7F3A\u5C11\u6570\u636E\u5E93\u8FC1\u79FB\u88C5\u914D\u6458\u8981");
|
|
@@ -181,45 +185,102 @@ async function verifyArtifact(path, artifact2) {
|
|
|
181
185
|
}
|
|
182
186
|
if (bytes !== artifact2.downloadBytes || digest2.digest("hex") !== artifact2.sha256 || "integrity" in artifact2 && artifact2.integrity !== `sha512-${integrity.digest("base64")}`) throw new Error("\u4E0B\u8F7D\u4EA7\u7269\u5927\u5C0F\u6216\u6458\u8981\u4E0D\u7B26");
|
|
183
187
|
}
|
|
184
|
-
|
|
188
|
+
var RetryableDownload = class extends Error {
|
|
189
|
+
};
|
|
190
|
+
async function downloadArtifact(artifact2, path, fetcher = fetch, progress) {
|
|
185
191
|
try {
|
|
186
192
|
await verifyArtifact(path, artifact2);
|
|
193
|
+
progress?.({ status: "cached", bytes: artifact2.downloadBytes });
|
|
187
194
|
return;
|
|
188
195
|
} catch (e) {
|
|
189
196
|
if (e.code !== "ENOENT") throw e;
|
|
190
197
|
}
|
|
191
198
|
await mkdir2(dirname2(path), { recursive: true, mode: 448 });
|
|
199
|
+
const sources = [.../* @__PURE__ */ new Set([artifact2.url, ...artifact2.mirrors ?? []])];
|
|
200
|
+
if (sources.length > 1) {
|
|
201
|
+
progress?.({ status: "probing", bytes: 0 });
|
|
202
|
+
const results = await Promise.all(sources.map(async (source) => {
|
|
203
|
+
const started = performance.now();
|
|
204
|
+
try {
|
|
205
|
+
const response = await requestSource(artifact2, source, fetcher, AbortSignal.timeout(5e3), "HEAD");
|
|
206
|
+
await response.body?.cancel();
|
|
207
|
+
return { source, latency: response.ok ? performance.now() - started : Infinity };
|
|
208
|
+
} catch {
|
|
209
|
+
return { source, latency: Infinity };
|
|
210
|
+
}
|
|
211
|
+
}));
|
|
212
|
+
results.sort((a, b) => a.latency - b.latency);
|
|
213
|
+
sources.splice(0, sources.length, ...results.map((result) => result.source));
|
|
214
|
+
} else if (!artifact2.bundled) sources.push(artifact2.url);
|
|
215
|
+
for (const [index, source] of sources.entries()) {
|
|
216
|
+
const host = artifact2.bundled ? void 0 : new URL(source).hostname;
|
|
217
|
+
progress?.({ status: "start", bytes: 0, host });
|
|
218
|
+
try {
|
|
219
|
+
await transfer(artifact2, path, source, fetcher, (bytes) => progress?.({ status: "receiving", bytes, host }));
|
|
220
|
+
progress?.({ status: "verified", bytes: artifact2.downloadBytes, host });
|
|
221
|
+
return;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
if (!(error instanceof RetryableDownload)) throw error;
|
|
224
|
+
if (index === sources.length - 1) throw new Error(`${host} \u4E0B\u8F7D\u672A\u5B8C\u6210\uFF08${error.message}\uFF09\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8DD1\uFF0C\u5DF2\u6821\u9A8C\u7684\u6587\u4EF6\u4F1A\u590D\u7528\u3002`, { cause: error });
|
|
225
|
+
progress?.({ status: "retry", bytes: 0, host });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function requestSource(artifact2, source, fetcher, signal, method, onResponse) {
|
|
230
|
+
const allowed = /* @__PURE__ */ new Set([new URL(source).hostname, ...artifact2.redirectHosts ?? []]);
|
|
231
|
+
let url = source;
|
|
232
|
+
for (let redirects = 0; redirects <= 4; redirects++) {
|
|
233
|
+
const next = new URL(url);
|
|
234
|
+
if (next.protocol !== "https:" || next.username || next.password || !allowed.has(next.hostname)) throw new Error("\u4E0B\u8F7D\u91CD\u5B9A\u5411\u4E0D\u5728\u56FA\u5B9A\u53D1\u884C\u5141\u8BB8\u6765\u6E90\u5185");
|
|
235
|
+
const response = await fetcher(url, { signal, method, redirect: "manual" });
|
|
236
|
+
onResponse?.();
|
|
237
|
+
if (![301, 302, 303, 307, 308].includes(response.status)) return response;
|
|
238
|
+
const location = response.headers.get("location");
|
|
239
|
+
await response.body?.cancel();
|
|
240
|
+
if (!location || redirects === 4) throw new Error("\u4E0B\u8F7D\u91CD\u5B9A\u5411\u65E0\u6548\u6216\u8FC7\u591A");
|
|
241
|
+
url = new URL(location, url).href;
|
|
242
|
+
}
|
|
243
|
+
throw new Error("\u4E0B\u8F7D\u672A\u8FD4\u56DE\u54CD\u5E94");
|
|
244
|
+
}
|
|
245
|
+
async function transfer(artifact2, path, source, fetcher, onBytes) {
|
|
192
246
|
const pending = `${path}.${randomUUID2()}.partial`;
|
|
193
247
|
const handle = await open2(pending, "wx", 384);
|
|
248
|
+
const idle = new AbortController();
|
|
249
|
+
let timer;
|
|
250
|
+
const keepAlive = () => {
|
|
251
|
+
clearTimeout(timer);
|
|
252
|
+
timer = setTimeout(() => idle.abort(), 45e3);
|
|
253
|
+
timer.unref();
|
|
254
|
+
};
|
|
255
|
+
const signal = AbortSignal.any([idle.signal, AbortSignal.timeout(30 * 6e4)]);
|
|
256
|
+
keepAlive();
|
|
194
257
|
try {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
let response;
|
|
198
|
-
const transferMs = Math.ceil(artifact2.downloadBytes / (64 * 1024)) * 1e3;
|
|
199
|
-
const signal = AbortSignal.timeout(Math.min(9e5, Math.max(12e4, 3e4 + transferMs)));
|
|
200
|
-
for (let redirects = 0; redirects <= 4; redirects++) {
|
|
201
|
-
const next = new URL(url);
|
|
202
|
-
if (next.protocol !== "https:" || next.username || next.password || !allowed.has(next.hostname)) throw new Error("\u4E0B\u8F7D\u91CD\u5B9A\u5411\u4E0D\u5728\u56FA\u5B9A\u53D1\u884C\u5141\u8BB8\u6765\u6E90\u5185");
|
|
203
|
-
response = await fetcher(url, { signal, redirect: "manual" });
|
|
204
|
-
if (![301, 302, 303, 307, 308].includes(response.status)) break;
|
|
205
|
-
const location = response.headers.get("location");
|
|
258
|
+
const response = await requestSource(artifact2, source, fetcher, signal, "GET", keepAlive);
|
|
259
|
+
if (!response.ok || !response.body) {
|
|
206
260
|
await response.body?.cancel();
|
|
207
|
-
if (!
|
|
208
|
-
|
|
261
|
+
if (!artifact2.bundled && (response.status === 408 || response.status === 429 || response.status >= 500 || artifact2.mirrors?.length && [403, 404].includes(response.status))) {
|
|
262
|
+
throw new RetryableDownload(`HTTP ${response.status}`);
|
|
263
|
+
}
|
|
264
|
+
throw new Error(`\u4E0B\u8F7D\u5931\u8D25\uFF1AHTTP ${response.status}`);
|
|
209
265
|
}
|
|
210
|
-
if (!response) throw new Error("\u4E0B\u8F7D\u672A\u8FD4\u56DE\u54CD\u5E94");
|
|
211
|
-
if (!response.ok || !response.body) throw new Error(`\u4E0B\u8F7D\u5931\u8D25\uFF1AHTTP ${response.status}`);
|
|
212
266
|
let received = 0;
|
|
213
267
|
for await (const chunk of response.body) {
|
|
214
268
|
received += chunk.byteLength;
|
|
269
|
+
keepAlive();
|
|
215
270
|
if (received > artifact2.downloadBytes) throw new Error("\u4E0B\u8F7D\u8D85\u8FC7\u53D1\u884C\u6E05\u5355\u5927\u5C0F");
|
|
216
271
|
await handle.writeFile(chunk);
|
|
272
|
+
onBytes(received);
|
|
217
273
|
}
|
|
218
274
|
await handle.sync();
|
|
219
275
|
await handle.close();
|
|
220
276
|
await verifyArtifact(pending, artifact2);
|
|
221
277
|
await rename2(pending, path);
|
|
278
|
+
} catch (error) {
|
|
279
|
+
if (!artifact2.bundled && signal.aborted) throw new RetryableDownload(idle.signal.aborted ? "\u8FDE\u7EED 45 \u79D2\u672A\u6536\u5230\u6570\u636E" : "\u8D85\u8FC7\u5355\u6B21\u4E0B\u8F7D\u65F6\u9650", { cause: error });
|
|
280
|
+
if (!artifact2.bundled && error instanceof TypeError) throw new RetryableDownload("\u7F51\u7EDC\u8FDE\u63A5\u4E2D\u65AD", { cause: error });
|
|
281
|
+
throw error;
|
|
222
282
|
} finally {
|
|
283
|
+
clearTimeout(timer);
|
|
223
284
|
await handle.close().catch(() => {
|
|
224
285
|
});
|
|
225
286
|
await rm(pending, { force: true });
|
|
@@ -303,8 +364,8 @@ async function extractArchive(archive, root, expandedLimit) {
|
|
|
303
364
|
}
|
|
304
365
|
} else if (kind === "1" || kind === "2") {
|
|
305
366
|
if (size || !linkText || linkText.startsWith("/") || linkText.includes("\\")) throw new Error("\u538B\u7F29\u5305\u94FE\u63A5\u65E0\u6548");
|
|
306
|
-
const
|
|
307
|
-
links.push({ target, source: safePath(root,
|
|
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 });
|
|
308
369
|
} else throw new Error(`\u538B\u7F29\u5305\u6761\u76EE\u7C7B\u578B\u4E0D\u53D7\u652F\u6301\uFF1A${kind}`);
|
|
309
370
|
}
|
|
310
371
|
if (size % 512) await take(512 - size % 512);
|
|
@@ -357,6 +418,13 @@ async function existingParent(path) {
|
|
|
357
418
|
current = parent;
|
|
358
419
|
}
|
|
359
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
|
+
}
|
|
360
428
|
async function sampleResources(paths) {
|
|
361
429
|
const limit = constrainedMemory();
|
|
362
430
|
let effectiveMemoryBytes = Math.min(totalmem(), limit || Infinity);
|
|
@@ -389,16 +457,7 @@ async function sampleResources(paths) {
|
|
|
389
457
|
} else if (process.platform === "linux") {
|
|
390
458
|
try {
|
|
391
459
|
const info = await readFile3("/proc/meminfo", "utf8");
|
|
392
|
-
|
|
393
|
-
if (available) availableMemoryBytes = Math.min(availableMemoryBytes ?? Infinity, Number(available[1]) * 1024);
|
|
394
|
-
if (limit) {
|
|
395
|
-
try {
|
|
396
|
-
const current = Number((await readFile3("/sys/fs/cgroup/memory.current", "utf8")).trim());
|
|
397
|
-
if (Number.isFinite(current)) availableMemoryBytes = Math.min(availableMemoryBytes ?? Infinity, Math.max(0, limit - current));
|
|
398
|
-
} catch {
|
|
399
|
-
availableMemoryBytes = null;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
460
|
+
availableMemoryBytes = linuxAvailableMemory(availableMemoryBytes, info);
|
|
402
461
|
const psi = await readFile3("/proc/pressure/memory", "utf8");
|
|
403
462
|
const full = /^full avg10=(\d+(?:\.\d+)?)/m.exec(psi);
|
|
404
463
|
if (full) pressure = Number(full[1]) > 0 ? "high" : "normal";
|
|
@@ -487,6 +546,50 @@ function npmCommand() {
|
|
|
487
546
|
return { command: process.env.ComSpec ?? "cmd.exe", prefix: ["/d", "/s", "/c", "npm"] };
|
|
488
547
|
}
|
|
489
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
|
+
|
|
490
593
|
// distribution/bootstrap/install.ts
|
|
491
594
|
var digest = (value) => createHash2("sha256").update(JSON.stringify(value)).digest("hex");
|
|
492
595
|
async function ownedDirectory(path, owner) {
|
|
@@ -561,13 +664,20 @@ function installationPaths(home, release) {
|
|
|
561
664
|
};
|
|
562
665
|
}
|
|
563
666
|
function versionAtLeast(actual, minimum) {
|
|
667
|
+
if (![actual, minimum].every((value) => /^\d+(?:\.\d+)*$/.test(value))) return false;
|
|
564
668
|
const a = actual.split(".").map(Number), b = minimum.split(".").map(Number);
|
|
565
|
-
if ([...a, ...b].some((n) => !Number.
|
|
669
|
+
if ([...a, ...b].some((n) => !Number.isSafeInteger(n))) return false;
|
|
566
670
|
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
567
671
|
if ((a[i] ?? 0) !== (b[i] ?? 0)) return (a[i] ?? 0) > (b[i] ?? 0);
|
|
568
672
|
}
|
|
569
673
|
return true;
|
|
570
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
|
+
}
|
|
571
681
|
async function install(home, candidate, hooks = {}, programsOnly = false) {
|
|
572
682
|
home = resolve3(home);
|
|
573
683
|
await mkdir3(home, { recursive: true, mode: 448 });
|
|
@@ -614,7 +724,8 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
|
|
|
614
724
|
}
|
|
615
725
|
}
|
|
616
726
|
const release = record?.release ?? validateRelease(candidate);
|
|
617
|
-
|
|
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) {
|
|
618
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");
|
|
619
730
|
}
|
|
620
731
|
record ??= { format: 1, release, releaseDigest: digest(release), home, status: "installing", stage: "preflight", stages: {} };
|
|
@@ -622,6 +733,20 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
|
|
|
622
733
|
const paths = installationPaths(home, release);
|
|
623
734
|
const execute4 = hooks.run ?? run;
|
|
624
735
|
const sample = hooks.sample ?? sampleResources;
|
|
736
|
+
const files = [
|
|
737
|
+
{ name: "\u706B\u706B\u8FD0\u884C\u5305", artifact: release.runtime, path: paths.archive },
|
|
738
|
+
{ name: "Python \u8FD0\u884C\u73AF\u5883", artifact: release.python, path: paths.pythonArchive },
|
|
739
|
+
{ name: "Python \u4F9D\u8D56\u6E05\u5355\u6216\u79BB\u7EBF\u5305", artifact: release.pythonDependencies, path: join3(home, "cache", release.pythonDependencies.sha256 + ".tgz") }
|
|
740
|
+
];
|
|
741
|
+
const withActivity = async (label, action) => {
|
|
742
|
+
hooks.progress?.(label);
|
|
743
|
+
const timer = hooks.progress && setInterval(() => hooks.progress?.(`${label}\u4ECD\u5728\u8FDB\u884C\uFF0C\u8BF7\u7A0D\u5019`), 2e4);
|
|
744
|
+
try {
|
|
745
|
+
await action();
|
|
746
|
+
} finally {
|
|
747
|
+
if (timer) clearInterval(timer);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
625
750
|
const cached = async (path, artifact2) => {
|
|
626
751
|
try {
|
|
627
752
|
await verifyArtifact(path, artifact2);
|
|
@@ -656,15 +781,10 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
|
|
|
656
781
|
}
|
|
657
782
|
};
|
|
658
783
|
const check = async () => {
|
|
659
|
-
const [
|
|
660
|
-
cached(paths.archive, release.runtime),
|
|
661
|
-
cached(paths.pythonArchive, release.python),
|
|
662
|
-
cached(join3(home, "cache", release.pythonDependencies.sha256 + ".tgz"), release.pythonDependencies),
|
|
663
|
-
installed()
|
|
664
|
-
]);
|
|
784
|
+
const [cacheHits, inventory] = await Promise.all([Promise.all(files.map((file) => cached(file.path, file.artifact))), installed()]);
|
|
665
785
|
const remainingDependencies = inventory.dependencies ? 0 : release.budget.dependenciesBytes;
|
|
666
786
|
const costs = [
|
|
667
|
-
{ path: join3(home, "cache"), bytes: (
|
|
787
|
+
{ path: join3(home, "cache"), bytes: files.reduce((sum, file, index) => sum + (cacheHits[index] ? 0 : file.artifact.downloadBytes), 0) },
|
|
668
788
|
{ path: paths.directory, bytes: inventory.dependencies ? 0 : release.runtime.expandedBytes + release.budget.installTemporaryBytes },
|
|
669
789
|
{ path: paths.python, bytes: inventory.python ? 0 : release.python.expandedBytes },
|
|
670
790
|
{ path: paths.environment, bytes: 0 },
|
|
@@ -690,6 +810,19 @@ async function install(home, candidate, hooks = {}, programsOnly = false) {
|
|
|
690
810
|
${details}
|
|
691
811
|
\u5904\u7406\u540E\uFF0C\u91CD\u65B0\u8FD0\u884C\u521A\u624D\u7684\u5B89\u88C5\u547D\u4EE4\u3002`), { code: "RESOURCE_BLOCKED" });
|
|
692
812
|
}
|
|
813
|
+
const pendingFiles = files.filter((_, index) => !cacheHits[index]);
|
|
814
|
+
return {
|
|
815
|
+
home,
|
|
816
|
+
resources: result,
|
|
817
|
+
cached: files.length - pendingFiles.length,
|
|
818
|
+
bundled: pendingFiles.filter((file) => file.artifact.bundled).length,
|
|
819
|
+
upstreamWheelCount: inventory.dependencies ? void 0 : release.pythonDependencies.upstreamWheelCount,
|
|
820
|
+
downloads: pendingFiles.filter((file) => !file.artifact.bundled).map((file) => ({
|
|
821
|
+
name: file.name,
|
|
822
|
+
size: file.artifact.downloadBytes,
|
|
823
|
+
host: new URL(file.artifact.url).hostname
|
|
824
|
+
}))
|
|
825
|
+
};
|
|
693
826
|
};
|
|
694
827
|
const stage = async (name, action) => {
|
|
695
828
|
hooks.progress?.(name);
|
|
@@ -700,8 +833,8 @@ ${details}
|
|
|
700
833
|
record.stages[name] = entry;
|
|
701
834
|
await atomicJSON(recordPath, record);
|
|
702
835
|
try {
|
|
703
|
-
await check();
|
|
704
|
-
const output = await action();
|
|
836
|
+
const plan = await check();
|
|
837
|
+
const output = await action(plan);
|
|
705
838
|
entry.status = "verified";
|
|
706
839
|
entry.outputDigest = digest(output ?? null);
|
|
707
840
|
entry.endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -709,13 +842,13 @@ ${details}
|
|
|
709
842
|
entry.status = "failed";
|
|
710
843
|
entry.endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
711
844
|
entry.errorCode = typeof error.code === "string" && /^[A-Z0-9_]+$/.test(error.code) ? error.code : "STAGE_FAILED";
|
|
712
|
-
record.status = entry.errorCode === "RESOURCE_BLOCKED" ? "blocked" : "failed";
|
|
845
|
+
record.status = entry.errorCode === "RESOURCE_BLOCKED" || entry.errorCode === "INSTALL_CANCELLED" ? "blocked" : "failed";
|
|
713
846
|
await atomicJSON(recordPath, record);
|
|
714
847
|
throw error;
|
|
715
848
|
}
|
|
716
849
|
await atomicJSON(recordPath, record);
|
|
717
850
|
};
|
|
718
|
-
await stage("preflight", async () => {
|
|
851
|
+
await stage("preflight", async (plan) => {
|
|
719
852
|
const npm = npmCommand();
|
|
720
853
|
const { stdout } = await promisify3(execFile3)(npm.command, [...npm.prefix, "--version"], { timeout: 5e3 });
|
|
721
854
|
if (!/^\d+\.\d+\.\d+\s*$/.test(stdout) || Number(stdout.split(".")[0]) !== release.npmMajor) throw new Error("npm \u7248\u672C\u672A\u77E5\u6216\u4E0D\u5728\u6B64\u53D1\u884C\u5DF2\u9A8C\u8BC1\u8303\u56F4\u5185");
|
|
@@ -725,11 +858,25 @@ ${details}
|
|
|
725
858
|
} finally {
|
|
726
859
|
await rm2(probe, { force: true });
|
|
727
860
|
}
|
|
861
|
+
if (hooks.confirm && !await hooks.confirm(plan)) {
|
|
862
|
+
throw Object.assign(new Error("\u5DF2\u53D6\u6D88\u5B89\u88C5\uFF0C\u672C\u6B21\u6CA1\u6709\u8FDB\u5165\u4E0B\u8F7D\u3002"), { code: "INSTALL_CANCELLED" });
|
|
863
|
+
}
|
|
728
864
|
});
|
|
729
865
|
await stage("download", async () => {
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
866
|
+
for (const [index, file] of files.entries()) {
|
|
867
|
+
const progress = (event) => hooks.downloadProgress?.({
|
|
868
|
+
name: file.name,
|
|
869
|
+
index: index + 1,
|
|
870
|
+
total: files.length,
|
|
871
|
+
size: file.artifact.downloadBytes
|
|
872
|
+
}, event);
|
|
873
|
+
try {
|
|
874
|
+
await downloadArtifact(file.artifact, file.path, hooks.fetcher, progress);
|
|
875
|
+
} catch (error) {
|
|
876
|
+
progress({ status: "failed", bytes: 0 });
|
|
877
|
+
throw new Error(`${file.name}\u5904\u7406\u5931\u8D25\u3002 ${error instanceof Error ? error.message : "\u8BF7\u91CD\u8BD5\u5B89\u88C5\u3002"}`, { cause: error });
|
|
878
|
+
}
|
|
879
|
+
}
|
|
733
880
|
return [release.runtime.sha256, release.python.sha256, release.pythonDependencies.sha256];
|
|
734
881
|
});
|
|
735
882
|
await stage("install-runtime", async () => {
|
|
@@ -762,7 +909,7 @@ ${details}
|
|
|
762
909
|
dependencies: { "@huohuo/research-agent": `file:${paths.archive}` }
|
|
763
910
|
});
|
|
764
911
|
const npm = npmCommand();
|
|
765
|
-
await execute4(npm.command, [...npm.prefix, "install", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund", "--prefer-offline", "--cache", join3(home, "cache/npm")], paths.directory);
|
|
912
|
+
await withActivity("\u5B89\u88C5 npm \u4F9D\u8D56", () => execute4(npm.command, [...npm.prefix, "install", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund", "--prefer-offline", "--cache", join3(home, "cache/npm")], paths.directory));
|
|
766
913
|
const runtime = JSON.parse(await readFile4(join3(paths.runtime, "package.json"), "utf8"));
|
|
767
914
|
if (runtime.name !== release.runtime.name || runtime.version !== release.version) throw new Error("\u8FD0\u884C\u5305\u8EAB\u4EFD\u4E0D\u7B26");
|
|
768
915
|
const requirements = await readFile4(join3(paths.runtime, "sidecar/requirements.lock"));
|
|
@@ -796,7 +943,12 @@ ${details}
|
|
|
796
943
|
}
|
|
797
944
|
if (!environmentReady) {
|
|
798
945
|
await execute4(join3(paths.python, release.python.executable), ["-m", "venv", paths.environment], home);
|
|
799
|
-
|
|
946
|
+
if (release.pythonDependencies.bundled && hooks.progress) {
|
|
947
|
+
const lock = await readFile4(join3(wheels, "requirements.txt"), "utf8");
|
|
948
|
+
const upstream = lock.split("\n").filter((line) => /^[\w.-]+\s+@\s+https:\/\//.test(line)).length;
|
|
949
|
+
if (upstream) hooks.progress?.(`Python \u4F9D\u8D56\u6E05\u5355\u6709 ${upstream} \u4E2A\u4E0A\u6E38\u6587\u4EF6\uFF0C\u4E0B\u65B9\u663E\u793A\u6B63\u5728\u5904\u7406\u7684\u6587\u4EF6`);
|
|
950
|
+
}
|
|
951
|
+
await withActivity("\u5B89\u88C5 Python \u4F9D\u8D56", () => execute4(python, [
|
|
800
952
|
"-m",
|
|
801
953
|
"pip",
|
|
802
954
|
"--isolated",
|
|
@@ -809,7 +961,7 @@ ${details}
|
|
|
809
961
|
...release.pythonDependencies.bundled ? ["--no-cache-dir"] : [],
|
|
810
962
|
"-r",
|
|
811
963
|
join3(wheels, "requirements.txt")
|
|
812
|
-
], home);
|
|
964
|
+
], home));
|
|
813
965
|
}
|
|
814
966
|
await execute4(python, ["-m", "pip", "--isolated", "check"], home);
|
|
815
967
|
await atomicJSON(join3(paths.environment, ".dependencies-ready.json"), { sha256: release.pythonDependencies.sha256 });
|
|
@@ -822,7 +974,7 @@ ${details}
|
|
|
822
974
|
return record;
|
|
823
975
|
}
|
|
824
976
|
await stage("assemble-profiles", async () => {
|
|
825
|
-
await execute4(process.execPath, [join3(paths.runtime, "lib/distribution/managed.js"), "prepare", home], home);
|
|
977
|
+
await withActivity("\u914D\u7F6E\u5BF9\u8BDD\u4E0E\u540E\u53F0\u8FD0\u884C\u4F9D\u8D56", () => execute4(process.execPath, [join3(paths.runtime, "lib/distribution/managed.js"), "prepare", home], home));
|
|
826
978
|
return { release: record.releaseDigest };
|
|
827
979
|
});
|
|
828
980
|
await stage("verify", async () => {
|
|
@@ -1071,6 +1223,220 @@ async function uninstall(home, hooks = {}) {
|
|
|
1071
1223
|
});
|
|
1072
1224
|
}
|
|
1073
1225
|
|
|
1226
|
+
// distribution/bootstrap/install-ui.ts
|
|
1227
|
+
import { createInterface } from "node:readline";
|
|
1228
|
+
import { homedir } from "node:os";
|
|
1229
|
+
import { relative, sep as sep3 } from "node:path";
|
|
1230
|
+
var gb = (bytes) => bytes === null ? "\u672A\u77E5" : `${(bytes / 1e9).toFixed(2)} GB`;
|
|
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
|
+
}
|
|
1241
|
+
function installUI(assumeYes) {
|
|
1242
|
+
const interactive = process.stdin.isTTY && process.stderr.isTTY;
|
|
1243
|
+
const output = process.stderr;
|
|
1244
|
+
let lastUpdate = 0;
|
|
1245
|
+
let lineActive = false;
|
|
1246
|
+
const clear = () => {
|
|
1247
|
+
if (lineActive) {
|
|
1248
|
+
output.write("\r\x1B[2K");
|
|
1249
|
+
lineActive = false;
|
|
1250
|
+
}
|
|
1251
|
+
};
|
|
1252
|
+
const line = (text) => {
|
|
1253
|
+
clear();
|
|
1254
|
+
output.write(text + "\n");
|
|
1255
|
+
};
|
|
1256
|
+
const heading = (text) => line(interactive && process.env.TERM !== "dumb" && process.env.NO_COLOR === void 0 ? `\x1B[1m${text}\x1B[22m` : text);
|
|
1257
|
+
return {
|
|
1258
|
+
async confirm(plan) {
|
|
1259
|
+
const { sample, policy, disks } = plan.resources;
|
|
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");
|
|
1266
|
+
for (const [index, disk] of disks.entries()) {
|
|
1267
|
+
const location = sample.disks.find((item) => item.device === disk.device)?.paths[0] ?? plan.home;
|
|
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}`);
|
|
1291
|
+
}
|
|
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("");
|
|
1296
|
+
if (assumeYes) return true;
|
|
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");
|
|
1298
|
+
const terminal = createInterface({ input: process.stdin, output, terminal: true });
|
|
1299
|
+
try {
|
|
1300
|
+
const answers = terminal[Symbol.asyncIterator]();
|
|
1301
|
+
output.write("\u7EE7\u7EED\u5B89\u88C5\uFF1F[y/N] ");
|
|
1302
|
+
const answer = await answers.next();
|
|
1303
|
+
return !answer.done && /^(y|yes|是)$/i.test(answer.value.trim());
|
|
1304
|
+
} finally {
|
|
1305
|
+
terminal.close();
|
|
1306
|
+
}
|
|
1307
|
+
},
|
|
1308
|
+
downloadProgress(file, event) {
|
|
1309
|
+
const label = `[${file.index}/${file.total}] ${file.name}`;
|
|
1310
|
+
if (event.status === "probing") {
|
|
1311
|
+
line(`${label} \u6B63\u5728\u68C0\u67E5\u4E0B\u8F7D\u6E90\u662F\u5426\u53EF\u8FBE`);
|
|
1312
|
+
} else if (event.status === "start") {
|
|
1313
|
+
lastUpdate = Date.now();
|
|
1314
|
+
line(`${label} ${event.host ? `\u6B63\u5728\u8FDE\u63A5 ${event.host}` : "\u6B63\u5728\u8BFB\u53D6\u968F\u5305\u8D44\u6E90"} ${mb(file.size)}`);
|
|
1315
|
+
} else if (event.status === "cached") {
|
|
1316
|
+
line(`${label} \u5DF2\u6821\u9A8C\u5E76\u590D\u7528\u7F13\u5B58`);
|
|
1317
|
+
} else if (event.status === "verified") {
|
|
1318
|
+
line(`${label} \u5DF2\u5B8C\u6210\u5E76\u6821\u9A8C ${mb(file.size)}`);
|
|
1319
|
+
} else if (event.status === "retry") {
|
|
1320
|
+
line(`${label} ${event.host} \u4E0B\u8F7D\u672A\u5B8C\u6210\uFF0C\u51C6\u5907\u91CD\u8BD5`);
|
|
1321
|
+
} else if (event.status === "failed") {
|
|
1322
|
+
clear();
|
|
1323
|
+
} else {
|
|
1324
|
+
const now = Date.now();
|
|
1325
|
+
if (now - lastUpdate < (interactive ? 180 : 15e3)) return;
|
|
1326
|
+
lastUpdate = now;
|
|
1327
|
+
const percent = Math.min(99, Math.floor(100 * event.bytes / file.size));
|
|
1328
|
+
const filled = Math.floor(percent / 10);
|
|
1329
|
+
const value = `[${"#".repeat(filled)}${"-".repeat(10 - filled)}] ${percent}% ${mb(event.bytes)} / ${mb(file.size)}`;
|
|
1330
|
+
if (interactive && (output.columns ?? 0) >= value.length) {
|
|
1331
|
+
clear();
|
|
1332
|
+
output.write(value);
|
|
1333
|
+
lineActive = true;
|
|
1334
|
+
} else line(`${label} ${value}`);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// distribution/bootstrap/status-ui.ts
|
|
1341
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
1342
|
+
import { join as join6 } from "node:path";
|
|
1343
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
1344
|
+
var mib = (bytes) => typeof bytes === "number" && Number.isFinite(bytes) ? `${Math.round(bytes / 2 ** 20)} MiB` : "\u672A\u77E5";
|
|
1345
|
+
var pressureLabels = { normal: "\u6B63\u5E38", high: "\u504F\u9AD8", critical: "\u4E25\u91CD", unknown: "\u672A\u77E5" };
|
|
1346
|
+
async function statusLines(home) {
|
|
1347
|
+
const title = "\u706B\u706B \xB7 \u8FD0\u884C\u72B6\u6001";
|
|
1348
|
+
const installed = await readInstallation(home);
|
|
1349
|
+
if (!installed) return [title, "\u5C1A\u672A\u5B89\u88C5\u706B\u706B\u3002"];
|
|
1350
|
+
if (installed.status !== "ready") return [title, "\u5B89\u88C5\u5C1A\u672A\u5B8C\u6210\u3002"];
|
|
1351
|
+
let admission;
|
|
1352
|
+
try {
|
|
1353
|
+
admission = JSON.parse(await readFile7(join6(home, "state/default-admission.json"), "utf8"));
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
if (error.code !== "ENOENT") return [title, "\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u8D44\u6E90\u72B6\u6001\u3002"];
|
|
1356
|
+
}
|
|
1357
|
+
const now = Date.now() / 1e3;
|
|
1358
|
+
if (!admission || admission.format !== 1 || admission.releaseDigest !== installed.releaseDigest || !Number.isFinite(admission.sampledAt) || !(admission.validUntil > now) || typeof admission.allowHeavy !== "boolean" || !admission.resources?.sample) {
|
|
1359
|
+
return [title, "\u6682\u65E0\u6709\u6548\u7684\u8FD0\u884C\u91C7\u6837\u3002\u8BF7\u68C0\u67E5\u706B\u706B\u670D\u52A1\u662F\u5426\u6B63\u5728\u8FD0\u884C\u3002"];
|
|
1360
|
+
}
|
|
1361
|
+
const { sample, reasons } = admission.resources;
|
|
1362
|
+
const needed = installed.release.budget.heavyJobHeadroomBytes;
|
|
1363
|
+
const gap = sample.availableMemoryBytes === null ? null : Math.max(0, needed - sample.availableMemoryBytes);
|
|
1364
|
+
const lines = [
|
|
1365
|
+
title,
|
|
1366
|
+
`\u540E\u53F0\u7814\u7A76 ${admission.allowHeavy ? "\u53EF\u63A5\u6536\u4EFB\u52A1" : "\u6682\u7F13\u65B0\u4EFB\u52A1"}`,
|
|
1367
|
+
`\u7CFB\u7EDF\u5185\u5B58 \u53EF\u7528 ${mib(sample.availableMemoryBytes)} / \u603B\u5BB9\u91CF ${mib(sample.effectiveMemoryBytes)}`,
|
|
1368
|
+
`\u4EFB\u52A1\u95E8\u69DB \u9884\u7559 ${mib(needed)}${gap ? `\uFF0C\u8FD8\u5DEE ${mib(gap)}` : ""}`,
|
|
1369
|
+
`\u5185\u5B58\u538B\u529B ${pressureLabels[sample.pressure] ?? "\u672A\u77E5"}`,
|
|
1370
|
+
`\u706B\u706B\u8FDB\u7A0B \u4F30\u7B97\u5360\u7528 ${mib(admission.rssSumEstimateBytes)}`
|
|
1371
|
+
];
|
|
1372
|
+
if (!admission.allowHeavy) lines.push(reasons?.[0] ? `\u6682\u7F13\u539F\u56E0 ${reasons[0]}` : "\u6B63\u5728\u7B49\u5F85\u8FDE\u7EED\u5065\u5EB7\u91C7\u6837\u3002");
|
|
1373
|
+
lines.push(`\u91C7\u6837\u65F6\u95F4 ${new Date(admission.sampledAt * 1e3).toLocaleTimeString()}`);
|
|
1374
|
+
return lines;
|
|
1375
|
+
}
|
|
1376
|
+
async function showStatus(home, watch) {
|
|
1377
|
+
const output = process.stdout;
|
|
1378
|
+
if (!watch || !output.isTTY || process.env.TERM === "dumb") {
|
|
1379
|
+
output.write((await statusLines(home)).join("\n") + "\n");
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
const stop = new AbortController();
|
|
1383
|
+
const close = () => stop.abort();
|
|
1384
|
+
const input = process.stdin;
|
|
1385
|
+
const raw = input.isTTY;
|
|
1386
|
+
const wasRaw = input.isRaw;
|
|
1387
|
+
const onKey = (chunk) => {
|
|
1388
|
+
if (chunk.includes(3)) close();
|
|
1389
|
+
};
|
|
1390
|
+
process.once("SIGINT", close);
|
|
1391
|
+
process.once("SIGTERM", close);
|
|
1392
|
+
try {
|
|
1393
|
+
if (raw) {
|
|
1394
|
+
input.setRawMode(true);
|
|
1395
|
+
input.on("data", onKey);
|
|
1396
|
+
input.resume();
|
|
1397
|
+
}
|
|
1398
|
+
output.write("\x1B[?1049h\x1B[?25l");
|
|
1399
|
+
while (!stop.signal.aborted) {
|
|
1400
|
+
const lines = await statusLines(home);
|
|
1401
|
+
if (stop.signal.aborted) break;
|
|
1402
|
+
output.write("\x1B[H\x1B[0J" + [...lines, "", "\u6BCF 2 \u79D2\u5237\u65B0 \xB7 Ctrl+C \u5173\u95ED\u770B\u677F"].join("\n"));
|
|
1403
|
+
await delay2(2e3, void 0, { signal: stop.signal }).catch((error) => {
|
|
1404
|
+
if (!stop.signal.aborted) throw error;
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
} finally {
|
|
1408
|
+
output.write("\x1B[?25h\x1B[?1049l");
|
|
1409
|
+
if (raw) {
|
|
1410
|
+
input.off("data", onKey);
|
|
1411
|
+
input.setRawMode(Boolean(wasRaw));
|
|
1412
|
+
input.pause();
|
|
1413
|
+
}
|
|
1414
|
+
process.off("SIGINT", close);
|
|
1415
|
+
process.off("SIGTERM", close);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
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
|
+
|
|
1074
1440
|
// distribution/bootstrap/entry.ts
|
|
1075
1441
|
function artifactSource(source, directory) {
|
|
1076
1442
|
return async (url, options) => {
|
|
@@ -1079,8 +1445,8 @@ function artifactSource(source, directory) {
|
|
|
1079
1445
|
const artifact2 = source && [source.runtime, source.python, source.pythonDependencies].find((item) => item.url === String(url));
|
|
1080
1446
|
const sha256 = bundled?.[1] ?? artifact2?.sha256;
|
|
1081
1447
|
if (!sha256) throw new Error("\u672C\u673A\u4EA7\u7269\u76EE\u5F55\u7F3A\u5C11\u8BE5\u56FA\u5B9A\u53D1\u884C\u8D44\u6E90");
|
|
1082
|
-
const root = directory ??
|
|
1083
|
-
const path =
|
|
1448
|
+
const root = directory ?? await assetsDirectory(source, import.meta.url);
|
|
1449
|
+
const path = join8(root, sha256 + ".tgz");
|
|
1084
1450
|
const file = await open3(path).catch((error) => {
|
|
1085
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");
|
|
1086
1452
|
throw error;
|
|
@@ -1089,7 +1455,7 @@ function artifactSource(source, directory) {
|
|
|
1089
1455
|
};
|
|
1090
1456
|
}
|
|
1091
1457
|
async function main(args = process.argv.slice(2)) {
|
|
1092
|
-
let home =
|
|
1458
|
+
let home = join8(homedir2(), ".huohuo"), noOpen = false, assumeYes = false, watch = false, command = "start";
|
|
1093
1459
|
let releaseFile, artifactDirectory;
|
|
1094
1460
|
for (let i = 0; i < args.length; i++) {
|
|
1095
1461
|
if (args[i] === "--home") {
|
|
@@ -1101,19 +1467,23 @@ async function main(args = process.argv.slice(2)) {
|
|
|
1101
1467
|
if (option === "--release") releaseFile = resolve5(args[++i]);
|
|
1102
1468
|
else artifactDirectory = resolve5(args[++i]);
|
|
1103
1469
|
} else if (args[i] === "--no-open") noOpen = true;
|
|
1470
|
+
else if (args[i] === "--yes") assumeYes = true;
|
|
1471
|
+
else if (args[i] === "--watch") watch = true;
|
|
1104
1472
|
else if (args[i] === "--version") {
|
|
1105
|
-
const packageFile =
|
|
1106
|
-
const manifest = JSON.parse(await
|
|
1473
|
+
const packageFile = join8(dirname6(fileURLToPath2(import.meta.url)), "package.json");
|
|
1474
|
+
const manifest = JSON.parse(await readFile9(packageFile, "utf8"));
|
|
1107
1475
|
console.log(`${manifest.name} ${manifest.version}${manifest.private ? "\uFF08\u672C\u5730\u5019\u9009\uFF09" : ""}`);
|
|
1108
1476
|
return;
|
|
1109
|
-
} else if (["start", "doctor", "stop", "upgrade", "uninstall"].includes(args[i])) command = args[i];
|
|
1110
|
-
else throw new Error("\u7528\u6CD5\uFF1Ahuohuo [start|doctor|stop|upgrade|uninstall] [--home \u76EE\u5F55] [--no-open]");
|
|
1477
|
+
} else if (["start", "status", "doctor", "stop", "upgrade", "uninstall"].includes(args[i])) command = args[i];
|
|
1478
|
+
else throw new Error("\u7528\u6CD5\uFF1Ahuohuo [start|status|doctor|stop|upgrade|uninstall] [--home \u76EE\u5F55] [--no-open]");
|
|
1111
1479
|
}
|
|
1480
|
+
if (watch && command !== "status") throw new Error("--watch \u8BF7\u4E0E status \u4E00\u8D77\u4F7F\u7528");
|
|
1112
1481
|
try {
|
|
1113
1482
|
home = await realpath5(home);
|
|
1114
1483
|
} catch (e) {
|
|
1115
1484
|
if (e.code !== "ENOENT") throw e;
|
|
1116
1485
|
}
|
|
1486
|
+
if (command === "status") return showStatus(home, watch);
|
|
1117
1487
|
let record = await readInstallation(home);
|
|
1118
1488
|
const labels = {
|
|
1119
1489
|
preflight: "\u68C0\u67E5\u672C\u673A\u73AF\u5883",
|
|
@@ -1124,13 +1494,19 @@ async function main(args = process.argv.slice(2)) {
|
|
|
1124
1494
|
verify: "\u68C0\u67E5\u5B89\u88C5\u7ED3\u679C",
|
|
1125
1495
|
"resume-current-package": "\u4E0A\u6B21\u5B89\u88C5\u5C1A\u672A\u5199\u5165\u8FD0\u884C\u73AF\u5883\uFF0C\u5C06\u4F7F\u7528\u5F53\u524D\u7248\u672C\u91CD\u65B0\u68C0\u67E5"
|
|
1126
1496
|
};
|
|
1127
|
-
const
|
|
1497
|
+
const installStages = ["preflight", "download", "install-runtime", "install-dependencies", "assemble-profiles", "verify"];
|
|
1498
|
+
const progress = (stage) => {
|
|
1499
|
+
const index = command === "start" ? installStages.indexOf(stage) : -1;
|
|
1500
|
+
const step = index < 0 ? "" : `[${index + 1}/${installStages.length}] `;
|
|
1501
|
+
console.log(`\u706B\u706B\uFF1A${step}${labels[stage] ?? stage}`);
|
|
1502
|
+
};
|
|
1503
|
+
const ui = installUI(assumeYes);
|
|
1128
1504
|
if (command === "upgrade") {
|
|
1129
|
-
const candidate = JSON.parse(await
|
|
1130
|
-
await upgrade(home, candidate, { fetcher: artifactSource(candidate, artifactDirectory), progress });
|
|
1505
|
+
const candidate = selectRelease(JSON.parse(await readFile9(releaseFile ?? join8(dirname6(fileURLToPath2(import.meta.url)), "release.json"), "utf8")));
|
|
1506
|
+
await upgrade(home, candidate, { fetcher: artifactSource(candidate, artifactDirectory), progress, ...ui });
|
|
1131
1507
|
const current = await readInstallation(home);
|
|
1132
1508
|
if (await control(home, current.releaseDigest, noOpen ? "status" : "open")) return;
|
|
1133
|
-
await run(process.execPath, [
|
|
1509
|
+
await run(process.execPath, [join8(installationPaths(home, current.release).runtime, "lib/distribution/managed.js"), "start", home, ...noOpen ? ["--no-open"] : []], home);
|
|
1134
1510
|
return;
|
|
1135
1511
|
}
|
|
1136
1512
|
if (command === "uninstall") {
|
|
@@ -1148,7 +1524,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
1148
1524
|
return;
|
|
1149
1525
|
}
|
|
1150
1526
|
const paths = installationPaths(home, record.release);
|
|
1151
|
-
const targets = [home, paths.directory, paths.environment,
|
|
1527
|
+
const targets = [home, paths.directory, paths.environment, join8(home, "instances/default")];
|
|
1152
1528
|
const resources = evaluateResources(await sampleResources(targets), record.release.budget, targets.map((path) => ({ path, bytes: 0 })), "start");
|
|
1153
1529
|
console.log(JSON.stringify({
|
|
1154
1530
|
home,
|
|
@@ -1169,33 +1545,34 @@ async function main(args = process.argv.slice(2)) {
|
|
|
1169
1545
|
}
|
|
1170
1546
|
const runtime = installationPaths(home, record.release).runtime;
|
|
1171
1547
|
try {
|
|
1172
|
-
await
|
|
1548
|
+
await readFile9(join8(runtime, "lib/distribution/managed.js"));
|
|
1173
1549
|
} catch (e) {
|
|
1174
1550
|
if (e.code !== "ENOENT") throw e;
|
|
1175
1551
|
record = void 0;
|
|
1176
1552
|
}
|
|
1177
1553
|
}
|
|
1178
1554
|
if (!record || record.status !== "ready") {
|
|
1179
|
-
|
|
1180
|
-
|
|
1555
|
+
const persisted = await readInstallation(home);
|
|
1556
|
+
let source = persisted?.release;
|
|
1557
|
+
if (!source) {
|
|
1558
|
+
let catalog = null;
|
|
1181
1559
|
try {
|
|
1182
|
-
catalog = JSON.parse(await
|
|
1560
|
+
catalog = JSON.parse(await readFile9(releaseFile ?? join8(dirname6(fileURLToPath2(import.meta.url)), "release.json"), "utf8"));
|
|
1183
1561
|
} catch (e) {
|
|
1184
1562
|
if (e.code !== "ENOENT") throw e;
|
|
1185
1563
|
}
|
|
1564
|
+
source = selectRelease(catalog);
|
|
1186
1565
|
}
|
|
1187
|
-
const source = (await readInstallation(home))?.release ?? catalog;
|
|
1188
1566
|
const fetcher = artifactSource(source, artifactDirectory);
|
|
1189
|
-
|
|
1190
|
-
record = await install(home, catalog, { fetcher, progress, bundleDirectory });
|
|
1567
|
+
record = await install(home, source, { fetcher, progress, ...ui });
|
|
1191
1568
|
}
|
|
1192
1569
|
const installedHome = record.home;
|
|
1193
|
-
await run(process.execPath, [
|
|
1570
|
+
await run(process.execPath, [join8(installationPaths(installedHome, record.release).runtime, "lib/distribution/managed.js"), "start", installedHome, ...noOpen ? ["--no-open"] : []], installedHome);
|
|
1194
1571
|
}
|
|
1195
|
-
if (process.argv[1] && await realpath5(resolve5(process.argv[1])).catch(() => "") === await realpath5(
|
|
1572
|
+
if (process.argv[1] && await realpath5(resolve5(process.argv[1])).catch(() => "") === await realpath5(fileURLToPath2(import.meta.url))) {
|
|
1196
1573
|
main().catch((error) => {
|
|
1197
1574
|
console.error(error instanceof Error ? error.message : "\u5B89\u88C5\u6216\u542F\u52A8\u5931\u8D25");
|
|
1198
|
-
process.exitCode = 1;
|
|
1575
|
+
if (error?.code !== "INSTALL_CANCELLED") process.exitCode = 1;
|
|
1199
1576
|
});
|
|
1200
1577
|
}
|
|
1201
1578
|
export {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pab-iprg/huohuo",
|
|
3
|
-
"version": "1.0.0-preview.
|
|
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": "
|
|
23
|
+
"tag": "preview"
|
|
21
24
|
}
|
|
22
25
|
}
|
package/release.json
CHANGED
|
@@ -1,93 +1,205 @@
|
|
|
1
1
|
{
|
|
2
|
+
"kind": "huohuo-release-catalog",
|
|
2
3
|
"format": 1,
|
|
3
|
-
"bootstrapFormat": 1,
|
|
4
4
|
"version": "1.0.0",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
}
|
|
91
203
|
}
|
|
92
|
-
|
|
204
|
+
]
|
|
93
205
|
}
|
|
Binary file
|