fix(skills): read BGM logs without pathname prechecks (#3769)

This commit is contained in:
James Russo
2026-09-08 16:40:20 -04:00
committed by GitHub
parent d66cd6dcc8
commit a92dc6865e
3 changed files with 79 additions and 10 deletions
+6 -6
View File
@@ -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",
+8 -4
View File
@@ -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");
})(""),
"",
);
});