@melaya/runner 1.0.32 → 1.0.34
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/dist/pythonEnv.js +39 -7
- package/package.json +1 -1
package/dist/pythonEnv.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { spawn } from "child_process";
|
|
13
13
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
14
|
+
import { createHash } from "crypto";
|
|
14
15
|
import { join } from "path";
|
|
15
16
|
import { homedir, platform } from "os";
|
|
16
17
|
import chalk from "chalk";
|
|
@@ -86,6 +87,19 @@ export function venvPython() {
|
|
|
86
87
|
? join(VENV_DIR, "Scripts", "python.exe")
|
|
87
88
|
: join(VENV_DIR, "bin", "python");
|
|
88
89
|
}
|
|
90
|
+
// The marker is keyed on sharedVersion + a hash of PIP_DEPS so changes
|
|
91
|
+
// to either invalidate the venv. Without the PIP_DEPS half, a runner
|
|
92
|
+
// update that adds a dep (e.g. scrapling[fetchers] in 1.0.32) would
|
|
93
|
+
// silently skip the reinstall on any box whose sharedVersion hadn't
|
|
94
|
+
// also changed — operators would update the runner but never see the
|
|
95
|
+
// new deps. Hashing the full deps list catches additions, removals,
|
|
96
|
+
// AND version-pin changes within deps.
|
|
97
|
+
function _pipDepsHash() {
|
|
98
|
+
return createHash("sha256").update(JSON.stringify(PIP_DEPS)).digest("hex").slice(0, 12);
|
|
99
|
+
}
|
|
100
|
+
function venvMarkerValue(expectedVersion) {
|
|
101
|
+
return `${expectedVersion}::${_pipDepsHash()}`;
|
|
102
|
+
}
|
|
89
103
|
function venvIsValid(expectedVersion) {
|
|
90
104
|
if (!existsSync(venvPython()))
|
|
91
105
|
return false;
|
|
@@ -93,7 +107,7 @@ function venvIsValid(expectedVersion) {
|
|
|
93
107
|
return false;
|
|
94
108
|
try {
|
|
95
109
|
const cached = readFileSync(VENV_MARK, "utf-8").trim();
|
|
96
|
-
return cached === expectedVersion;
|
|
110
|
+
return cached === venvMarkerValue(expectedVersion);
|
|
97
111
|
}
|
|
98
112
|
catch {
|
|
99
113
|
return false;
|
|
@@ -161,16 +175,34 @@ export async function ensurePythonEnv(systemPython, expectedVersion, onProgress
|
|
|
161
175
|
}
|
|
162
176
|
// Scrapling browser binaries — best-effort. The fast-path AsyncFetcher
|
|
163
177
|
// (curl_cffi TLS impersonation) works without this; only StealthyFetcher
|
|
164
|
-
// (Cloudflare-bypass rescue path) needs Chromium.
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
178
|
+
// (Cloudflare-bypass rescue path) needs Chromium. Scrapling has no
|
|
179
|
+
// __main__.py — invoke its CLI entry point via `from scrapling.cli
|
|
180
|
+
// import main; main(['install'])` so the call works the same on every
|
|
181
|
+
// platform without reaching into the venv's Scripts/bin folder.
|
|
182
|
+
// Non-fatal: if it fails (offline, mirror down, scrapling.cli rename in
|
|
183
|
+
// a future version) the search just stays on the fast path.
|
|
168
184
|
onProgress("installing scrapling browsers (best-effort, for stealth rescue path)");
|
|
169
|
-
const scrCode = await runProc(venvPython(), ["-
|
|
185
|
+
const scrCode = await runProc(venvPython(), ["-c",
|
|
186
|
+
"import sys\n" +
|
|
187
|
+
"# Try every known CLI shape across scrapling versions, return 0 on first success.\n" +
|
|
188
|
+
"tried = []\n" +
|
|
189
|
+
"for mod, attr in (('scrapling.cli','main'), ('scrapling.__main__','main'), ('scrapling.cli','cli')):\n" +
|
|
190
|
+
" try:\n" +
|
|
191
|
+
" m = __import__(mod, fromlist=[attr])\n" +
|
|
192
|
+
" fn = getattr(m, attr)\n" +
|
|
193
|
+
" try: fn(['install'])\n" +
|
|
194
|
+
" except TypeError: fn() # zero-arg variants\n" +
|
|
195
|
+
" sys.exit(0)\n" +
|
|
196
|
+
" except Exception as e:\n" +
|
|
197
|
+
" tried.append(f'{mod}.{attr}: {type(e).__name__}: {e}')\n" +
|
|
198
|
+
" continue\n" +
|
|
199
|
+
"print('scrapling install: no working CLI entry — tried:'); [print(' -', t) for t in tried]\n" +
|
|
200
|
+
"sys.exit(2)\n",
|
|
201
|
+
], onProgress);
|
|
170
202
|
if (scrCode !== 0) {
|
|
171
203
|
onProgress(`(scrapling install exited ${scrCode} — fast-path fetcher still works; stealth-rescue disabled)`);
|
|
172
204
|
}
|
|
173
|
-
writeFileSync(VENV_MARK, expectedVersion, "utf-8");
|
|
205
|
+
writeFileSync(VENV_MARK, venvMarkerValue(expectedVersion), "utf-8");
|
|
174
206
|
// sanity: confirm shortuuid + agentscope (via PYTHONPATH) resolve now.
|
|
175
207
|
// Probe must mirror the spawn env so PYTHONPATH=CACHE_DIR points at
|
|
176
208
|
// the cached agentscope source — without this the probe ImportError's
|