archgate 0.26.0 → 0.26.2
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/archgate.cjs +100 -78
- package/package.json +1 -6
- package/scripts/postinstall.cjs +3 -23
package/bin/archgate.cjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
-
const { execFileSync } = require("child_process");
|
|
4
|
+
const { execFileSync, execSync } = require("child_process");
|
|
5
5
|
const https = require("https");
|
|
6
6
|
const zlib = require("zlib");
|
|
7
7
|
const path = require("path");
|
|
8
8
|
const fs = require("fs");
|
|
9
|
+
const os = require("os");
|
|
9
10
|
|
|
10
|
-
function
|
|
11
|
+
function getArtifactName() {
|
|
11
12
|
const { platform, arch } = process;
|
|
12
13
|
if (platform === "darwin" && arch === "arm64") return "archgate-darwin-arm64";
|
|
13
14
|
if (platform === "linux" && arch === "x64") return "archgate-linux-x64";
|
|
@@ -21,6 +22,10 @@ function getBinaryName() {
|
|
|
21
22
|
return process.platform === "win32" ? "archgate.exe" : "archgate";
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
function getCacheDir() {
|
|
26
|
+
return path.join(os.homedir(), ".archgate", "bin");
|
|
27
|
+
}
|
|
28
|
+
|
|
24
29
|
function getPackageVersion() {
|
|
25
30
|
const pkg = JSON.parse(
|
|
26
31
|
fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8")
|
|
@@ -29,34 +34,20 @@ function getPackageVersion() {
|
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
function getBinaryPath() {
|
|
32
|
-
const pkgName = getPlatformPackageName();
|
|
33
37
|
const binaryName = getBinaryName();
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
const pkgDir = path.dirname(require.resolve(`${pkgName}/package.json`));
|
|
38
|
-
const binaryPath = path.join(pkgDir, "bin", binaryName);
|
|
39
|
-
if (fs.existsSync(binaryPath)) return binaryPath;
|
|
40
|
-
} catch {
|
|
41
|
-
/* platform package not installed */
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// 2. Fallback: binary downloaded into our own bin/ by postinstall or a
|
|
45
|
-
// previous on-demand download.
|
|
46
|
-
const fallbackPath = path.join(__dirname, binaryName);
|
|
47
|
-
if (fs.existsSync(fallbackPath)) return fallbackPath;
|
|
48
|
-
|
|
38
|
+
const cachePath = path.join(getCacheDir(), binaryName);
|
|
39
|
+
if (fs.existsSync(cachePath)) return cachePath;
|
|
49
40
|
return null;
|
|
50
41
|
}
|
|
51
42
|
|
|
52
43
|
// ---------------------------------------------------------------------------
|
|
53
|
-
// On-demand download from
|
|
44
|
+
// On-demand download from GitHub Releases
|
|
54
45
|
// ---------------------------------------------------------------------------
|
|
55
46
|
|
|
56
47
|
function fetchWithRedirects(url) {
|
|
57
48
|
return new Promise((resolve, reject) => {
|
|
58
49
|
https
|
|
59
|
-
.get(url, (res) => {
|
|
50
|
+
.get(url, { headers: { "User-Agent": "archgate-cli" } }, (res) => {
|
|
60
51
|
if (
|
|
61
52
|
res.statusCode >= 300 &&
|
|
62
53
|
res.statusCode < 400 &&
|
|
@@ -82,74 +73,106 @@ function stripNulls(str) {
|
|
|
82
73
|
}
|
|
83
74
|
|
|
84
75
|
/**
|
|
85
|
-
* Download the platform
|
|
76
|
+
* Download the platform binary from GitHub Releases and cache it.
|
|
86
77
|
* Returns the path to the downloaded binary.
|
|
87
78
|
*/
|
|
88
79
|
async function downloadBinary() {
|
|
89
|
-
const
|
|
80
|
+
const artifactName = getArtifactName();
|
|
90
81
|
const version = getPackageVersion();
|
|
91
82
|
const binaryName = getBinaryName();
|
|
83
|
+
const isWin = process.platform === "win32";
|
|
84
|
+
const ext = isWin ? "zip" : "tar.gz";
|
|
92
85
|
|
|
93
|
-
const url = `https://
|
|
94
|
-
console.error(
|
|
95
|
-
`archgate: binary not found, downloading ${pkgName}@${version}...`
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
const res = await fetchWithRedirects(url);
|
|
86
|
+
const url = `https://github.com/archgate/cli/releases/download/v${version}/${artifactName}.${ext}`;
|
|
87
|
+
console.error(`archgate: binary not found, downloading v${version}...`);
|
|
99
88
|
|
|
100
|
-
const
|
|
101
|
-
|
|
89
|
+
const cacheDir = getCacheDir();
|
|
90
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
91
|
+
const destPath = path.join(cacheDir, binaryName);
|
|
102
92
|
|
|
103
|
-
|
|
104
|
-
|
|
93
|
+
if (isWin) {
|
|
94
|
+
// Download zip to temp file, extract with PowerShell
|
|
95
|
+
const res = await fetchWithRedirects(url);
|
|
105
96
|
const chunks = [];
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const prefix = stripNulls(header.subarray(345, 500).toString("utf8"));
|
|
124
|
-
if (prefix) name = `${prefix}/${name}`;
|
|
125
|
-
|
|
126
|
-
const sizeStr = stripNulls(
|
|
127
|
-
header.subarray(124, 136).toString("utf8")
|
|
128
|
-
).trim();
|
|
129
|
-
const size = parseInt(sizeStr, 8) || 0;
|
|
130
|
-
const blocks = Math.ceil(size / 512);
|
|
131
|
-
const fileData = data.subarray(offset, offset + size);
|
|
132
|
-
offset += blocks * 512;
|
|
133
|
-
|
|
134
|
-
if (name.endsWith(expectedSuffix)) {
|
|
135
|
-
fs.writeFileSync(destPath, fileData, { mode: 0o755 });
|
|
136
|
-
found = true;
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
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
|
+
});
|
|
102
|
+
const tmpZip = path.join(cacheDir, "archgate-download.zip");
|
|
103
|
+
const tmpExtract = path.join(cacheDir, "archgate-extract");
|
|
104
|
+
fs.writeFileSync(tmpZip, Buffer.concat(chunks));
|
|
105
|
+
try {
|
|
106
|
+
fs.mkdirSync(tmpExtract, { recursive: true });
|
|
107
|
+
execSync(
|
|
108
|
+
`powershell -NoProfile -Command "Expand-Archive -Path '${tmpZip}' -DestinationPath '${tmpExtract}' -Force"`,
|
|
109
|
+
{ stdio: "pipe" }
|
|
110
|
+
);
|
|
111
|
+
const extractedBinary = path.join(tmpExtract, binaryName);
|
|
112
|
+
if (!fs.existsSync(extractedBinary)) {
|
|
113
|
+
throw new Error(`Binary ${binaryName} not found in zip archive`);
|
|
139
114
|
}
|
|
115
|
+
fs.copyFileSync(extractedBinary, destPath);
|
|
116
|
+
} finally {
|
|
117
|
+
try { fs.unlinkSync(tmpZip); } catch { /* cleanup */ }
|
|
118
|
+
try { fs.rmSync(tmpExtract, { recursive: true, force: true }); } catch { /* cleanup */ }
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
// Download tar.gz, extract binary using inline tar parser
|
|
122
|
+
const res = await fetchWithRedirects(url);
|
|
123
|
+
await new Promise((resolve, reject) => {
|
|
124
|
+
const gunzip = zlib.createGunzip();
|
|
125
|
+
const chunks = [];
|
|
126
|
+
|
|
127
|
+
res.pipe(gunzip);
|
|
128
|
+
gunzip.on("data", (chunk) => chunks.push(chunk));
|
|
129
|
+
gunzip.on("end", () => {
|
|
130
|
+
const data = Buffer.concat(chunks);
|
|
131
|
+
let offset = 0;
|
|
132
|
+
let found = false;
|
|
133
|
+
|
|
134
|
+
while (offset + 512 <= data.length) {
|
|
135
|
+
const header = data.subarray(offset, offset + 512);
|
|
136
|
+
offset += 512;
|
|
137
|
+
|
|
138
|
+
if (header.every((b) => b === 0)) break;
|
|
139
|
+
|
|
140
|
+
let name = stripNulls(header.subarray(0, 100).toString("utf8"));
|
|
141
|
+
const prefix = stripNulls(
|
|
142
|
+
header.subarray(345, 500).toString("utf8")
|
|
143
|
+
);
|
|
144
|
+
if (prefix) name = `${prefix}/${name}`;
|
|
145
|
+
|
|
146
|
+
const sizeStr = stripNulls(
|
|
147
|
+
header.subarray(124, 136).toString("utf8")
|
|
148
|
+
).trim();
|
|
149
|
+
const size = parseInt(sizeStr, 8) || 0;
|
|
150
|
+
const blocks = Math.ceil(size / 512);
|
|
151
|
+
const fileData = data.subarray(offset, offset + size);
|
|
152
|
+
offset += blocks * 512;
|
|
153
|
+
|
|
154
|
+
if (name === binaryName || name.endsWith(`/${binaryName}`)) {
|
|
155
|
+
fs.writeFileSync(destPath, fileData, { mode: 0o755 });
|
|
156
|
+
found = true;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
140
160
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
161
|
+
if (found) {
|
|
162
|
+
resolve();
|
|
163
|
+
} else {
|
|
164
|
+
reject(
|
|
165
|
+
new Error(`Could not find ${binaryName} in archive from ${url}`)
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
gunzip.on("error", reject);
|
|
149
171
|
});
|
|
172
|
+
}
|
|
150
173
|
|
|
151
|
-
|
|
152
|
-
|
|
174
|
+
console.error(`archgate: binary downloaded successfully.`);
|
|
175
|
+
return destPath;
|
|
153
176
|
}
|
|
154
177
|
|
|
155
178
|
// ---------------------------------------------------------------------------
|
|
@@ -159,15 +182,14 @@ async function downloadBinary() {
|
|
|
159
182
|
async function main() {
|
|
160
183
|
let binary = getBinaryPath();
|
|
161
184
|
|
|
162
|
-
// If the binary is missing
|
|
163
|
-
// download it on-demand from the npm registry.
|
|
185
|
+
// If the binary is missing, download it on-demand from GitHub Releases.
|
|
164
186
|
if (!binary) {
|
|
165
187
|
try {
|
|
166
188
|
binary = await downloadBinary();
|
|
167
189
|
} catch (err) {
|
|
168
190
|
console.error(
|
|
169
191
|
`archgate: failed to download binary: ${err.message}\n` +
|
|
170
|
-
`
|
|
192
|
+
`Visit https://cli.archgate.dev/getting-started/installation/ for alternative install methods.`
|
|
171
193
|
);
|
|
172
194
|
process.exit(2);
|
|
173
195
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archgate",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.2",
|
|
4
4
|
"description": "Enforce Architecture Decision Records as executable rules — for both humans and AI agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adr",
|
|
@@ -75,10 +75,5 @@
|
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"typescript": "^5 || ^6.0.0"
|
|
77
77
|
},
|
|
78
|
-
"optionalDependencies": {
|
|
79
|
-
"archgate-darwin-arm64": "0.26.0",
|
|
80
|
-
"archgate-linux-x64": "0.26.0",
|
|
81
|
-
"archgate-win32-x64": "0.26.0"
|
|
82
|
-
},
|
|
83
78
|
"readme": "README.md"
|
|
84
79
|
}
|
package/scripts/postinstall.cjs
CHANGED
|
@@ -8,32 +8,12 @@
|
|
|
8
8
|
|
|
9
9
|
const path = require("path");
|
|
10
10
|
const fs = require("fs");
|
|
11
|
-
|
|
12
|
-
const PACKAGE_NAME_MAP = {
|
|
13
|
-
"darwin-arm64": "archgate-darwin-arm64",
|
|
14
|
-
"linux-x64": "archgate-linux-x64",
|
|
15
|
-
"win32-x64": "archgate-win32-x64",
|
|
16
|
-
};
|
|
11
|
+
const os = require("os");
|
|
17
12
|
|
|
18
13
|
function isBinaryPresent() {
|
|
19
|
-
const key = `${process.platform}-${process.arch}`;
|
|
20
|
-
const pkgName = PACKAGE_NAME_MAP[key];
|
|
21
|
-
if (!pkgName) return true; // unsupported platform — skip silently
|
|
22
|
-
|
|
23
14
|
const binaryName = process.platform === "win32" ? "archgate.exe" : "archgate";
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
const pkgDir = path.dirname(require.resolve(`${pkgName}/package.json`));
|
|
28
|
-
if (fs.existsSync(path.join(pkgDir, "bin", binaryName))) return true;
|
|
29
|
-
} catch {
|
|
30
|
-
/* not installed */
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Check local fallback
|
|
34
|
-
if (fs.existsSync(path.join(__dirname, "..", "bin", binaryName))) return true;
|
|
35
|
-
|
|
36
|
-
return false;
|
|
15
|
+
const cachePath = path.join(os.homedir(), ".archgate", "bin", binaryName);
|
|
16
|
+
return fs.existsSync(cachePath);
|
|
37
17
|
}
|
|
38
18
|
|
|
39
19
|
if (!isBinaryPresent()) {
|