@melaya/runner 1.1.77 → 1.1.79
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 +23 -4
- package/dist/sharedVendor.d.ts +2 -0
- package/dist/sharedVendor.js +61 -7
- package/package.json +50 -50
package/dist/pythonEnv.js
CHANGED
|
@@ -265,7 +265,10 @@ const PIP_DEPS = [
|
|
|
265
265
|
"segno", // qr_tools (QR generation)
|
|
266
266
|
"PyGithub", // github tool (imports as `github`)
|
|
267
267
|
"python-gitlab", // github tool (imports as `gitlab`)
|
|
268
|
-
|
|
268
|
+
// pandas-ta publishes for Python >= 3.12 only: on a 3.10 / 3.11 host an unmarked
|
|
269
|
+
// entry fails the WHOLE venv install (Mac on Homebrew 3.11, 2026-10-29). The marker
|
|
270
|
+
// skips it there; alphavantage_tools imports it lazily and degrades without it.
|
|
271
|
+
'pandas-ta; python_version >= "3.12"', // alphavantage_tools (TA indicators; pandas already present)
|
|
269
272
|
"python-telegram-bot", // messaging (bot-API rich path; pulls httpx, already present)
|
|
270
273
|
"aiomysql", // database (async MySQL; sync PyMySQL already present)
|
|
271
274
|
];
|
|
@@ -580,12 +583,26 @@ async function _ensurePythonEnvImpl(systemPython, expectedVersion, onProgress =
|
|
|
580
583
|
// blocked by a uv edge case.
|
|
581
584
|
const _uvBin = _resolveUvBin();
|
|
582
585
|
let acode;
|
|
586
|
+
// Always in the runner log (onProgress is silent outside verbose mode): a deps
|
|
587
|
+
// change reinstalls every runner's venv once, and nobody could tell it happened,
|
|
588
|
+
// how long it took or why it failed (Mac, 2026-10-30).
|
|
589
|
+
const installStart = Date.now();
|
|
590
|
+
const installTail = [];
|
|
591
|
+
const installOut = (line) => {
|
|
592
|
+
installTail.push(line);
|
|
593
|
+
if (installTail.length > 12)
|
|
594
|
+
installTail.shift();
|
|
595
|
+
onProgress(line);
|
|
596
|
+
};
|
|
597
|
+
console.log(` python venv: installing ${PIP_DEPS.length} deps with ${_uvBin ? "uv" : "pip"} (deps changed)`);
|
|
583
598
|
if (_uvBin) {
|
|
584
599
|
onProgress(`installing ${PIP_DEPS.length} python deps via uv (fast; cached reinstalls take seconds)`);
|
|
585
|
-
acode = await runProc(_uvBin, ["pip", "install", "--python", venvPython(), "--quiet", ...PIP_DEPS],
|
|
600
|
+
acode = await runProc(_uvBin, ["pip", "install", "--python", venvPython(), "--quiet", ...PIP_DEPS], installOut, { UV_SYSTEM_CERTS: "1" });
|
|
586
601
|
if (acode !== 0) {
|
|
587
602
|
onProgress("uv install failed — falling back to pip (--no-cache-dir)");
|
|
588
|
-
|
|
603
|
+
console.warn(` python venv: uv failed (exit ${acode}), falling back to pip:\n ${installTail.join("\n ")}`);
|
|
604
|
+
installTail.length = 0;
|
|
605
|
+
acode = await runProc(venvPython(), ["-m", "pip", "install", "--use-feature=truststore", "--no-cache-dir", "--quiet", "--disable-pip-version-check", ...PIP_DEPS], installOut);
|
|
589
606
|
}
|
|
590
607
|
}
|
|
591
608
|
else {
|
|
@@ -596,9 +613,10 @@ async function _ensurePythonEnvImpl(systemPython, expectedVersion, onProgress =
|
|
|
596
613
|
// importantly, guarantees a corrupt cached wheel can never install a subtly
|
|
597
614
|
// broken package into a fresh runner venv. Costs a re-download on rebuild,
|
|
598
615
|
// which is rare (only on a deps change or a manual clear).
|
|
599
|
-
acode = await runProc(venvPython(), ["-m", "pip", "install", "--use-feature=truststore", "--no-cache-dir", "--quiet", "--disable-pip-version-check", ...PIP_DEPS],
|
|
616
|
+
acode = await runProc(venvPython(), ["-m", "pip", "install", "--use-feature=truststore", "--no-cache-dir", "--quiet", "--disable-pip-version-check", ...PIP_DEPS], installOut);
|
|
600
617
|
}
|
|
601
618
|
if (acode !== 0) {
|
|
619
|
+
console.warn(` python venv: install failed (exit ${acode}) after ${Math.round((Date.now() - installStart) / 1000)} s:\n ${installTail.join("\n ")}`);
|
|
602
620
|
return {
|
|
603
621
|
ok: false,
|
|
604
622
|
pythonPath: systemPython,
|
|
@@ -682,5 +700,6 @@ async function _ensurePythonEnvImpl(systemPython, expectedVersion, onProgress =
|
|
|
682
700
|
}
|
|
683
701
|
writeFileSync(VENV_MARK, venvMarkerValue(expectedVersion), "utf-8");
|
|
684
702
|
onProgress(`✓ venv ready (python=${venvPython()})`);
|
|
703
|
+
console.log(` python venv: ready in ${Math.round((Date.now() - installStart) / 1000)} s (python=${venvPython()})`);
|
|
685
704
|
return { ok: true, pythonPath: venvPython() };
|
|
686
705
|
}
|
package/dist/sharedVendor.d.ts
CHANGED
|
@@ -16,3 +16,5 @@
|
|
|
16
16
|
export declare function getLocalSharedVersion(): string | null;
|
|
17
17
|
export declare function getSharedDir(): string;
|
|
18
18
|
export declare function ensureSharedModules(serverUrl: string, expectedVersion: string, runnerToken: string): Promise<void>;
|
|
19
|
+
/** No data for this long aborts the download (a slow one keeps going). */
|
|
20
|
+
export declare const SHARED_STALL_MS = 30000;
|
package/dist/sharedVendor.js
CHANGED
|
@@ -68,6 +68,48 @@ export function ensureSharedModules(serverUrl, expectedVersion, runnerToken) {
|
|
|
68
68
|
_sharedInFlight.set(expectedVersion, p);
|
|
69
69
|
return p;
|
|
70
70
|
}
|
|
71
|
+
/** No data for this long aborts the download (a slow one keeps going). */
|
|
72
|
+
export const SHARED_STALL_MS = 30_000;
|
|
73
|
+
/** Hard ceiling for one download, however slowly it trickles. */
|
|
74
|
+
const SHARED_MAX_MS = 5 * 60_000;
|
|
75
|
+
/** GET the bundle (gzip accepted: about 4 MB instead of 24), aborting only on a stall. */
|
|
76
|
+
async function fetchSharedBundle(url, runnerToken) {
|
|
77
|
+
const ac = new AbortController();
|
|
78
|
+
let stall;
|
|
79
|
+
const arm = () => {
|
|
80
|
+
if (stall)
|
|
81
|
+
clearTimeout(stall);
|
|
82
|
+
stall = setTimeout(() => ac.abort(new Error(`no data for ${SHARED_STALL_MS / 1000} s`)), SHARED_STALL_MS);
|
|
83
|
+
};
|
|
84
|
+
const ceiling = setTimeout(() => ac.abort(new Error(`not done after ${SHARED_MAX_MS / 1000} s`)), SHARED_MAX_MS);
|
|
85
|
+
arm();
|
|
86
|
+
try {
|
|
87
|
+
const res = await fetch(url, {
|
|
88
|
+
headers: { Authorization: `Bearer ${runnerToken}`, "Accept-Encoding": "gzip" },
|
|
89
|
+
signal: ac.signal,
|
|
90
|
+
});
|
|
91
|
+
if (!res.ok)
|
|
92
|
+
throw new Error(`Failed to fetch shared modules: HTTP ${res.status}`);
|
|
93
|
+
if (!res.body)
|
|
94
|
+
throw new Error("Failed to fetch shared modules: empty response");
|
|
95
|
+
const reader = res.body.getReader();
|
|
96
|
+
const chunks = [];
|
|
97
|
+
for (;;) {
|
|
98
|
+
const { done, value } = await reader.read();
|
|
99
|
+
if (done)
|
|
100
|
+
break;
|
|
101
|
+
if (value)
|
|
102
|
+
chunks.push(value);
|
|
103
|
+
arm();
|
|
104
|
+
}
|
|
105
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf-8"));
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
if (stall)
|
|
109
|
+
clearTimeout(stall);
|
|
110
|
+
clearTimeout(ceiling);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
71
113
|
async function _ensureSharedModulesImpl(serverUrl, expectedVersion, runnerToken) {
|
|
72
114
|
mkdirSync(CACHE_DIR, { recursive: true });
|
|
73
115
|
// Check cached version. We require BOTH a version match AND a valid nested
|
|
@@ -90,14 +132,26 @@ async function _ensureSharedModulesImpl(serverUrl, expectedVersion, runnerToken)
|
|
|
90
132
|
// Fetch from server
|
|
91
133
|
const httpUrl = serverUrl.replace(/^wss:/, "https:").replace(/^ws:/, "http:");
|
|
92
134
|
const url = `${httpUrl}/api/v1/runner/shared-bundle?v=${expectedVersion}`;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
135
|
+
// Right after a deploy every runner downloads the new bundle at once: a slow but
|
|
136
|
+
// live download must not fail the run (run 3751301796504315, 2026-10-30, hit the
|
|
137
|
+
// old flat 30 s limit). Abort only when no data arrives for SHARED_STALL_MS, retry
|
|
138
|
+
// once, and if both fail while a previous bundle is still valid on disk, run with
|
|
139
|
+
// it and say so (the next run fetches the new one).
|
|
140
|
+
let body;
|
|
141
|
+
try {
|
|
142
|
+
body = await fetchSharedBundle(url, runnerToken).catch(async (e) => {
|
|
143
|
+
console.warn(` ⚠ shared modules ${expectedVersion}: download failed (${e?.message}), retrying once`);
|
|
144
|
+
return fetchSharedBundle(url, runnerToken);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
if (existsSync(VERSION_FILE) && cacheLooksValid()) {
|
|
149
|
+
const cached = readFileSync(VERSION_FILE, "utf-8").trim();
|
|
150
|
+
console.warn(` ⚠ shared modules ${expectedVersion}: download failed again (${e?.message}); running with the cached ${cached}`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
throw e;
|
|
99
154
|
}
|
|
100
|
-
const body = await res.json();
|
|
101
155
|
// Verify integrity
|
|
102
156
|
const computed = createHash("sha256").update(JSON.stringify(body.files)).digest("hex");
|
|
103
157
|
if (computed !== body.hash) {
|
package/package.json
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@melaya/runner",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
|
|
5
|
-
"license": "Apache-2.0",
|
|
6
|
-
"author": "Melaya Labs LLC",
|
|
7
|
-
"private": false,
|
|
8
|
-
"type": "module",
|
|
9
|
-
"bin": {
|
|
10
|
-
"melaya-runner": "dist/cli.js"
|
|
11
|
-
},
|
|
12
|
-
"main": "dist/index.js",
|
|
13
|
-
"files": [
|
|
14
|
-
"dist/**/*.js",
|
|
15
|
-
"dist/**/*.d.ts",
|
|
16
|
-
"dist/**/*.py",
|
|
17
|
-
"localRagIngest.py",
|
|
18
|
-
"localRagRetrieve.py",
|
|
19
|
-
"localDbProbe.py",
|
|
20
|
-
"localBacklinkFetch.py",
|
|
21
|
-
"localVault.py",
|
|
22
|
-
"localVaultMirror.py",
|
|
23
|
-
"nltk_data/**",
|
|
24
|
-
"README.md",
|
|
25
|
-
"LICENSE"
|
|
26
|
-
],
|
|
27
|
-
"scripts": {
|
|
28
|
-
"build": "tsc && node -e \"const fs=require('fs'); fs.copyFileSync('localRagIngest.py','dist/localRagIngest.py'); fs.copyFileSync('localRagRetrieve.py','dist/localRagRetrieve.py'); fs.copyFileSync('localDbProbe.py','dist/localDbProbe.py'); fs.copyFileSync('localBacklinkFetch.py','dist/localBacklinkFetch.py'); fs.copyFileSync('localVault.py','dist/localVault.py'); fs.copyFileSync('localVaultMirror.py','dist/localVaultMirror.py'); fs.copyFileSync('src/assistantHost.py','dist/assistantHost.py')\"",
|
|
29
|
-
"test": "node --test --import tsx \"src/**/*.test.ts\"",
|
|
30
|
-
"prepublishOnly": "npm run build"
|
|
31
|
-
},
|
|
32
|
-
"dependencies": {
|
|
33
|
-
"chalk": "^5.3.0",
|
|
34
|
-
"commander": "^12.0.0",
|
|
35
|
-
"ora": "^8.0.0",
|
|
36
|
-
"playwright": "^1.48.0",
|
|
37
|
-
"socket.io-client": "^4.8.0"
|
|
38
|
-
},
|
|
39
|
-
"devDependencies": {
|
|
40
|
-
"@types/node": "^20.0.0",
|
|
41
|
-
"tsx": "^4.19.2",
|
|
42
|
-
"typescript": "^5.5.0"
|
|
43
|
-
},
|
|
44
|
-
"engines": {
|
|
45
|
-
"node": ">=18"
|
|
46
|
-
},
|
|
47
|
-
"publishConfig": {
|
|
48
|
-
"access": "public"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@melaya/runner",
|
|
3
|
+
"version": "1.1.79",
|
|
4
|
+
"description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Melaya Labs LLC",
|
|
7
|
+
"private": false,
|
|
8
|
+
"type": "module",
|
|
9
|
+
"bin": {
|
|
10
|
+
"melaya-runner": "dist/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/**/*.js",
|
|
15
|
+
"dist/**/*.d.ts",
|
|
16
|
+
"dist/**/*.py",
|
|
17
|
+
"localRagIngest.py",
|
|
18
|
+
"localRagRetrieve.py",
|
|
19
|
+
"localDbProbe.py",
|
|
20
|
+
"localBacklinkFetch.py",
|
|
21
|
+
"localVault.py",
|
|
22
|
+
"localVaultMirror.py",
|
|
23
|
+
"nltk_data/**",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc && node -e \"const fs=require('fs'); fs.copyFileSync('localRagIngest.py','dist/localRagIngest.py'); fs.copyFileSync('localRagRetrieve.py','dist/localRagRetrieve.py'); fs.copyFileSync('localDbProbe.py','dist/localDbProbe.py'); fs.copyFileSync('localBacklinkFetch.py','dist/localBacklinkFetch.py'); fs.copyFileSync('localVault.py','dist/localVault.py'); fs.copyFileSync('localVaultMirror.py','dist/localVaultMirror.py'); fs.copyFileSync('src/assistantHost.py','dist/assistantHost.py')\"",
|
|
29
|
+
"test": "node --test --import tsx \"src/**/*.test.ts\"",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"chalk": "^5.3.0",
|
|
34
|
+
"commander": "^12.0.0",
|
|
35
|
+
"ora": "^8.0.0",
|
|
36
|
+
"playwright": "^1.48.0",
|
|
37
|
+
"socket.io-client": "^4.8.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^20.0.0",
|
|
41
|
+
"tsx": "^4.19.2",
|
|
42
|
+
"typescript": "^5.5.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
}
|
|
50
|
+
}
|