@melaya/runner 1.0.13 → 1.0.14
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/sharedVendor.js +48 -9
- package/package.json +1 -1
package/dist/sharedVendor.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* runtime from the server's /api/v1/agents/shared-bundle endpoint,
|
|
8
8
|
* integrity-verified via SHA-256, and cached at ~/.melaya-runner/shared/.
|
|
9
9
|
*/
|
|
10
|
-
import { mkdirSync, writeFileSync, readFileSync, existsSync } from "fs";
|
|
10
|
+
import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from "fs";
|
|
11
11
|
import { join } from "path";
|
|
12
12
|
import { homedir } from "os";
|
|
13
13
|
import { createHash } from "crypto";
|
|
@@ -21,14 +21,38 @@ export function getSharedDir() {
|
|
|
21
21
|
// and `from crews.X.main import ...` all resolve.
|
|
22
22
|
return CACHE_DIR;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Canary files that prove the current nested bundle layout has been written
|
|
26
|
+
* to disk. A cache where any of these is missing was produced by an older
|
|
27
|
+
* (flat-layout) bundler and must be refetched regardless of version match.
|
|
28
|
+
*/
|
|
29
|
+
const CANARY_FILES = [
|
|
30
|
+
join(CACHE_DIR, "shared", "runtime", "agent_factory.py"),
|
|
31
|
+
join(CACHE_DIR, "shared", "runtime", "__init__.py"),
|
|
32
|
+
join(CACHE_DIR, "shared", "tools", "__init__.py"),
|
|
33
|
+
join(CACHE_DIR, "agentscope", "__init__.py"),
|
|
34
|
+
];
|
|
35
|
+
function cacheLooksValid() {
|
|
36
|
+
return CANARY_FILES.every((p) => existsSync(p));
|
|
37
|
+
}
|
|
24
38
|
export async function ensureSharedModules(serverUrl, expectedVersion) {
|
|
25
39
|
mkdirSync(CACHE_DIR, { recursive: true });
|
|
26
|
-
// Check cached version
|
|
40
|
+
// Check cached version. We require BOTH a version match AND a valid nested
|
|
41
|
+
// layout — older runner versions wrote flat filenames into shared/, and
|
|
42
|
+
// those caches must be purged rather than trusted.
|
|
27
43
|
if (existsSync(VERSION_FILE)) {
|
|
28
44
|
const cached = readFileSync(VERSION_FILE, "utf-8").trim();
|
|
29
|
-
if (cached === expectedVersion &&
|
|
30
|
-
return;
|
|
45
|
+
if (cached === expectedVersion && cacheLooksValid()) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Purge stale cache — flat-layout shared/ or missing agentscope dir.
|
|
50
|
+
// This is safe: we always refetch the full bundle below.
|
|
51
|
+
if (existsSync(SHARED_DIR) && !cacheLooksValid()) {
|
|
52
|
+
try {
|
|
53
|
+
rmSync(SHARED_DIR, { recursive: true, force: true });
|
|
31
54
|
}
|
|
55
|
+
catch { /* best-effort */ }
|
|
32
56
|
}
|
|
33
57
|
// Fetch from server
|
|
34
58
|
const httpUrl = serverUrl.replace(/^wss:/, "https:").replace(/^ws:/, "http:");
|
|
@@ -44,6 +68,7 @@ export async function ensureSharedModules(serverUrl, expectedVersion) {
|
|
|
44
68
|
throw new Error("Shared module integrity check failed — hash mismatch");
|
|
45
69
|
}
|
|
46
70
|
// Write files (sanitize filenames to prevent path traversal)
|
|
71
|
+
const writtenDirs = new Set();
|
|
47
72
|
for (const [filename, content] of Object.entries(body.files)) {
|
|
48
73
|
const safe = filename.replace(/\.\./g, "").replace(/^\//, "").replace(/\\/g, "/");
|
|
49
74
|
if (safe.includes("..") || safe.startsWith("/"))
|
|
@@ -54,13 +79,27 @@ export async function ensureSharedModules(serverUrl, expectedVersion) {
|
|
|
54
79
|
const dir = join(filepath, "..");
|
|
55
80
|
mkdirSync(dir, { recursive: true });
|
|
56
81
|
writeFileSync(filepath, content, "utf-8");
|
|
82
|
+
// Track every ancestor directory under CACHE_DIR so we can ensure each
|
|
83
|
+
// has an __init__.py for Python package resolution.
|
|
84
|
+
let cursor = dir;
|
|
85
|
+
while (cursor.startsWith(CACHE_DIR) && cursor !== CACHE_DIR) {
|
|
86
|
+
writtenDirs.add(cursor);
|
|
87
|
+
cursor = join(cursor, "..");
|
|
88
|
+
}
|
|
57
89
|
}
|
|
58
|
-
// Ensure __init__.py in shared/ and crews
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
90
|
+
// Ensure __init__.py in every nested directory of shared/ and crews/.
|
|
91
|
+
// agentscope ships its own __init__.py files as part of the bundle. This
|
|
92
|
+
// covers cases where the server didn't include an __init__.py for a
|
|
93
|
+
// particular subdir (e.g. accidentally removed) — Python won't treat it
|
|
94
|
+
// as a package without one, breaking imports like `shared.runtime.*`.
|
|
95
|
+
for (const dir of writtenDirs) {
|
|
96
|
+
const rel = dir.slice(CACHE_DIR.length + 1).replace(/\\/g, "/");
|
|
97
|
+
const topLevel = rel.split("/")[0];
|
|
98
|
+
if (topLevel !== "shared" && topLevel !== "crews")
|
|
99
|
+
continue;
|
|
100
|
+
const init = join(dir, "__init__.py");
|
|
101
|
+
if (!existsSync(init))
|
|
62
102
|
writeFileSync(init, "", "utf-8");
|
|
63
|
-
}
|
|
64
103
|
}
|
|
65
104
|
writeFileSync(VERSION_FILE, expectedVersion, "utf-8");
|
|
66
105
|
}
|