juggernaut-bedrock 4.0.0 → 4.0.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/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # juggernaut-bedrock
2
+
3
+ **Claude Code → Amazon Bedrock in one command.**
4
+
5
+ Juggernaut is a cross-platform CLI that wires [Claude Code](https://docs.anthropic.com/en/docs/claude-code) to [Amazon Bedrock](https://aws.amazon.com/bedrock/) instead of Anthropic's direct API. One install, one `apply`, then just run `claude` — no shell profile hacks, no manual env vars, no jq.
6
+
7
+ Built for developers shipping with GenAI today: IAM and SSO for teams, API keys for solo runs, and a `doctor` command when something's off.
8
+
9
+ <p align="center">
10
+ <a href="https://github.com/jpvelasco/juggernaut/actions/workflows/ci.yml"><img src="https://github.com/jpvelasco/juggernaut/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
11
+ <a href="https://github.com/jpvelasco/juggernaut/releases/latest"><img src="https://img.shields.io/github/v/release/jpvelasco/juggernaut" alt="Release"></a>
12
+ <a href="https://www.npmjs.com/package/juggernaut-bedrock"><img src="https://img.shields.io/npm/v/juggernaut-bedrock" alt="npm"></a>
13
+ <a href="https://app.codacy.com/gh/jpvelasco/juggernaut/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade"><img src="https://app.codacy.com/project/badge/Grade/2bf1e68b80964537b5c65350663c3073" alt="Codacy Grade"></a>
14
+ </p>
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install -g juggernaut-bedrock
20
+ ```
21
+
22
+ Or try it without installing globally:
23
+
24
+ ```bash
25
+ npx juggernaut-bedrock version
26
+ ```
27
+
28
+ Works on **macOS**, **Linux**, **Windows**, and **WSL** — `x64` and `arm64`.
29
+
30
+ ## Quickstart
31
+
32
+ ```bash
33
+ # 1. Install (above)
34
+
35
+ # 2. Configure — IAM/SSO (recommended) or interactive prompt
36
+ juggernaut apply --auth=iam
37
+
38
+ # 3. Launch Claude Code (token injected automatically)
39
+ claude
40
+ ```
41
+
42
+ Bedrock API key auth? Run `juggernaut apply` and follow the prompt — credentials land in your OS keychain, not your shell history.
43
+
44
+ ## What it does
45
+
46
+ ```
47
+ juggernaut apply --auth=iam
48
+ ```
49
+
50
+ That one command:
51
+
52
+ 1. **Writes** Bedrock config to `~/.claude/settings.json` (or project scope)
53
+ 2. **Sets** model IDs, region, effort level, and `CLAUDE_CODE_USE_BEDROCK=1` — only after credentials check out
54
+ 3. **Installs** a `claude` launcher shim that reads your token from the keychain and execs the real binary
55
+
56
+ No `.bashrc` edits. No copying API keys into env vars. No "why isn't Bedrock routing?" at 2am.
57
+
58
+ ## Why Bedrock?
59
+
60
+ | | Direct Anthropic API | Amazon Bedrock |
61
+ |---|---------------------|----------------|
62
+ | **Billing** | Separate account | Your AWS bill |
63
+ | **Auth** | API keys | IAM, SSO, roles |
64
+ | **Region** | Anthropic infra | Your chosen AWS region |
65
+ | **Compliance** | Anthropic certs | SOC, HIPAA, FedRAMP via AWS |
66
+ | **Network** | Public internet | VPC endpoints, PrivateLink |
67
+
68
+ ## Auth modes
69
+
70
+ | Mode | Command | Best for |
71
+ |------|---------|----------|
72
+ | **IAM / SSO** | `juggernaut apply --auth=iam` | Teams, enterprise, existing AWS identity |
73
+ | **Bedrock API key** | `juggernaut apply` | Solo devs, quick setup |
74
+ | **Interactive** | `juggernaut apply` (no flags) | First run — guided prompts |
75
+ | **Preview** | `juggernaut apply --dry-run` | See what would change, change nothing |
76
+
77
+ ## Commands
78
+
79
+ | Command | What it does |
80
+ |---------|--------------|
81
+ | `apply` | Configure Bedrock + install the `claude` shim |
82
+ | `show` | Print your current Juggernaut config |
83
+ | `doctor` | Diagnostics — block, credentials, launcher |
84
+ | `migrate` | Upgrade from v3 shell installer to v4 Go binary |
85
+ | `uninstall` | Remove config, token, and shim |
86
+ | `version` | Print installed version (`--json` for machines) |
87
+
88
+ ## Default models
89
+
90
+ | Tier | Model |
91
+ |------|-------|
92
+ | **Primary** | Claude Sonnet 4.6 |
93
+ | **Opus** | Claude Opus 4.7 |
94
+ | **Fast** | Claude Haiku 4.5 |
95
+
96
+ Override any tier: `juggernaut apply --auth=iam --model=global.anthropic.claude-sonnet-4-6`
97
+
98
+ ## Troubleshooting
99
+
100
+ Stuck? Start here:
101
+
102
+ ```bash
103
+ juggernaut doctor
104
+ ```
105
+
106
+ Common fixes: complete Anthropic model access in the Bedrock console (403), refresh SSO (`aws sso login`), or re-run `juggernaut apply`.
107
+
108
+ ## Documentation
109
+
110
+ Full docs, IAM policy, migration guide, and platform notes:
111
+
112
+ **[github.com/jpvelasco/juggernaut](https://github.com/jpvelasco/juggernaut)**
113
+
114
+ ## License
115
+
116
+ MIT — see [LICENSE](https://github.com/jpvelasco/juggernaut/blob/main/LICENSE).
117
+
118
+ Juggernaut is an independent tool, not affiliated with Anthropic or Amazon Web Services.
package/bin/.gitkeep ADDED
File without changes
package/install.js CHANGED
@@ -3,11 +3,16 @@
3
3
  const https = require("https");
4
4
  const fs = require("fs");
5
5
  const path = require("path");
6
- const os = require("os");
6
+ const { Readable } = require("stream");
7
7
  const crypto = require("crypto");
8
8
 
9
9
  const REPO = "jpvelasco/juggernaut";
10
10
  const BIN_DIR = path.join(__dirname, "bin");
11
+ const ALLOWED_HOSTS = new Set([
12
+ "github.com",
13
+ "api.github.com",
14
+ "release-assets.githubusercontent.com"
15
+ ]);
11
16
 
12
17
  function getPlatform() {
13
18
  const osMap = { darwin: "darwin", linux: "linux", win32: "windows" };
@@ -20,12 +25,23 @@ function getPlatform() {
20
25
  return { os: p, arch: a, platform: `${p}_${a}` };
21
26
  }
22
27
 
23
- function httpsGet(url) {
28
+ function httpsGetBuffer(url) {
29
+ const parsed = new URL(url);
30
+ if (!ALLOWED_HOSTS.has(parsed.hostname)) {
31
+ throw new Error(`URL host not allowed: ${parsed.hostname}`);
32
+ }
24
33
  return new Promise((resolve, reject) => {
25
34
  https
26
35
  .get(url, { headers: { "User-Agent": "juggernaut-npm-installer/1.0" } }, (res) => {
27
36
  if (res.statusCode === 301 || res.statusCode === 302) {
28
- return httpsGet(res.headers.location).then(resolve).catch(reject);
37
+ const location = res.headers.location;
38
+ const redirectParsed = new URL(location);
39
+ if (!ALLOWED_HOSTS.has(redirectParsed.hostname)) {
40
+ reject(new Error(`Redirect host not allowed: ${redirectParsed.hostname}`));
41
+ return;
42
+ }
43
+ httpsGetBuffer(location).then(resolve).catch(reject);
44
+ return;
29
45
  }
30
46
  const chunks = [];
31
47
  res.on("data", (chunk) => chunks.push(chunk));
@@ -36,27 +52,8 @@ function httpsGet(url) {
36
52
  });
37
53
  }
38
54
 
39
- function downloadFile(url, dest) {
40
- return new Promise((resolve, reject) => {
41
- const file = fs.createWriteStream(dest);
42
- function fetch(fetchUrl) {
43
- https
44
- .get(fetchUrl, { headers: { "User-Agent": "juggernaut-npm-installer/1.0" } }, (res) => {
45
- if (res.statusCode === 301 || res.statusCode === 302) {
46
- return fetch(res.headers.location);
47
- }
48
- res.pipe(file);
49
- file.on("finish", () => file.close(resolve));
50
- file.on("error", reject);
51
- })
52
- .on("error", reject);
53
- }
54
- fetch(url);
55
- });
56
- }
57
-
58
55
  async function getLatestVersion() {
59
- const data = await httpsGet(`https://api.github.com/repos/${REPO}/releases/latest`);
56
+ const data = await httpsGetBuffer(`https://api.github.com/repos/${REPO}/releases/latest`);
60
57
  const release = JSON.parse(data.toString());
61
58
  return release.tag_name.replace(/^v/, "");
62
59
  }
@@ -68,49 +65,33 @@ async function main() {
68
65
  const archive = `juggernaut_${platform}.${ext}`;
69
66
  const baseUrl = `https://github.com/${REPO}/releases/download/v${version}`;
70
67
 
71
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "juggernaut-"));
72
- const archivePath = path.join(tmp, archive);
73
- const checksumPath = path.join(tmp, "checksums.txt");
74
-
75
- try {
76
- console.log(`Downloading Juggernaut v${version} (${platform})...`);
77
- await downloadFile(`${baseUrl}/${archive}`, archivePath);
78
- await downloadFile(`${baseUrl}/checksums.txt`, checksumPath);
79
-
80
- // Verify checksum.
81
- const checksums = fs.readFileSync(checksumPath, "utf8");
82
- const line = checksums.split("\n").find((l) => l.includes(archive));
83
- if (!line) throw new Error(`Checksum not found for ${archive}`);
84
- const expected = line.trim().split(/\s+/)[0].toLowerCase();
85
- const actual = crypto
86
- .createHash("sha256")
87
- .update(fs.readFileSync(archivePath))
88
- .digest("hex")
89
- .toLowerCase();
90
- if (actual !== expected) {
91
- throw new Error(`Checksum mismatch for ${archive}\n expected: ${expected}\n got: ${actual}`);
92
- }
68
+ console.log(`Downloading Juggernaut v${version} (${platform})...`);
69
+ const archiveBuf = await httpsGetBuffer(`${baseUrl}/${archive}`);
70
+ const checksumsBuf = await httpsGetBuffer(`${baseUrl}/checksums.txt`);
93
71
 
94
- fs.mkdirSync(BIN_DIR, { recursive: true });
95
-
96
- if (ext === "zip") {
97
- const AdmZip = require("adm-zip");
98
- const zip = new AdmZip(archivePath);
99
- zip.extractEntryTo("juggernaut.exe", BIN_DIR, false, true);
100
- } else {
101
- const tar = require("tar");
102
- await tar.x({ file: archivePath, cwd: BIN_DIR });
103
- fs.chmodSync(path.join(BIN_DIR, "juggernaut"), 0o755);
104
- }
72
+ const checksums = checksumsBuf.toString("utf8");
73
+ const line = checksums.split("\n").find((l) => l.includes(archive));
74
+ if (!line) throw new Error(`Checksum not found for ${archive}`);
75
+ const expected = line.trim().split(/\s+/)[0].toLowerCase();
76
+ const actual = crypto.createHash("sha256").update(archiveBuf).digest("hex").toLowerCase();
77
+ if (actual !== expected) {
78
+ throw new Error(`Checksum mismatch for ${archive}\n expected: ${expected}\n got: ${actual}`);
79
+ }
105
80
 
106
- console.log(`Juggernaut v${version} installed successfully.`);
107
- console.log(`Run: juggernaut apply`);
108
- } finally {
109
- fs.rmSync(tmp, { recursive: true, force: true });
81
+ if (ext === "zip") {
82
+ const AdmZip = require("adm-zip");
83
+ const zip = new AdmZip(archiveBuf);
84
+ zip.extractEntryTo("juggernaut.exe", BIN_DIR, false, true);
85
+ } else {
86
+ const tar = require("tar");
87
+ await tar.x({ cwd: BIN_DIR, mode: 0o700, strict: true }, Readable.from(archiveBuf));
110
88
  }
89
+
90
+ console.log(`Juggernaut v${version} installed successfully.`);
91
+ console.log(`Run: juggernaut apply`);
111
92
  }
112
93
 
113
94
  main().catch((err) => {
114
95
  console.error("Installation failed:", err.message);
115
96
  process.exit(1);
116
- });
97
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "juggernaut-bedrock",
3
- "version": "4.0.0",
4
- "description": "Configure Claude Code to use Amazon Bedrock — cross-platform installer",
3
+ "version": "4.0.2",
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
6
  "juggernaut": "./bin/juggernaut"
7
7
  },
@@ -9,16 +9,34 @@
9
9
  "postinstall": "node install.js"
10
10
  },
11
11
  "dependencies": {
12
- "tar": "^6.0.0",
13
- "adm-zip": "^0.5.0"
12
+ "tar": "7.5.16",
13
+ "adm-zip": "0.5.17"
14
14
  },
15
15
  "os": ["darwin", "linux", "win32"],
16
16
  "cpu": ["x64", "arm64"],
17
- "keywords": ["claude", "bedrock", "aws", "anthropic", "claude-code"],
17
+ "keywords": [
18
+ "claude",
19
+ "claude-code",
20
+ "bedrock",
21
+ "amazon-bedrock",
22
+ "aws",
23
+ "anthropic",
24
+ "genai",
25
+ "ai-coding",
26
+ "llm",
27
+ "coding-agent",
28
+ "cli",
29
+ "iam",
30
+ "sso",
31
+ "developer-tools"
32
+ ],
18
33
  "license": "MIT",
19
34
  "repository": {
20
35
  "type": "git",
21
36
  "url": "https://github.com/jpvelasco/juggernaut"
22
37
  },
38
+ "bugs": {
39
+ "url": "https://github.com/jpvelasco/juggernaut/issues"
40
+ },
23
41
  "homepage": "https://github.com/jpvelasco/juggernaut#readme"
24
42
  }