kvguard 0.0.0-stage → 0.1.0

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/bin/kvguard.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ // Runs the native kvguard binary, downloading it first when postinstall was skipped
3
+ // (e.g. `npm install --ignore-scripts`).
4
+ "use strict";
5
+
6
+ const fs = require("node:fs");
7
+ const { spawnSync } = require("node:child_process");
8
+ const { install, BINARY } = require("../install.js");
9
+
10
+ async function main() {
11
+ if (!fs.existsSync(BINARY)) {
12
+ await install();
13
+ }
14
+ const result = spawnSync(BINARY, process.argv.slice(2), { stdio: "inherit" });
15
+ if (result.error) throw result.error;
16
+ process.exit(result.status ?? 1);
17
+ }
18
+
19
+ main().catch((err) => {
20
+ console.error(`kvguard: ${err.message}`);
21
+ process.exit(1);
22
+ });
package/install.js ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ // Downloads the kvguard binary for this platform from GitHub Releases, verifies its SHA-256
3
+ // checksum and unpacks it into ./native. Uses only Node built-ins and the system `tar`.
4
+ "use strict";
5
+
6
+ const crypto = require("node:crypto");
7
+ const fs = require("node:fs");
8
+ const https = require("node:https");
9
+ const os = require("node:os");
10
+ const path = require("node:path");
11
+ const { execFileSync } = require("node:child_process");
12
+
13
+ const REPO = "tranhoangtu-it/kvguard";
14
+ const TARGETS = {
15
+ "linux-x64": "x86_64-unknown-linux-musl",
16
+ "linux-arm64": "aarch64-unknown-linux-musl",
17
+ "darwin-x64": "x86_64-apple-darwin",
18
+ "darwin-arm64": "aarch64-apple-darwin",
19
+ "win32-x64": "x86_64-pc-windows-msvc",
20
+ };
21
+ const MAX_REDIRECTS = 5;
22
+
23
+ const NATIVE_DIR = path.join(__dirname, "native");
24
+ const BINARY = path.join(NATIVE_DIR, process.platform === "win32" ? "kvguard.exe" : "kvguard");
25
+
26
+ function assetName(version) {
27
+ const target = TARGETS[`${process.platform}-${process.arch}`];
28
+ if (!target) {
29
+ throw new Error(`kvguard has no prebuilt binary for ${process.platform}-${process.arch}`);
30
+ }
31
+ const ext = process.platform === "win32" ? "zip" : "tar.gz";
32
+ return `kvguard-v${version}-${target}.${ext}`;
33
+ }
34
+
35
+ function download(url, redirects = 0) {
36
+ return new Promise((resolve, reject) => {
37
+ https
38
+ .get(url, { headers: { "User-Agent": "kvguard-npm-installer" } }, (res) => {
39
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
40
+ res.resume();
41
+ if (redirects >= MAX_REDIRECTS) return reject(new Error(`too many redirects for ${url}`));
42
+ return resolve(download(new URL(res.headers.location, url).toString(), redirects + 1));
43
+ }
44
+ if (res.statusCode !== 200) {
45
+ res.resume();
46
+ return reject(new Error(`GET ${url} failed with HTTP ${res.statusCode}`));
47
+ }
48
+ const chunks = [];
49
+ res.on("data", (c) => chunks.push(c));
50
+ res.on("end", () => resolve(Buffer.concat(chunks)));
51
+ res.on("error", reject);
52
+ })
53
+ .on("error", reject);
54
+ });
55
+ }
56
+
57
+ async function install() {
58
+ const { version } = require("./package.json");
59
+ const asset = assetName(version);
60
+ const base = `https://github.com/${REPO}/releases/download/v${version}`;
61
+ const [archive, checksumFile] = await Promise.all([
62
+ download(`${base}/${asset}`),
63
+ download(`${base}/${asset}.sha256`),
64
+ ]);
65
+
66
+ const expected = checksumFile.toString("utf8").trim().split(/\s+/)[0].toLowerCase();
67
+ const actual = crypto.createHash("sha256").update(archive).digest("hex");
68
+ if (expected !== actual) {
69
+ throw new Error(`checksum mismatch for ${asset}: expected ${expected}, got ${actual}`);
70
+ }
71
+
72
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "kvguard-"));
73
+ try {
74
+ const archivePath = path.join(tmp, asset);
75
+ fs.writeFileSync(archivePath, archive);
76
+ fs.mkdirSync(NATIVE_DIR, { recursive: true });
77
+ // bsdtar (Windows 10+, macOS) and GNU tar both read .tar.gz; bsdtar also reads .zip.
78
+ execFileSync("tar", ["-xf", archivePath, "-C", NATIVE_DIR], { stdio: "inherit" });
79
+ } finally {
80
+ fs.rmSync(tmp, { recursive: true, force: true });
81
+ }
82
+ if (process.platform !== "win32") fs.chmodSync(BINARY, 0o755);
83
+ return BINARY;
84
+ }
85
+
86
+ module.exports = { install, BINARY };
87
+
88
+ if (require.main === module) {
89
+ install()
90
+ .then((bin) => console.log(`kvguard installed at ${bin}`))
91
+ .catch((err) => {
92
+ console.error(`kvguard: install failed: ${err.message}`);
93
+ process.exit(1);
94
+ });
95
+ }
package/package.json CHANGED
@@ -1,6 +1,23 @@
1
1
  {
2
2
  "name": "kvguard",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Local proxy & TUI that catches 1-token prompt/KV cache prefix misses and auto-heals requests for Anthropic, OpenAI, llama.cpp and Ollama.",
5
+ "license": "MIT OR Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/tranhoangtu-it/kvguard.git"
9
+ },
10
+ "keywords": ["llm", "prompt-caching", "kv-cache", "proxy", "claude", "openai", "llama.cpp", "ollama"],
11
+ "bin": {
12
+ "kvguard": "bin/kvguard.js"
13
+ },
14
+ "scripts": {
15
+ "postinstall": "node install.js"
16
+ },
17
+ "files": ["bin/", "install.js"],
18
+ "engines": {
19
+ "node": ">=18"
20
+ },
21
+ "os": ["darwin", "linux", "win32"],
22
+ "cpu": ["x64", "arm64"]
23
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.