iterate-harness 2.2.3 → 2.2.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/lib/bootstrap.js +9 -1
- package/lib/ui.js +36 -10
- package/package.json +3 -2
- package/scripts/postinstall.js +11 -2
package/lib/bootstrap.js
CHANGED
|
@@ -587,6 +587,13 @@ function reportBootstrapFailure(error) {
|
|
|
587
587
|
process.stderr.write(`[iterate-harness] ${message}\n`);
|
|
588
588
|
}
|
|
589
589
|
|
|
590
|
+
// The install wizard only makes sense when a human can actually answer: both
|
|
591
|
+
// stdin and stderr must be TTYs. Piped/CI/`< /dev/null` runs bootstrap
|
|
592
|
+
// non-interactively so they never block on a prompt that nobody can see.
|
|
593
|
+
function interactiveSession() {
|
|
594
|
+
return Boolean(process.stdin.isTTY && process.stderr.isTTY);
|
|
595
|
+
}
|
|
596
|
+
|
|
590
597
|
async function runHarness(args, env) {
|
|
591
598
|
// Print the banner on every run (claude-code style). Skipped automatically
|
|
592
599
|
// when stderr is not a TTY (e.g. piped output), so `ih --version | jq` stays clean.
|
|
@@ -594,7 +601,7 @@ async function runHarness(args, env) {
|
|
|
594
601
|
|
|
595
602
|
let target;
|
|
596
603
|
try {
|
|
597
|
-
target = await ensureRuntime(env, { interactive:
|
|
604
|
+
target = await ensureRuntime(env, { interactive: interactiveSession() });
|
|
598
605
|
} catch (error) {
|
|
599
606
|
if (error instanceof CancelledError) {
|
|
600
607
|
ui.info(error.message);
|
|
@@ -658,6 +665,7 @@ module.exports = {
|
|
|
658
665
|
installCandidates,
|
|
659
666
|
installHarness,
|
|
660
667
|
installRemoteArtifact,
|
|
668
|
+
interactiveSession,
|
|
661
669
|
isRemoteHttpUrl,
|
|
662
670
|
isSupportedPython,
|
|
663
671
|
needsBootstrap,
|
package/lib/ui.js
CHANGED
|
@@ -80,24 +80,50 @@ function frameSection(title, lines) {
|
|
|
80
80
|
console.error(bottom);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
// Safety net for unattended runs: if stdin is closed (EOF) or the user never
|
|
84
|
+
// answers, fall back to the default instead of hanging forever. Override per
|
|
85
|
+
// call with `options.timeoutMs` (0 disables the timeout entirely).
|
|
86
|
+
const DEFAULT_PROMPT_TIMEOUT_MS = 120000;
|
|
87
|
+
|
|
88
|
+
function askYesNo(question, defaultNo = false, options = {}) {
|
|
89
|
+
const input = options.input || process.stdin;
|
|
90
|
+
const output = options.output || process.stdout;
|
|
91
|
+
const timeoutMs =
|
|
92
|
+
options.timeoutMs === undefined ? DEFAULT_PROMPT_TIMEOUT_MS : options.timeoutMs;
|
|
84
93
|
return new Promise((resolve) => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
let settled = false;
|
|
95
|
+
let timer = null;
|
|
96
|
+
const rl = readline.createInterface({ input, output });
|
|
97
|
+
const finish = (value) => {
|
|
98
|
+
if (settled) return;
|
|
99
|
+
settled = true;
|
|
100
|
+
if (timer) clearTimeout(timer);
|
|
101
|
+
rl.close();
|
|
102
|
+
resolve(value);
|
|
103
|
+
};
|
|
104
|
+
// readline emits "close" on EOF (piped/`< /dev/null` stdin) — never hang.
|
|
105
|
+
rl.on("close", () => finish(defaultNo));
|
|
106
|
+
if (timeoutMs > 0) {
|
|
107
|
+
timer = setTimeout(() => {
|
|
108
|
+
process.stderr.write(
|
|
109
|
+
`\x1b[2m no response after ${Math.round(timeoutMs / 1000)}s; using default\x1b[0m\n`
|
|
110
|
+
);
|
|
111
|
+
finish(defaultNo);
|
|
112
|
+
}, timeoutMs);
|
|
113
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
114
|
+
}
|
|
89
115
|
const hint = defaultNo ? "[y/N]" : "[Y/n]";
|
|
90
116
|
rl.question(`\x1b[36m◆\x1b[0m ${question} ${hint} `, (answer) => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (a === "
|
|
94
|
-
else
|
|
95
|
-
else resolve(defaultNo);
|
|
117
|
+
const a = String(answer).trim().toLowerCase();
|
|
118
|
+
if (a === "y" || a === "yes") finish(true);
|
|
119
|
+
else if (a === "n" || a === "no") finish(false);
|
|
120
|
+
else finish(defaultNo);
|
|
96
121
|
});
|
|
97
122
|
});
|
|
98
123
|
}
|
|
99
124
|
|
|
100
125
|
module.exports = {
|
|
126
|
+
DEFAULT_PROMPT_TIMEOUT_MS,
|
|
101
127
|
ITERATE_BANNER,
|
|
102
128
|
printBanner,
|
|
103
129
|
info,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iterate-harness",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.5",
|
|
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",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"lib",
|
|
25
25
|
"scripts",
|
|
26
26
|
"README.md",
|
|
27
|
+
"README.zh-CN.md",
|
|
27
28
|
"LICENSE"
|
|
28
29
|
],
|
|
29
30
|
"keywords": [
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
"installer"
|
|
35
36
|
],
|
|
36
37
|
"scripts": {
|
|
37
|
-
"test": "node --test test/bootstrap.test.js test/postinstall.test.js",
|
|
38
|
+
"test": "node --test test/bootstrap.test.js test/postinstall.test.js test/ui.test.js",
|
|
38
39
|
"postinstall": "node scripts/postinstall.js"
|
|
39
40
|
}
|
|
40
41
|
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -17,9 +17,18 @@
|
|
|
17
17
|
const { ensureRuntime, reportBootstrapFailure, SKIP_INSTALL_ENV_VAR } = require("../lib/bootstrap");
|
|
18
18
|
const ui = require("../lib/ui");
|
|
19
19
|
|
|
20
|
+
// Mirror the truthy-value handling in bootstrap.ensureRuntime so postinstall
|
|
21
|
+
// and the CLI entry point agree on what counts as "skip install" (any of
|
|
22
|
+
// 1/true/yes/on, case-insensitive).
|
|
23
|
+
function skipInstallRequested() {
|
|
24
|
+
return ["1", "true", "yes", "on"].includes(
|
|
25
|
+
String(process.env[SKIP_INSTALL_ENV_VAR] || "").toLowerCase()
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
20
29
|
async function main() {
|
|
21
|
-
if (
|
|
22
|
-
ui.warning(`Skipped install during npm install (${SKIP_INSTALL_ENV_VAR}
|
|
30
|
+
if (skipInstallRequested()) {
|
|
31
|
+
ui.warning(`Skipped install during npm install (${SKIP_INSTALL_ENV_VAR}=${process.env[SKIP_INSTALL_ENV_VAR]}); harness installs on the first \`ih\` run.`);
|
|
23
32
|
return;
|
|
24
33
|
}
|
|
25
34
|
try {
|