keen-code 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.
Files changed (3) hide show
  1. package/bin/keen.js +22 -0
  2. package/package.json +23 -0
  3. package/postinstall.js +113 -0
package/bin/keen.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ // Shim: locates the downloaded keen binary and passes through all arguments.
3
+
4
+ const { execFileSync } = require("child_process");
5
+ const path = require("path");
6
+ const fs = require("fs");
7
+
8
+ const binaryName = process.platform === "win32" ? "keen.exe" : "keen";
9
+ const binaryPath = path.join(__dirname, binaryName);
10
+
11
+ if (!fs.existsSync(binaryPath)) {
12
+ console.error(
13
+ "[keen] Binary not found. Try reinstalling: npm install -g keen-code"
14
+ );
15
+ process.exit(1);
16
+ }
17
+
18
+ try {
19
+ execFileSync(binaryPath, process.argv.slice(2), { stdio: "inherit" });
20
+ } catch (err) {
21
+ process.exit(err.status ?? 1);
22
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "keen-code",
3
+ "version": "0.1.0",
4
+ "description": "CLI coding agent powered by AI (npm compatibility wrapper)",
5
+ "bin": {
6
+ "keen": "bin/keen.js"
7
+ },
8
+ "scripts": {
9
+ "postinstall": "node postinstall.js"
10
+ },
11
+ "files": [
12
+ "bin/",
13
+ "postinstall.js"
14
+ ],
15
+ "engines": {
16
+ "node": ">=16"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/mochow13/keen-code/"
21
+ },
22
+ "license": "MIT"
23
+ }
package/postinstall.js ADDED
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ // Downloads the keen binary for the current platform during npm install.
3
+
4
+ const https = require("https");
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+ const crypto = require("crypto");
8
+ const { execFileSync } = require("child_process");
9
+
10
+ const REPO = "keen-code/keen-code";
11
+ const VERSION = require("./package.json").version;
12
+
13
+ const PLATFORM_MAP = {
14
+ darwin: "darwin",
15
+ linux: "linux",
16
+ win32: "windows",
17
+ };
18
+
19
+ const ARCH_MAP = {
20
+ x64: "amd64",
21
+ arm64: "arm64",
22
+ };
23
+
24
+ function fail(msg) {
25
+ console.error(`[keen] ${msg}`);
26
+ process.exit(1);
27
+ }
28
+
29
+ const platform = PLATFORM_MAP[process.platform];
30
+ if (!platform) fail(`Unsupported platform: ${process.platform}`);
31
+
32
+ const arch = ARCH_MAP[process.arch];
33
+ if (!arch) fail(`Unsupported architecture: ${process.arch}`);
34
+
35
+ const ext = platform === "windows" ? "zip" : "tar.gz";
36
+ const archiveName = `keen_${VERSION}_${platform}_${arch}.${ext}`;
37
+ const baseURL = `https://github.com/${REPO}/releases/download/v${VERSION}`;
38
+ const archiveURL = `${baseURL}/${archiveName}`;
39
+ const checksumsURL = `${baseURL}/checksums.txt`;
40
+
41
+ const binDir = path.join(__dirname, "bin");
42
+ const binaryName = platform === "windows" ? "keen.exe" : "keen";
43
+ const binaryPath = path.join(binDir, binaryName);
44
+
45
+ if (!fs.existsSync(binDir)) fs.mkdirSync(binDir, { recursive: true });
46
+
47
+ function download(url) {
48
+ return new Promise((resolve, reject) => {
49
+ const chunks = [];
50
+ const get = (u) =>
51
+ https.get(u, { headers: { "User-Agent": "keen-code-npm-installer" } }, (res) => {
52
+ if (res.statusCode === 301 || res.statusCode === 302) {
53
+ get(res.headers.location);
54
+ return;
55
+ }
56
+ if (res.statusCode !== 200) {
57
+ reject(new Error(`HTTP ${res.statusCode} for ${u}`));
58
+ return;
59
+ }
60
+ res.on("data", (c) => chunks.push(c));
61
+ res.on("end", () => resolve(Buffer.concat(chunks)));
62
+ res.on("error", reject);
63
+ });
64
+ get(url);
65
+ });
66
+ }
67
+
68
+ function verifySHA256(buf, expected) {
69
+ const actual = crypto.createHash("sha256").update(buf).digest("hex");
70
+ if (actual !== expected) {
71
+ fail(`Checksum mismatch: expected ${expected}, got ${actual}`);
72
+ }
73
+ }
74
+
75
+ function extractBinary(archiveBuf) {
76
+ const tmpArchive = path.join(binDir, archiveName);
77
+ fs.writeFileSync(tmpArchive, archiveBuf);
78
+
79
+ if (ext === "tar.gz") {
80
+ execFileSync("tar", ["-xzf", tmpArchive, "-C", binDir, binaryName]);
81
+ } else {
82
+ // windows zip
83
+ execFileSync("powershell", [
84
+ "-Command",
85
+ `Expand-Archive -Path '${tmpArchive}' -DestinationPath '${binDir}' -Force`,
86
+ ]);
87
+ }
88
+
89
+ fs.unlinkSync(tmpArchive);
90
+ fs.chmodSync(binaryPath, 0o755);
91
+ }
92
+
93
+ async function main() {
94
+ console.log(`[keen] Downloading keen v${VERSION} for ${platform}/${arch}...`);
95
+
96
+ const [archiveBuf, checksumsBuf] = await Promise.all([
97
+ download(archiveURL),
98
+ download(checksumsURL),
99
+ ]);
100
+
101
+ const checksums = checksumsBuf.toString("utf8");
102
+ const match = checksums.split("\n").find((line) => line.includes(archiveName));
103
+ if (!match) fail(`No checksum entry found for ${archiveName}`);
104
+
105
+ const expectedHash = match.trim().split(/\s+/)[0];
106
+ verifySHA256(archiveBuf, expectedHash);
107
+ console.log("[keen] Checksum verified.");
108
+
109
+ extractBinary(archiveBuf);
110
+ console.log(`[keen] Installed to ${binaryPath}`);
111
+ }
112
+
113
+ main().catch((err) => fail(err.message));