mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-06 00:44:07 +01:00
fix(context-bill): --exact refuses a walked file swapped for a FIFO instead of blocking on open
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
b67d9f288f
commit
398ca092df
2 files changed
+34
-2
No files matched your search
+3
-2
@@ -891,13 +891,14 @@ export function exactSendSet(root: string): ExactSendSet {
|
||||
* following a final link, and only if it is still the same regular file at
|
||||
* the same realpath. A link retargeted, a file replaced, or a parent directory
|
||||
* swapped for a link after the walk is refused, so nothing the bill did not
|
||||
* walk can be sent.
|
||||
* walk can be sent. The open does not block, so a file swapped for a FIFO is
|
||||
* refused as not a regular file instead of hanging the run.
|
||||
*/
|
||||
function readWalkedFile(real: string, dev: number, ino: number, file: string, root: string): string {
|
||||
const changed = () => new Error(`${file} changed after it was walked; nothing from ${root} was sent. Run again.`);
|
||||
let fd: number;
|
||||
try {
|
||||
fd = fs.openSync(real, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
||||
fd = fs.openSync(real, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0) | (fs.constants.O_NONBLOCK ?? 0));
|
||||
} catch {
|
||||
throw changed();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
import { describe, it, expect, beforeAll, afterAll } from "bun:test";
|
||||
import { canRevokeWrites } from "./helpers/fs-caps";
|
||||
import { bunIsolationArgs } from "../lib/bun-isolation";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -1194,6 +1195,36 @@ describe("--exact (opt-in measurement; offline here via an injected fetch)", ()
|
||||
expect(sameFileMoved.err).toContain("changed after it was walked");
|
||||
});
|
||||
|
||||
it("refuses a walked file swapped for a FIFO instead of hanging", () => {
|
||||
if (process.platform === "win32") return;
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "context-bill-fifo-"));
|
||||
const skills = path.join(tmp, "skills");
|
||||
fs.mkdirSync(path.join(skills, "qa"), { recursive: true });
|
||||
fs.writeFileSync(path.join(skills, "qa", "SKILL.md"), "---\nname: qa\ndescription: d\n---\n# qa\n");
|
||||
fs.writeFileSync(path.join(skills, "qa", "a.md"), "A");
|
||||
const script = path.join(tmp, "probe.ts");
|
||||
fs.writeFileSync(
|
||||
script,
|
||||
[
|
||||
`import fs from "node:fs";`,
|
||||
`import { spawnSync } from "node:child_process";`,
|
||||
`import { exactSendSet, measureExactTokens } from ${JSON.stringify(path.join(ROOT, "lib", "context-bill.ts"))};`,
|
||||
`const root = ${JSON.stringify(skills)};`,
|
||||
`const sendSet = exactSendSet(root);`,
|
||||
`fs.unlinkSync(${JSON.stringify(path.join(skills, "qa", "a.md"))});`,
|
||||
`spawnSync("mkfifo", [${JSON.stringify(path.join(skills, "qa", "a.md"))}], { timeout: 5_000 });`,
|
||||
`const fetchImpl = (async () => ({ ok: true, json: async () => ({ input_tokens: 9 }) })) as never;`,
|
||||
`try { await measureExactTokens(root, { sendSet, model: "m", apiKey: "sk-test", fetchImpl, egressHome: ${JSON.stringify(tmp)} }); console.log("SENT"); }`,
|
||||
`catch (error) { console.log("REFUSED " + (error as Error).message); }`,
|
||||
].join("\n"),
|
||||
);
|
||||
const result = Bun.spawnSync([process.execPath, ...bunIsolationArgs(), script], { timeout: 15_000 });
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.stdout.toString()).toContain("REFUSED");
|
||||
expect(result.stdout.toString()).toContain("changed after it was walked");
|
||||
});
|
||||
|
||||
it("sends files in the root and the linked gstack checkout, and refuses and names anything else a link reaches", async () => {
|
||||
if (process.platform === "win32") return;
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "context-bill-egress-"));
|
||||
|
||||
Reference in new issue
Block a user