iterate-harness 1.12.9 → 1.12.10
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 +8 -6
- package/README.zh-CN.md +5 -4
- package/lib/bootstrap.js +72 -58
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,12 +30,14 @@ delegation logic used at install time and at runtime is the same:
|
|
|
30
30
|
(overridable via `ITERATE_HARNESS_NPM_HOME`).
|
|
31
31
|
3. `pip install`s the harness **pinned to this npm package's version** — npm
|
|
32
32
|
`1.6.0` always installs harness `v1.6.0`. Upgrading the npm package
|
|
33
|
-
automatically re-installs the matching harness version
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
automatically re-installs the matching harness version on the next run.
|
|
34
|
+
The preferred source is the official **PyPI** index: pip resolves
|
|
35
|
+
`iterate-harness==X.Y.Z` against your configured mirror (e.g.
|
|
36
|
+
`pypi.tuna.tsinghua.edu.cn`), so installs work even when GitHub is
|
|
37
|
+
unreachable or its TLS cert fails to verify. If PyPI is unavailable, the
|
|
38
|
+
wrapper falls back to the pre-built **wheel** on the GitHub release
|
|
39
|
+
(frontend assets baked in, like iterate-skill-installer ships pre-wrapped
|
|
40
|
+
assets), then the pinned **source archive** as a final resort.
|
|
39
41
|
4. Delegates to the venv's real `ih` executable with argv, stdio, signals and
|
|
40
42
|
exit codes forwarded.
|
|
41
43
|
|
package/README.zh-CN.md
CHANGED
|
@@ -27,10 +27,11 @@ harness 本身是**一个 Python 包**。这个 npm 包是一个薄分发包装
|
|
|
27
27
|
2. 在 `~/.iterate-harness-npm/venv` 创建托管虚拟环境
|
|
28
28
|
(可通过 `ITERATE_HARNESS_NPM_HOME` 覆盖)。
|
|
29
29
|
3. `pip install` **与本 npm 包版本锁定的** harness 发布版本 —— npm 的 `1.6.0`
|
|
30
|
-
总会安装 harness `v1.6.0`。升级 npm
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
若 wheel
|
|
30
|
+
总会安装 harness `v1.6.0`。升级 npm 包会在下次运行时自动重装对应的 harness 版本。
|
|
31
|
+
**首选官方 PyPI 索引**:pip 会按你配置的镜像源(如 `pypi.tuna.tsinghua.edu.cn`)
|
|
32
|
+
解析 `iterate-harness==X.Y.Z`,即使 GitHub 不可达或 TLS 证书校验失败也能安装。
|
|
33
|
+
若 PyPI 不可用,再兑底 GitHub release 上的**预构建 wheel**(已内置编译好的前端
|
|
34
|
+
资源——与 iterate-skill-installer 分发预打包资源一致),最后才用锁定的**源码归档**。
|
|
34
35
|
4. 代理调用 venv 里真正的 `ih` 可执行文件,转发 argv、stdio、信号与退出码。
|
|
35
36
|
|
|
36
37
|
若 `npm install` 期间无法完成安装(无 Python、无网络,或
|
package/lib/bootstrap.js
CHANGED
|
@@ -8,17 +8,18 @@
|
|
|
8
8
|
* 1. Resolve a Python interpreter >= 3.10 (env override first).
|
|
9
9
|
* 2. Create/reuse the venv at ~/.iterate-harness-npm/venv.
|
|
10
10
|
* 3. pip-install the harness release pinned to THIS npm package's version
|
|
11
|
-
* (lockstep: npm 1.12.
|
|
12
|
-
* the
|
|
13
|
-
* the
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
11
|
+
* (lockstep: npm 1.12.9 installs harness v1.12.9). The preferred source is
|
|
12
|
+
* the official **PyPI** index — pip resolves `iterate-harness==X.Y.Z` against
|
|
13
|
+
* the user's configured mirror (e.g. `pypi.tuna.tsinghua.edu.cn`), so it
|
|
14
|
+
* works even when GitHub is unreachable or its TLS cert fails to verify.
|
|
15
|
+
* If PyPI is unavailable, the wrapper falls back to the pre-built GitHub
|
|
16
|
+
* **wheel** release asset (frontend dist baked in, like iterate-skill-
|
|
17
|
+
* installer ships pre-wrapped assets), pip-installing it directly and, if
|
|
18
|
+
* pip's trust store is broken (common on macOS python.org builds), retrying
|
|
19
|
+
* by downloading the wheel with Node's own https stack — and, failing that,
|
|
20
|
+
* with curl (system trust store) — then pip-installing the local file. If
|
|
21
|
+
* the wheel is missing too, the worker falls back to the pinned source
|
|
22
|
+
* archive as a final resort.
|
|
22
23
|
* 4. Stamp the installed version; a mismatch (npm upgrade) triggers a
|
|
23
24
|
* re-install on the next run.
|
|
24
25
|
* 5. Spawn the venv's `ih` with argv/stdio/signals forwarded and the exit
|
|
@@ -60,6 +61,11 @@ const HARNESS_GITHUB_REPO = "jingzhao-l/iterate-harness";
|
|
|
60
61
|
const HARNESS_RELEASE_DOWNLOAD_URL = `https://github.com/${HARNESS_GITHUB_REPO}/releases/download`;
|
|
61
62
|
const HARNESS_REPO_ARCHIVE_URL = `https://github.com/${HARNESS_GITHUB_REPO}/archive/refs/tags`;
|
|
62
63
|
|
|
64
|
+
// Preferred install channel: the official PyPI index. pip reads the user's
|
|
65
|
+
// mirror config (e.g. `pypi.tuna.tsinghua.edu.cn`), so it stays fast and
|
|
66
|
+
// stable even when GitHub is unreachable — unlike the GitHub wheel fallback.
|
|
67
|
+
const PYPI_PACKAGE_NAME = "iterate-harness";
|
|
68
|
+
|
|
63
69
|
// Preferred installable artifact: the pre-built wheel (frontend dist baked in).
|
|
64
70
|
const WHEEL_ASSET_SUFFIX = "-py3-none-any.whl";
|
|
65
71
|
// Last-resort artifact: the pinned source archive (pip builds from source).
|
|
@@ -91,6 +97,12 @@ function wheelAssetUrl(version) {
|
|
|
91
97
|
return `${HARNESS_RELEASE_DOWNLOAD_URL}/v${version}/${wheelAssetName(version)}`;
|
|
92
98
|
}
|
|
93
99
|
|
|
100
|
+
// PyPI package spec pinned to the version (e.g. `iterate-harness==1.12.9`).
|
|
101
|
+
// pip resolves this against the user's configured index/mirror, not GitHub.
|
|
102
|
+
function pypiInstallSpec(version) {
|
|
103
|
+
return `${PYPI_PACKAGE_NAME}==${version}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
94
106
|
// Last-resort installable source: the pinned source archive (builds on pip).
|
|
95
107
|
function releaseTarballUrl(version) {
|
|
96
108
|
return `${HARNESS_REPO_ARCHIVE_URL}/v${version}.tar.gz`;
|
|
@@ -274,25 +286,17 @@ function runStep(command, args, options) {
|
|
|
274
286
|
}
|
|
275
287
|
}
|
|
276
288
|
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
return wheelAssetUrl(version);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Last-resort fallback URL: the pinned source archive. Only relevant when the
|
|
288
|
-
// user did not override the install URL (an explicit override means they own
|
|
289
|
-
// the source and no archive fallback should kick in).
|
|
290
|
-
function installFallbackUrl(version, env) {
|
|
289
|
+
// Ordered install candidates, best-effort first:
|
|
290
|
+
// 1. PyPI spec — pip installs via the user's mirror (stable, no GitHub).
|
|
291
|
+
// 2. Pre-built wheel on the GitHub release (needs GitHub, but no source build).
|
|
292
|
+
// 3. Pinned source archive on GitHub (last resort; pip builds from source).
|
|
293
|
+
// An explicit env override short-circuits the chain (the user owns the source).
|
|
294
|
+
function installCandidates(version, env) {
|
|
291
295
|
const override = env ? env[INSTALL_URL_ENV_VAR] : undefined;
|
|
292
296
|
if (override) {
|
|
293
|
-
return
|
|
297
|
+
return [override];
|
|
294
298
|
}
|
|
295
|
-
return releaseTarballUrl(version);
|
|
299
|
+
return [pypiInstallSpec(version), wheelAssetUrl(version), releaseTarballUrl(version)];
|
|
296
300
|
}
|
|
297
301
|
|
|
298
302
|
// ---------------------------------------------------------------------------
|
|
@@ -421,41 +425,55 @@ async function downloadTarballTo(url, dest, deps) {
|
|
|
421
425
|
|
|
422
426
|
async function installHarness(options) {
|
|
423
427
|
const python = options.python;
|
|
424
|
-
const url = options.url;
|
|
425
428
|
const homeDir = options.homeDir;
|
|
426
429
|
const version = options.version;
|
|
430
|
+
const candidates = options.candidates;
|
|
427
431
|
const runStepFn = options.runStepFn || runStep;
|
|
428
432
|
const downloader = options.downloader || downloadTarballTo;
|
|
429
|
-
// Optional terminal fallback (e.g. the source archive if the wheel is missing).
|
|
430
|
-
const fallback = options.fallback;
|
|
431
433
|
|
|
434
|
+
if (!candidates || candidates.length === 0) {
|
|
435
|
+
throw new BootstrapError("no install candidate URLs were provided");
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const failures = [];
|
|
439
|
+
for (const candidate of candidates) {
|
|
440
|
+
try {
|
|
441
|
+
if (isRemoteHttpUrl(candidate)) {
|
|
442
|
+
// HTTP(S) candidate: `pip` first, then node/curl download + local pip
|
|
443
|
+
// (survives broken Python TLS trust stores on macOS python.org builds).
|
|
444
|
+
await installRemoteArtifact(python, candidate, homeDir, version, runStepFn, downloader);
|
|
445
|
+
} else {
|
|
446
|
+
// Non-URL candidate (e.g. `iterate-harness==1.12.9` resolved by pip
|
|
447
|
+
// against the user's mirror): no artifact to download, just pip it.
|
|
448
|
+
runStepFn(python, pipInstallArgs(candidate));
|
|
449
|
+
}
|
|
450
|
+
return;
|
|
451
|
+
} catch (error) {
|
|
452
|
+
failures.push(`${candidate}: ${error.message}`);
|
|
453
|
+
process.stderr.write(
|
|
454
|
+
`[iterate-harness] install from "${candidate}" failed (${error.message}); ` +
|
|
455
|
+
"trying the next candidate ...\n"
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
throw new BootstrapError(`all install candidates failed:\n ${failures.join("\n ")}`);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// pip-install a downloadable HTTP artifact, retrying with a node/curl download
|
|
464
|
+
// of the raw file + local pip install when pip's TLS trust store is broken.
|
|
465
|
+
async function installRemoteArtifact(python, url, homeDir, version, runStepFn, downloader) {
|
|
432
466
|
try {
|
|
433
467
|
runStepFn(python, pipInstallArgs(url));
|
|
434
468
|
return;
|
|
435
469
|
} catch (primaryError) {
|
|
436
|
-
if (!isRemoteHttpUrl(url)) {
|
|
437
|
-
throw primaryError;
|
|
438
|
-
}
|
|
439
470
|
process.stderr.write(
|
|
440
471
|
`[iterate-harness] pip install from ${url} failed; retrying via direct download ...\n`
|
|
441
472
|
);
|
|
442
|
-
const cachePath = downloadCachePath(homeDir, version, artifactExtensionFor(url));
|
|
443
|
-
try {
|
|
444
|
-
await downloader(url, cachePath);
|
|
445
|
-
} catch (downloadError) {
|
|
446
|
-
if (fallback) {
|
|
447
|
-
process.stderr.write(
|
|
448
|
-
`[iterate-harness] primary artifact unavailable (${downloadError.message}); ` +
|
|
449
|
-
`falling back to source archive ${fallback.url} ...\n`
|
|
450
|
-
);
|
|
451
|
-
return installHarness(fallback);
|
|
452
|
-
}
|
|
453
|
-
throw new BootstrapError(
|
|
454
|
-
`${primaryError.message}\n\n(direct-download fallback also failed: ${downloadError.message})`
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
|
-
runStepFn(python, pipInstallArgs(cachePath));
|
|
458
473
|
}
|
|
474
|
+
const cachePath = downloadCachePath(homeDir, version, artifactExtensionFor(url));
|
|
475
|
+
await downloader(url, cachePath);
|
|
476
|
+
runStepFn(python, pipInstallArgs(cachePath));
|
|
459
477
|
}
|
|
460
478
|
|
|
461
479
|
async function bootstrap(homeDir, version, env) {
|
|
@@ -477,18 +495,13 @@ async function bootstrap(homeDir, version, env) {
|
|
|
477
495
|
runStep(interpreter.command, [...interpreter.preArgs, "-m", "venv", venvDir]);
|
|
478
496
|
}
|
|
479
497
|
|
|
480
|
-
const
|
|
481
|
-
const fallbackUrl = installFallbackUrl(version, env);
|
|
498
|
+
const candidates = installCandidates(version, env);
|
|
482
499
|
ui.step(`Installing iterate-harness ${version}`);
|
|
483
500
|
await installHarness({
|
|
484
501
|
python: executable.python,
|
|
485
|
-
|
|
502
|
+
candidates,
|
|
486
503
|
homeDir,
|
|
487
504
|
version,
|
|
488
|
-
fallback:
|
|
489
|
-
fallbackUrl != null
|
|
490
|
-
? { python: executable.python, url: fallbackUrl, homeDir, version }
|
|
491
|
-
: undefined,
|
|
492
505
|
});
|
|
493
506
|
|
|
494
507
|
const stampPath = path.join(homeDir, STAMP_FILE_NAME);
|
|
@@ -624,15 +637,16 @@ module.exports = {
|
|
|
624
637
|
downloadFile,
|
|
625
638
|
downloadTarballTo,
|
|
626
639
|
ensureRuntime,
|
|
627
|
-
|
|
640
|
+
installCandidates,
|
|
628
641
|
installHarness,
|
|
629
|
-
|
|
642
|
+
installRemoteArtifact,
|
|
630
643
|
isRemoteHttpUrl,
|
|
631
644
|
isSupportedPython,
|
|
632
645
|
needsBootstrap,
|
|
633
646
|
packageVersion,
|
|
634
647
|
parsePythonVersion,
|
|
635
648
|
pipInstallArgs,
|
|
649
|
+
pypiInstallSpec,
|
|
636
650
|
pythonCandidates,
|
|
637
651
|
releaseTarballUrl,
|
|
638
652
|
reportBootstrapFailure,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iterate-harness",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.10",
|
|
4
4
|
"description": "npm distribution wrapper for iterate-harness — installs the Python harness (ih CLI) into a managed venv on install and exposes it as a global command",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|