archgate 0.39.0 → 0.41.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/package.json +7 -6
- package/{bin → shims/npm}/archgate.cjs +96 -29
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archgate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "Enforce Architecture Decision Records as executable rules — for both humans and AI agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adr",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"url": "git+https://github.com/archgate/cli.git"
|
|
25
25
|
},
|
|
26
26
|
"bin": {
|
|
27
|
-
"archgate": "
|
|
27
|
+
"archgate": "shims/npm/archgate.cjs"
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
|
-
"
|
|
30
|
+
"shims/npm/archgate.cjs"
|
|
31
31
|
],
|
|
32
32
|
"os": [
|
|
33
33
|
"darwin",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"docs:preview": "cd docs && bunx --bun astro preview",
|
|
47
47
|
"format": "oxfmt --write .",
|
|
48
48
|
"format:check": "oxfmt --check .",
|
|
49
|
+
"format:file": "oxfmt --write",
|
|
49
50
|
"knip": "knip",
|
|
50
51
|
"lint": "oxlint --deny-warnings .",
|
|
51
52
|
"test": "bun test --timeout 60000",
|
|
@@ -66,12 +67,12 @@
|
|
|
66
67
|
"conventional-changelog-angular": "8.3.1",
|
|
67
68
|
"czg": "1.13.1",
|
|
68
69
|
"fast-check": "4.8.0",
|
|
69
|
-
"inquirer": "
|
|
70
|
-
"knip": "6.14.
|
|
70
|
+
"inquirer": "14.0.0",
|
|
71
|
+
"knip": "6.14.2",
|
|
71
72
|
"meriyah": "7.1.0",
|
|
72
73
|
"oxfmt": "0.51.0",
|
|
73
74
|
"oxlint": "1.66.0",
|
|
74
|
-
"posthog-node": "5.35.
|
|
75
|
+
"posthog-node": "5.35.2",
|
|
75
76
|
"zod": "4.4.3"
|
|
76
77
|
},
|
|
77
78
|
"peerDependencies": {
|
|
@@ -2,24 +2,32 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
const { execFileSync, execSync } = require("child_process");
|
|
5
|
+
const crypto = require("crypto");
|
|
5
6
|
const https = require("https");
|
|
6
7
|
const zlib = require("zlib");
|
|
7
8
|
const path = require("path");
|
|
8
9
|
const fs = require("fs");
|
|
9
10
|
const os = require("os");
|
|
10
11
|
|
|
11
|
-
function getArtifactName() {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
if (
|
|
15
|
-
if (
|
|
12
|
+
function getArtifactName(platform, arch) {
|
|
13
|
+
const p = platform || process.platform;
|
|
14
|
+
const a = arch || process.arch;
|
|
15
|
+
if (p === "darwin" && a === "arm64") return "archgate-darwin-arm64";
|
|
16
|
+
if (p === "linux" && a === "x64") return "archgate-linux-x64";
|
|
17
|
+
if (p === "win32" && a === "x64") return "archgate-win32-x64";
|
|
16
18
|
throw new Error(
|
|
17
|
-
`Unsupported platform: ${
|
|
19
|
+
`Unsupported platform: ${p}/${a}\narchgate supports darwin/arm64, linux/x64, and win32/x64.`
|
|
18
20
|
);
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
function getBinaryName() {
|
|
22
|
-
return process.platform === "win32"
|
|
23
|
+
function getBinaryName(platform) {
|
|
24
|
+
return (platform || process.platform) === "win32"
|
|
25
|
+
? "archgate.exe"
|
|
26
|
+
: "archgate";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getArchiveExt(platform) {
|
|
30
|
+
return (platform || process.platform) === "win32" ? "zip" : "tar.gz";
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
function getCacheDir() {
|
|
@@ -28,7 +36,7 @@ function getCacheDir() {
|
|
|
28
36
|
|
|
29
37
|
function getPackageVersion() {
|
|
30
38
|
const pkg = JSON.parse(
|
|
31
|
-
fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8")
|
|
39
|
+
fs.readFileSync(path.join(__dirname, "..", "..", "package.json"), "utf8")
|
|
32
40
|
);
|
|
33
41
|
return pkg.version;
|
|
34
42
|
}
|
|
@@ -72,6 +80,48 @@ function stripNulls(str) {
|
|
|
72
80
|
return idx === -1 ? str : str.slice(0, idx);
|
|
73
81
|
}
|
|
74
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Download a URL into a Buffer, following redirects.
|
|
85
|
+
*/
|
|
86
|
+
async function downloadToBuffer(url) {
|
|
87
|
+
const res = await fetchWithRedirects(url);
|
|
88
|
+
const chunks = [];
|
|
89
|
+
await new Promise((resolve, reject) => {
|
|
90
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
91
|
+
res.on("end", resolve);
|
|
92
|
+
res.on("error", reject);
|
|
93
|
+
});
|
|
94
|
+
return Buffer.concat(chunks);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Verify SHA256 checksum of a buffer against the companion .sha256 file.
|
|
99
|
+
* The .sha256 file format is: "<hex-sha256> <filename>\n"
|
|
100
|
+
*/
|
|
101
|
+
async function verifySha256(archiveBuffer, checksumUrl, version, fetchFn) {
|
|
102
|
+
const download = fetchFn || downloadToBuffer;
|
|
103
|
+
let checksumData;
|
|
104
|
+
try {
|
|
105
|
+
checksumData = await download(checksumUrl);
|
|
106
|
+
} catch {
|
|
107
|
+
// If checksum file is unavailable, skip verification with a warning
|
|
108
|
+
console.error(
|
|
109
|
+
"archgate: warning: checksum file not available, skipping verification"
|
|
110
|
+
);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const expectedHash = checksumData.toString("utf8").trim().split(/\s+/u)[0];
|
|
114
|
+
const actualHash = crypto
|
|
115
|
+
.createHash("sha256")
|
|
116
|
+
.update(archiveBuffer)
|
|
117
|
+
.digest("hex");
|
|
118
|
+
if (actualHash !== expectedHash) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`checksum verification failed for v${version} (expected ${expectedHash}, got ${actualHash})`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
75
125
|
/**
|
|
76
126
|
* Download the platform binary from GitHub Releases and cache it.
|
|
77
127
|
* Returns the path to the downloaded binary.
|
|
@@ -81,27 +131,26 @@ async function downloadBinary() {
|
|
|
81
131
|
const version = getPackageVersion();
|
|
82
132
|
const binaryName = getBinaryName();
|
|
83
133
|
const isWin = process.platform === "win32";
|
|
84
|
-
const ext =
|
|
134
|
+
const ext = getArchiveExt();
|
|
85
135
|
|
|
86
|
-
const
|
|
136
|
+
const baseUrl = `https://github.com/archgate/cli/releases/download/v${version}/${artifactName}`;
|
|
137
|
+
const url = `${baseUrl}.${ext}`;
|
|
138
|
+
const checksumUrl = `${baseUrl}.${ext}.sha256`;
|
|
87
139
|
console.error(`archgate: binary not found, downloading v${version}...`);
|
|
88
140
|
|
|
89
141
|
const cacheDir = getCacheDir();
|
|
90
142
|
fs.mkdirSync(cacheDir, { recursive: true });
|
|
91
143
|
const destPath = path.join(cacheDir, binaryName);
|
|
92
144
|
|
|
145
|
+
// Download archive and verify checksum (shared across platforms)
|
|
146
|
+
const archiveBuffer = await downloadToBuffer(url);
|
|
147
|
+
await verifySha256(archiveBuffer, checksumUrl, version);
|
|
148
|
+
|
|
93
149
|
if (isWin) {
|
|
94
|
-
//
|
|
95
|
-
const res = await fetchWithRedirects(url);
|
|
96
|
-
const chunks = [];
|
|
97
|
-
await new Promise((resolve, reject) => {
|
|
98
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
99
|
-
res.on("end", resolve);
|
|
100
|
-
res.on("error", reject);
|
|
101
|
-
});
|
|
150
|
+
// Extract zip with PowerShell
|
|
102
151
|
const tmpZip = path.join(cacheDir, "archgate-download.zip");
|
|
103
152
|
const tmpExtract = path.join(cacheDir, "archgate-extract");
|
|
104
|
-
fs.writeFileSync(tmpZip,
|
|
153
|
+
fs.writeFileSync(tmpZip, archiveBuffer);
|
|
105
154
|
try {
|
|
106
155
|
fs.mkdirSync(tmpExtract, { recursive: true });
|
|
107
156
|
execSync(
|
|
@@ -114,17 +163,24 @@ async function downloadBinary() {
|
|
|
114
163
|
}
|
|
115
164
|
fs.copyFileSync(extractedBinary, destPath);
|
|
116
165
|
} finally {
|
|
117
|
-
try {
|
|
118
|
-
|
|
166
|
+
try {
|
|
167
|
+
fs.unlinkSync(tmpZip);
|
|
168
|
+
} catch {
|
|
169
|
+
/* cleanup */
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
fs.rmSync(tmpExtract, { recursive: true, force: true });
|
|
173
|
+
} catch {
|
|
174
|
+
/* cleanup */
|
|
175
|
+
}
|
|
119
176
|
}
|
|
120
177
|
} else {
|
|
121
|
-
//
|
|
122
|
-
const res = await fetchWithRedirects(url);
|
|
178
|
+
// Extract binary from tar.gz using inline tar parser
|
|
123
179
|
await new Promise((resolve, reject) => {
|
|
124
180
|
const gunzip = zlib.createGunzip();
|
|
125
181
|
const chunks = [];
|
|
126
182
|
|
|
127
|
-
|
|
183
|
+
gunzip.end(archiveBuffer);
|
|
128
184
|
gunzip.on("data", (chunk) => chunks.push(chunk));
|
|
129
185
|
gunzip.on("end", () => {
|
|
130
186
|
const data = Buffer.concat(chunks);
|
|
@@ -138,9 +194,7 @@ async function downloadBinary() {
|
|
|
138
194
|
if (header.every((b) => b === 0)) break;
|
|
139
195
|
|
|
140
196
|
let name = stripNulls(header.subarray(0, 100).toString("utf8"));
|
|
141
|
-
const prefix = stripNulls(
|
|
142
|
-
header.subarray(345, 500).toString("utf8")
|
|
143
|
-
);
|
|
197
|
+
const prefix = stripNulls(header.subarray(345, 500).toString("utf8"));
|
|
144
198
|
if (prefix) name = `${prefix}/${name}`;
|
|
145
199
|
|
|
146
200
|
const sizeStr = stripNulls(
|
|
@@ -204,4 +258,17 @@ async function main() {
|
|
|
204
258
|
}
|
|
205
259
|
}
|
|
206
260
|
|
|
207
|
-
main()
|
|
261
|
+
// When required as a module (tests), export helpers without running main().
|
|
262
|
+
// When executed directly (npm bin), run the CLI passthrough.
|
|
263
|
+
if (require.main === module) {
|
|
264
|
+
main();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
module.exports = {
|
|
268
|
+
getArtifactName,
|
|
269
|
+
getBinaryName,
|
|
270
|
+
getArchiveExt,
|
|
271
|
+
getCacheDir,
|
|
272
|
+
stripNulls,
|
|
273
|
+
verifySha256,
|
|
274
|
+
};
|