@scovant/core 0.1.0 → 0.1.1
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/bin/scovant.js +57 -14
- package/package.json +1 -1
package/bin/scovant.js
CHANGED
|
@@ -28,6 +28,10 @@ const TESTING = process.env.SCOVANT_NPM_TEST === '1';
|
|
|
28
28
|
// missing tool: reporting "install uv" to someone whose uv is installed and
|
|
29
29
|
// merely slow is false. Track the two apart so the no-runner message can.
|
|
30
30
|
let timedOut = false;
|
|
31
|
+
// Every python3.NN candidate that answered the probe with the WRONG engine
|
|
32
|
+
// version (only populated once the loop in resolveRunner has exhausted
|
|
33
|
+
// every candidate with no match) — see mismatchExhaustedMessage.
|
|
34
|
+
let versionMismatches = [];
|
|
31
35
|
|
|
32
36
|
function probe(cmd, args, opts = PROBE) {
|
|
33
37
|
const r = spawnSync(cmd, args, opts);
|
|
@@ -64,21 +68,43 @@ function existsOnPath(cmd) {
|
|
|
64
68
|
// that for free — they resolve the pinned SPEC. The `python3 -m
|
|
65
69
|
// scovant_core` path runs whatever that interpreter happens to have, so the
|
|
66
70
|
// guarantee only holds if we check it: parse the version the probe printed
|
|
67
|
-
// and compare.
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
|
|
71
|
+
// and compare. Scores are only comparable within one ruleset version, so a
|
|
72
|
+
// mismatch here is not a warning-and-continue situation — it is a launcher
|
|
73
|
+
// that is about to run a different engine than the one it claims to be —
|
|
74
|
+
// and this fails CLOSED (exit 9), naming BOTH versions on stderr, unless
|
|
75
|
+
// the caller explicitly opts into the old warn-and-continue behavior via
|
|
76
|
+
// SCOVANT_ALLOW_VERSION_MISMATCH=1.
|
|
77
|
+
// Returns the version string the probe reported, or null if `stdout`
|
|
78
|
+
// didn't carry a recognizable one.
|
|
79
|
+
function extractVersion(stdout) {
|
|
73
80
|
const m = /scovant-core\s+(\S+)/.exec(stdout || '');
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
return m ? m[1] : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function mismatchWarning(cmd, found) {
|
|
76
85
|
const what = found ? `version ${found}` : 'an unknown version';
|
|
77
|
-
|
|
78
|
-
`scovant: warning —
|
|
86
|
+
return (
|
|
87
|
+
`scovant: warning — scovant: engine version mismatch — "${cmd} -m scovant_core" runs scovant-core ${what}, ` +
|
|
79
88
|
`but this launcher is @scovant/core ${version}.\n` +
|
|
80
|
-
`
|
|
81
|
-
`
|
|
89
|
+
`Scores are only comparable within one ruleset version, so the launcher refuses to run a different engine.\n` +
|
|
90
|
+
`Fix: install uv or pipx (they pin "${SPEC}"), or "${cmd} -m pip install \\"${SPEC}\\"".\n`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Every candidate that answered the probe but carried the wrong version —
|
|
95
|
+
// a mismatched EARLIER candidate must not abort the whole loop when a
|
|
96
|
+
// LATER one carries the exact version, so this is only consulted once the
|
|
97
|
+
// loop has exhausted every candidate with no match.
|
|
98
|
+
function mismatchExhaustedMessage(tried) {
|
|
99
|
+
const lines = tried.map(({ cmd, found }) => ` - "${cmd} -m scovant_core --version" reported ${found ? `version ${found}` : 'an unknown version'}`);
|
|
100
|
+
return (
|
|
101
|
+
`scovant: engine version mismatch — every Python interpreter tried on PATH runs a different\n` +
|
|
102
|
+
`scovant-core version than this launcher (@scovant/core ${version}):\n` +
|
|
103
|
+
lines.join('\n') + '\n' +
|
|
104
|
+
`Scores are only comparable within one ruleset version, so the launcher refuses to run a different engine.\n` +
|
|
105
|
+
`Fix: install uv or pipx (they pin "${SPEC}"), or "python3 -m pip install \\"${SPEC}\\"".\n` +
|
|
106
|
+
`Override (not recommended): SCOVANT_ALLOW_VERSION_MISMATCH=1\n`
|
|
107
|
+
);
|
|
82
108
|
}
|
|
83
109
|
|
|
84
110
|
function resolveRunner() {
|
|
@@ -105,18 +131,35 @@ function resolveRunner() {
|
|
|
105
131
|
// and still fail to execute as a module; a probe that only checks import
|
|
106
132
|
// would hand back a runner that crashes on the real invocation instead of
|
|
107
133
|
// reporting "no runner found".
|
|
134
|
+
// A mismatched EARLIER candidate must not abort the probe: keep trying
|
|
135
|
+
// later candidates, and only report failure once every one of them has
|
|
136
|
+
// been tried and none matched.
|
|
137
|
+
const mismatches = [];
|
|
108
138
|
for (const py of ['python3.13', 'python3.12', 'python3']) {
|
|
109
139
|
const r = probe(py, ['-m', 'scovant_core', '--version'], PROBE_CAPTURE);
|
|
110
|
-
if (r.ok)
|
|
111
|
-
|
|
140
|
+
if (!r.ok) continue;
|
|
141
|
+
const found = extractVersion(r.stdout);
|
|
142
|
+
if (found === version) {
|
|
112
143
|
return { cmd: py, pre: ['-m', 'scovant_core'] };
|
|
113
144
|
}
|
|
145
|
+
if (process.env.SCOVANT_ALLOW_VERSION_MISMATCH === '1') {
|
|
146
|
+
process.stderr.write(mismatchWarning(py, found));
|
|
147
|
+
return { cmd: py, pre: ['-m', 'scovant_core'] };
|
|
148
|
+
}
|
|
149
|
+
mismatches.push({ cmd: py, found });
|
|
150
|
+
}
|
|
151
|
+
if (mismatches.length) {
|
|
152
|
+
versionMismatches = mismatches;
|
|
114
153
|
}
|
|
115
154
|
return null;
|
|
116
155
|
}
|
|
117
156
|
|
|
118
157
|
const runner = resolveRunner();
|
|
119
158
|
if (!runner) {
|
|
159
|
+
if (versionMismatches.length) {
|
|
160
|
+
process.stderr.write(mismatchExhaustedMessage(versionMismatches));
|
|
161
|
+
process.exit(9);
|
|
162
|
+
}
|
|
120
163
|
if (timedOut) {
|
|
121
164
|
process.stderr.write(
|
|
122
165
|
`scovant: a runner was found but its probe timed out, so nothing could be started.\n\n` +
|