@taniwhaai/arai 0.2.6 → 0.2.8

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 (2) hide show
  1. package/install.js +60 -0
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -13,7 +13,9 @@ function main() {
13
13
  const version = getVersion();
14
14
  const binaryName = getBinaryDownloadName(platform);
15
15
  const url = `https://github.com/${REPO}/releases/download/v${version}/${binaryName}`;
16
+ const checksumsUrl = `https://github.com/${REPO}/releases/download/v${version}/checksums.txt`;
16
17
  const dest = path.join(__dirname, "bin", getLocalBinaryName());
18
+ const checksumsPath = path.join(__dirname, "bin", "checksums.txt");
17
19
 
18
20
  console.log(` Downloading arai v${version} for ${platform}...`);
19
21
 
@@ -42,6 +44,52 @@ function main() {
42
44
  process.exit(1);
43
45
  }
44
46
 
47
+ // Verify SHA-256 against checksums.txt published with the release.
48
+ // ARAI_SKIP_CHECKSUM=1 escape hatch matches install.sh; intended only for
49
+ // local dev against unsigned builds.
50
+ if (process.env.ARAI_SKIP_CHECKSUM === "1") {
51
+ console.warn(" \u26a0 Skipping checksum verification (ARAI_SKIP_CHECKSUM=1)");
52
+ } else {
53
+ try {
54
+ execSync(`curl -sL --fail -o "${checksumsPath}" "${checksumsUrl}"`, {
55
+ stdio: "pipe",
56
+ });
57
+ } catch (e) {
58
+ console.error(` Failed to fetch checksums.txt from ${checksumsUrl}`);
59
+ console.error(" This release is missing checksums.txt \u2014 refusing to install.");
60
+ console.error(" Set ARAI_SKIP_CHECKSUM=1 to bypass (NOT recommended).");
61
+ fs.unlinkSync(dest);
62
+ process.exit(1);
63
+ }
64
+
65
+ const checksums = fs.readFileSync(checksumsPath, "utf8");
66
+ const expected = parseChecksum(checksums, binaryName);
67
+ if (!expected) {
68
+ console.error(` ${binaryName} not present in checksums.txt`);
69
+ fs.unlinkSync(dest);
70
+ fs.unlinkSync(checksumsPath);
71
+ process.exit(1);
72
+ }
73
+
74
+ const crypto = require("crypto");
75
+ const actual = crypto
76
+ .createHash("sha256")
77
+ .update(fs.readFileSync(dest))
78
+ .digest("hex");
79
+
80
+ if (actual !== expected) {
81
+ console.error(` Checksum mismatch for ${binaryName}`);
82
+ console.error(` expected: ${expected}`);
83
+ console.error(` actual: ${actual}`);
84
+ fs.unlinkSync(dest);
85
+ fs.unlinkSync(checksumsPath);
86
+ process.exit(1);
87
+ }
88
+
89
+ fs.unlinkSync(checksumsPath);
90
+ console.log(` \u2713 Checksum verified`);
91
+ }
92
+
45
93
  // Make executable on Unix
46
94
  if (process.platform !== "win32") {
47
95
  fs.chmodSync(dest, 0o755);
@@ -50,6 +98,18 @@ function main() {
50
98
  console.log(` \u2713 arai v${version} installed`);
51
99
  }
52
100
 
101
+ // Parse a `sha256sum` output line for the given filename. Format is
102
+ // `<64hex> <filename>` per GNU coreutils. Returns the hex digest or null.
103
+ function parseChecksum(content, filename) {
104
+ for (const line of content.split(/\r?\n/)) {
105
+ const match = line.match(/^([a-f0-9]{64})\s+(.+)$/i);
106
+ if (match && match[2].trim() === filename) {
107
+ return match[1].toLowerCase();
108
+ }
109
+ }
110
+ return null;
111
+ }
112
+
53
113
  function detectPlatform() {
54
114
  const platform = os.platform();
55
115
  const arch = os.arch();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taniwhaai/arai",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "AI coding rules that actually work. Enforce instruction files via hooks — CLAUDE.md, .cursorrules, copilot-instructions, and more.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "repository": {