项目文件夹

文件
amis ebc7615414 feat: add external usage snapshot fallback
Allow the HUD to show usage from an opt-in local snapshot when Claude Code omits rate limits, while rejecting stale or malformed data and documenting the fallback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 10:04:40 +10:00

103 行
3.2 KiB
JavaScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { fileURLToPath } from "node:url";
import { spawnSync } from "node:child_process";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { readFileSync } from "node:fs";
function stripAnsi(text) {
return text
.replace(
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nq-uy=><]/g,
"",
)
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "");
}
function skipIfSpawnBlocked(result, t) {
if (result.error?.code === "EPERM") {
t.skip("spawnSync is blocked by sandbox policy in this environment");
return true;
}
return false;
}
test("CLI renders expected output for a basic transcript", async (t) => {
const fixturePath = fileURLToPath(
new URL("./fixtures/transcript-render.jsonl", import.meta.url),
);
const expectedPath = fileURLToPath(
new URL("./fixtures/expected/render-basic.txt", import.meta.url),
);
const expected = readFileSync(expectedPath, "utf8").trimEnd();
const homeDir = await mkdtemp(path.join(tmpdir(), "claude-hud-home-"));
// Use a fixed 3-level path for deterministic test output
const projectDir = path.join(homeDir, "dev", "apps", "my-project");
await import("node:fs/promises").then((fs) =>
fs.mkdir(projectDir, { recursive: true }),
);
try {
const stdin = JSON.stringify({
model: { display_name: "Opus" },
context_window: {
context_window_size: 200000,
current_usage: { input_tokens: 45000 },
},
transcript_path: fixturePath,
cwd: projectDir,
});
const result = spawnSync("node", ["dist/index.js"], {
cwd: path.resolve(process.cwd()),
input: stdin,
encoding: "utf8",
env: { ...process.env, HOME: homeDir, LANG: "C" },
});
if (skipIfSpawnBlocked(result, t)) return;
assert.equal(result.error, undefined, result.error?.message);
assert.equal(result.status, 0, result.stderr || "non-zero exit");
const normalized = stripAnsi(result.stdout)
.replace(/\u00A0/g, " ")
.trimEnd();
if (process.env.UPDATE_SNAPSHOTS === "1") {
await writeFile(expectedPath, normalized + "\n", "utf8");
return;
}
assert.equal(normalized, expected);
} finally {
await rm(homeDir, { recursive: true, force: true });
}
});
test("CLI prints initializing message on empty stdin", async (t) => {
const homeDir = await mkdtemp(path.join(tmpdir(), "claude-hud-home-"));
try {
const result = spawnSync("node", ["dist/index.js"], {
cwd: path.resolve(process.cwd()),
input: "",
encoding: "utf8",
env: { ...process.env, HOME: homeDir, LANG: "C" },
});
if (skipIfSpawnBlocked(result, t)) return;
assert.equal(result.error, undefined, result.error?.message);
assert.equal(result.status, 0, result.stderr || "non-zero exit");
const normalized = stripAnsi(result.stdout)
.replace(/\u00A0/g, " ")
.trimEnd();
assert.ok(
normalized.startsWith("[claude-hud] Initializing..."),
`unexpected output: ${normalized}`,
);
} finally {
await rm(homeDir, { recursive: true, force: true });
}
});