mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(skills): read BGM logs without pathname prechecks (#3769)
This commit is contained in:
@@ -34,7 +34,7 @@
|
||||
"files": 11
|
||||
},
|
||||
"hyperframes-core": {
|
||||
"hash": "033a6c675e66a5e7",
|
||||
"hash": "6921c3b1dbeefb30",
|
||||
"files": 20
|
||||
},
|
||||
"hyperframes-creative": {
|
||||
@@ -50,8 +50,8 @@
|
||||
"files": 12
|
||||
},
|
||||
"media-use": {
|
||||
"hash": "5b35f62a2c59f220",
|
||||
"files": 153
|
||||
"hash": "e870f18213f27c8b",
|
||||
"files": 154
|
||||
},
|
||||
"motion-graphics": {
|
||||
"hash": "853ac75cbab69036",
|
||||
@@ -62,12 +62,12 @@
|
||||
"files": 132
|
||||
},
|
||||
"pr-to-video": {
|
||||
"hash": "9ce0b5a7c75e969f",
|
||||
"hash": "e3c6d1527c04a804",
|
||||
"files": 30
|
||||
},
|
||||
"product-launch-video": {
|
||||
"hash": "01256d74e511f163",
|
||||
"files": 29
|
||||
"hash": "71226b200d8adae3",
|
||||
"files": 30
|
||||
},
|
||||
"remotion-to-hyperframes": {
|
||||
"hash": "79fcf8e9641d126e",
|
||||
|
||||
@@ -48,10 +48,14 @@ function isProcessAlive(pid) {
|
||||
}
|
||||
|
||||
function readTail(path, maxChars = 6000) {
|
||||
if (!path || !existsSync(path)) return "";
|
||||
const s = statSync(path);
|
||||
const txt = readFileSync(path, "utf8");
|
||||
return txt.slice(Math.max(0, txt.length - Math.min(maxChars, s.size)));
|
||||
if (!path) return "";
|
||||
try {
|
||||
const txt = readFileSync(path, "utf8");
|
||||
return txt.slice(Math.max(0, txt.length - maxChars));
|
||||
} catch (error) {
|
||||
if (error.code === "ENOENT" || error.code === "ENOTDIR") return "";
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function detectFailure(logTail) {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { test } from "node:test";
|
||||
import { runInNewContext } from "node:vm";
|
||||
|
||||
const source = readFileSync(new URL("./wait-bgm.mjs", import.meta.url), "utf8");
|
||||
const start = source.indexOf("function readTail(");
|
||||
const end = source.indexOf("\nfunction detectFailure(", start);
|
||||
function load(read) {
|
||||
return runInNewContext(`${source.slice(start, end)}; readTail`, {
|
||||
readFileSync: read,
|
||||
existsSync: () => true,
|
||||
statSync: () => {
|
||||
throw new Error("pathname stat must not precede log read");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
test("reads a log once without a separate pathname check", () => {
|
||||
let calls = 0;
|
||||
const read = load((path, encoding) => {
|
||||
calls++;
|
||||
assert.equal(path, "renderer.log");
|
||||
assert.equal(encoding, "utf8");
|
||||
return "prefix\nRuntimeError: failed";
|
||||
});
|
||||
assert.equal(read("renderer.log", 20), "RuntimeError: failed");
|
||||
assert.equal(calls, 1);
|
||||
});
|
||||
|
||||
test("preserves UTF-16 character tail boundaries and zero-length tails", () => {
|
||||
const txt = "🎵日本語".repeat(2000);
|
||||
const read = load(() => txt);
|
||||
assert.equal(read("log"), txt.slice(-6000));
|
||||
assert.equal(read("log", 0), "");
|
||||
});
|
||||
|
||||
for (const code of ["ENOENT", "ENOTDIR"]) {
|
||||
test(`a log removed during polling is absent (${code})`, () => {
|
||||
const read = load(() => {
|
||||
throw Object.assign(new Error(code), { code });
|
||||
});
|
||||
assert.equal(read("log"), "");
|
||||
});
|
||||
}
|
||||
|
||||
test("preserves unexpected read errors", () => {
|
||||
const error = Object.assign(new Error("denied"), { code: "EACCES" });
|
||||
assert.throws(
|
||||
() =>
|
||||
load(() => {
|
||||
throw error;
|
||||
})("log"),
|
||||
(caught) => caught === error,
|
||||
);
|
||||
});
|
||||
|
||||
test("empty log path does not access the filesystem", () => {
|
||||
assert.equal(
|
||||
load(() => {
|
||||
throw new Error("unexpected read");
|
||||
})(""),
|
||||
"",
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user