juggernaut-bedrock 4.1.0 → 4.2.1
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/index.js +54 -0
- package/package.json +11 -4
- package/install.js +0 -204
package/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const { spawnSync } = require("child_process");
|
|
5
|
+
|
|
6
|
+
const PACKAGES_DIR = path.join(__dirname, "packages");
|
|
7
|
+
|
|
8
|
+
const PLATFORM_MAP = {
|
|
9
|
+
"linux-x64": "juggernaut-bedrock-linux-x64",
|
|
10
|
+
"linux-arm64": "juggernaut-bedrock-linux-arm64",
|
|
11
|
+
"darwin-x64": "juggernaut-bedrock-darwin-x64",
|
|
12
|
+
"darwin-arm64": "juggernaut-bedrock-darwin-arm64",
|
|
13
|
+
"win32-x64": "juggernaut-bedrock-win32-x64",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function getPlatformPackage(platform, arch) {
|
|
17
|
+
return PLATFORM_MAP[`${platform}-${arch}`] || null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getBinaryPath(pkgName, platform) {
|
|
21
|
+
const binaryName = platform === "win32" ? "juggernaut.exe" : "juggernaut";
|
|
22
|
+
return path.join(PACKAGES_DIR, pkgName, "bin", binaryName);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (require.main === module) {
|
|
26
|
+
const pkg = getPlatformPackage(process.platform, process.arch);
|
|
27
|
+
if (!pkg) {
|
|
28
|
+
process.stderr.write(
|
|
29
|
+
`juggernaut-bedrock: unsupported platform ${process.platform}/${process.arch}\n` +
|
|
30
|
+
`Please file an issue: https://github.com/jpvelasco/juggernaut/issues\n`
|
|
31
|
+
);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const bin = getBinaryPath(pkg, process.platform);
|
|
36
|
+
const fs = require("fs");
|
|
37
|
+
if (!fs.existsSync(bin)) {
|
|
38
|
+
process.stderr.write(
|
|
39
|
+
`juggernaut-bedrock: binary not found at ${bin}\n` +
|
|
40
|
+
`Try reinstalling: npm install -g juggernaut-bedrock\n` +
|
|
41
|
+
`If the problem persists, file an issue: https://github.com/jpvelasco/juggernaut/issues\n`
|
|
42
|
+
);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// nosemgrep: javascript.lang.security.detect-child-process
|
|
47
|
+
const result = spawnSync(bin, process.argv.slice(2), {
|
|
48
|
+
stdio: "inherit",
|
|
49
|
+
env: process.env,
|
|
50
|
+
});
|
|
51
|
+
process.exit(result.status ?? 1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { getPlatformPackage, getBinaryPath };
|
package/package.json
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juggernaut-bedrock",
|
|
3
|
-
"version": "4.1
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"description": "Route Claude Code through Amazon Bedrock in one command — IAM, SSO, or API key. Cross-platform CLI for GenAI developers.",
|
|
5
5
|
"bin": {
|
|
6
|
-
"juggernaut": "./
|
|
6
|
+
"juggernaut": "./index.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"
|
|
9
|
+
"test": "node --test index.test.js"
|
|
10
|
+
},
|
|
11
|
+
"optionalDependencies": {
|
|
12
|
+
"juggernaut-bedrock-linux-x64": "4.2.1",
|
|
13
|
+
"juggernaut-bedrock-linux-arm64": "4.2.1",
|
|
14
|
+
"juggernaut-bedrock-darwin-x64": "4.2.1",
|
|
15
|
+
"juggernaut-bedrock-darwin-arm64": "4.2.1",
|
|
16
|
+
"juggernaut-bedrock-win32-x64": "4.2.1"
|
|
10
17
|
},
|
|
11
18
|
"os": [
|
|
12
19
|
"darwin",
|
|
@@ -36,7 +43,7 @@
|
|
|
36
43
|
"license": "MIT",
|
|
37
44
|
"repository": {
|
|
38
45
|
"type": "git",
|
|
39
|
-
"url": "https://github.com/jpvelasco/juggernaut"
|
|
46
|
+
"url": "git+https://github.com/jpvelasco/juggernaut.git"
|
|
40
47
|
},
|
|
41
48
|
"bugs": {
|
|
42
49
|
"url": "https://github.com/jpvelasco/juggernaut/issues"
|
package/install.js
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const https = require("https");
|
|
4
|
-
const fs = require("fs");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
const os = require("os");
|
|
7
|
-
const crypto = require("crypto");
|
|
8
|
-
const { execFile, execFileSync } = require("child_process");
|
|
9
|
-
const { promisify } = require("util");
|
|
10
|
-
|
|
11
|
-
const execFileAsync = promisify(execFile);
|
|
12
|
-
|
|
13
|
-
const REPO = "jpvelasco/juggernaut";
|
|
14
|
-
const PACKAGE_DIR = __dirname;
|
|
15
|
-
const BIN_DIR = path.join(PACKAGE_DIR, "bin");
|
|
16
|
-
const TMP_PREFIX = path.join(os.tmpdir(), "juggernaut-install-");
|
|
17
|
-
const ARCHIVE_TAR = "archive.tar.gz";
|
|
18
|
-
const ARCHIVE_ZIP = "archive.zip";
|
|
19
|
-
const BIN_NAME = "juggernaut";
|
|
20
|
-
const ALLOWED_HOSTS = new Set([
|
|
21
|
-
"github.com",
|
|
22
|
-
"api.github.com",
|
|
23
|
-
"release-assets.githubusercontent.com"
|
|
24
|
-
]);
|
|
25
|
-
|
|
26
|
-
function joinUnder(base, ...segments) {
|
|
27
|
-
if (base.indexOf("\0") !== -1) {
|
|
28
|
-
throw new Error("Invalid base path");
|
|
29
|
-
}
|
|
30
|
-
for (const seg of segments) {
|
|
31
|
-
if (seg.indexOf("..") !== -1 || seg.indexOf("/") !== -1 || seg.indexOf("\\") !== -1) {
|
|
32
|
-
throw new Error(`Invalid path segment: ${seg}`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
const joined = path.join(base, ...segments);
|
|
36
|
-
const normalBase = path.normalize(base) + path.sep;
|
|
37
|
-
const normalJoined = path.normalize(joined);
|
|
38
|
-
const compare = process.platform === "win32"
|
|
39
|
-
? (a, b) => a.toLowerCase().startsWith(b.toLowerCase())
|
|
40
|
-
: (a, b) => a.startsWith(b);
|
|
41
|
-
if (!compare(normalJoined, normalBase)) {
|
|
42
|
-
const exactBase = process.platform === "win32"
|
|
43
|
-
? path.normalize(base).toLowerCase()
|
|
44
|
-
: path.normalize(base);
|
|
45
|
-
const exactJoined = process.platform === "win32"
|
|
46
|
-
? normalJoined.toLowerCase()
|
|
47
|
-
: normalJoined;
|
|
48
|
-
if (exactJoined !== exactBase) {
|
|
49
|
-
throw new Error(`Path traversal detected: ${joined} is outside ${base}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return normalJoined;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function getPlatform() {
|
|
56
|
-
const osMap = { darwin: "darwin", linux: "linux", win32: "windows" };
|
|
57
|
-
const archMap = { x64: "amd64", arm64: "arm64" };
|
|
58
|
-
const p = osMap[process.platform];
|
|
59
|
-
const a = archMap[process.arch];
|
|
60
|
-
if (!p || !a) {
|
|
61
|
-
throw new Error(`Unsupported platform: ${process.platform}/${process.arch}`);
|
|
62
|
-
}
|
|
63
|
-
return { os: p, arch: a, platform: `${p}_${a}` };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function httpsGetBuffer(url) {
|
|
67
|
-
const parsed = new URL(url);
|
|
68
|
-
if (!ALLOWED_HOSTS.has(parsed.hostname)) {
|
|
69
|
-
throw new Error(`URL host not allowed: ${parsed.hostname}`);
|
|
70
|
-
}
|
|
71
|
-
return new Promise((resolve, reject) => {
|
|
72
|
-
https
|
|
73
|
-
.get(url, { headers: { "User-Agent": "juggernaut-npm-installer/1.0" } }, (res) => {
|
|
74
|
-
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
75
|
-
const location = res.headers.location;
|
|
76
|
-
const redirectParsed = new URL(location);
|
|
77
|
-
if (!ALLOWED_HOSTS.has(redirectParsed.hostname)) {
|
|
78
|
-
reject(new Error(`Redirect host not allowed: ${redirectParsed.hostname}`));
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
httpsGetBuffer(location).then(resolve).catch(reject);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const chunks = [];
|
|
85
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
86
|
-
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
87
|
-
res.on("error", reject);
|
|
88
|
-
})
|
|
89
|
-
.on("error", reject);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function getLatestVersion() {
|
|
94
|
-
const data = await httpsGetBuffer(`https://api.github.com/repos/${REPO}/releases/latest`);
|
|
95
|
-
const release = JSON.parse(data.toString());
|
|
96
|
-
return release.tag_name.replace(/^v/, "");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function pickArchive(platform, checksumsText) {
|
|
100
|
-
const tarArchive = `juggernaut_${platform}.tar.gz`;
|
|
101
|
-
if (checksumsText.includes(tarArchive)) return { name: tarArchive, kind: "tar.gz" };
|
|
102
|
-
const zipArchive = `juggernaut_${platform}.zip`;
|
|
103
|
-
if (process.platform === "win32" && checksumsText.includes(zipArchive)) {
|
|
104
|
-
return { name: zipArchive, kind: "zip" };
|
|
105
|
-
}
|
|
106
|
-
throw new Error(`No supported archive found in release checksums for ${platform}`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function extractTarGz(archiveBuf) {
|
|
110
|
-
const tmpDir = fs.mkdtempSync(TMP_PREFIX);
|
|
111
|
-
const archivePath = joinUnder(tmpDir, ARCHIVE_TAR);
|
|
112
|
-
const prevCwd = process.cwd();
|
|
113
|
-
try {
|
|
114
|
-
process.chdir(tmpDir);
|
|
115
|
-
fs.writeFileSync(ARCHIVE_TAR, archiveBuf);
|
|
116
|
-
process.chdir(PACKAGE_DIR);
|
|
117
|
-
fs.mkdirSync("bin", { recursive: true });
|
|
118
|
-
try {
|
|
119
|
-
execFileSync("tar", ["-xzf", archivePath, "-C", BIN_DIR], { stdio: "pipe" });
|
|
120
|
-
} catch (err) {
|
|
121
|
-
if (err.code === "ENOENT") {
|
|
122
|
-
throw new Error(
|
|
123
|
-
"tar not found on PATH. On Windows, use Windows 10 version 1803 or later (includes tar.exe)."
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
const detail = err.stderr ? err.stderr.toString().trim() : err.message;
|
|
127
|
-
throw new Error(`tar extraction failed: ${detail}`);
|
|
128
|
-
}
|
|
129
|
-
if (process.platform !== "win32") {
|
|
130
|
-
process.chdir(BIN_DIR);
|
|
131
|
-
if (fs.existsSync(BIN_NAME)) {
|
|
132
|
-
fs.chmodSync(BIN_NAME, 0o700);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
} finally {
|
|
136
|
-
process.chdir(prevCwd);
|
|
137
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async function extractZip(archiveBuf) {
|
|
142
|
-
const tmpDir = fs.mkdtempSync(TMP_PREFIX);
|
|
143
|
-
const archivePath = joinUnder(tmpDir, ARCHIVE_ZIP);
|
|
144
|
-
const prevCwd = process.cwd();
|
|
145
|
-
try {
|
|
146
|
-
process.chdir(tmpDir);
|
|
147
|
-
fs.writeFileSync(ARCHIVE_ZIP, archiveBuf);
|
|
148
|
-
process.chdir(PACKAGE_DIR);
|
|
149
|
-
fs.mkdirSync("bin", { recursive: true });
|
|
150
|
-
const script = [
|
|
151
|
-
"$ErrorActionPreference = 'Stop'",
|
|
152
|
-
`Expand-Archive -LiteralPath '${archivePath.replace(/'/g, "''")}' -DestinationPath '${BIN_DIR.replace(/'/g, "''")}' -Force`
|
|
153
|
-
].join("; ");
|
|
154
|
-
try {
|
|
155
|
-
await execFileAsync(
|
|
156
|
-
"powershell",
|
|
157
|
-
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
158
|
-
{ stdio: "pipe" }
|
|
159
|
-
);
|
|
160
|
-
} catch (err) {
|
|
161
|
-
if (err.code === "ENOENT") {
|
|
162
|
-
throw new Error(
|
|
163
|
-
"PowerShell not found. PowerShell 5.0+ is required to extract archives on Windows."
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
const detail = err.stderr ? err.stderr.toString().trim() : err.message;
|
|
167
|
-
throw new Error(`ZIP extraction failed: ${detail}`);
|
|
168
|
-
}
|
|
169
|
-
} finally {
|
|
170
|
-
process.chdir(prevCwd);
|
|
171
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async function main() {
|
|
176
|
-
const { platform } = getPlatform();
|
|
177
|
-
const version = await getLatestVersion();
|
|
178
|
-
const baseUrl = `https://github.com/${REPO}/releases/download/v${version}`;
|
|
179
|
-
const checksumsBuf = await httpsGetBuffer(`${baseUrl}/checksums.txt`);
|
|
180
|
-
const checksums = checksumsBuf.toString("utf8");
|
|
181
|
-
const { name: archive, kind } = pickArchive(platform, checksums);
|
|
182
|
-
|
|
183
|
-
console.log(`Downloading Juggernaut v${version} (${platform})...`);
|
|
184
|
-
const archiveBuf = await httpsGetBuffer(`${baseUrl}/${archive}`);
|
|
185
|
-
|
|
186
|
-
const line = checksums.split("\n").find((l) => l.includes(archive));
|
|
187
|
-
if (!line) throw new Error(`Checksum not found for ${archive}`);
|
|
188
|
-
const expected = line.trim().split(/\s+/)[0].toLowerCase();
|
|
189
|
-
const actual = crypto.createHash("sha256").update(archiveBuf).digest("hex").toLowerCase();
|
|
190
|
-
if (actual !== expected) {
|
|
191
|
-
throw new Error(`Checksum mismatch for ${archive}\n expected: ${expected}\n got: ${actual}`);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (kind === "zip") await extractZip(archiveBuf);
|
|
195
|
-
else extractTarGz(archiveBuf);
|
|
196
|
-
|
|
197
|
-
console.log(`Juggernaut v${version} installed successfully.`);
|
|
198
|
-
console.log(`Run: juggernaut apply`);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
main().catch((err) => {
|
|
202
|
-
console.error("Installation failed:", err.message);
|
|
203
|
-
process.exit(1);
|
|
204
|
-
});
|