juggernaut-bedrock 4.0.3 → 4.1.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/README.md +44 -7
- package/install.js +122 -15
- package/package.json +10 -7
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ juggernaut apply --auth=iam
|
|
|
50
50
|
That one command:
|
|
51
51
|
|
|
52
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
|
|
53
|
+
2. **Sets** model IDs, region, effort level, permission mode, and `CLAUDE_CODE_USE_BEDROCK=1` — only after credentials check out
|
|
54
54
|
3. **Installs** a `claude` launcher shim that reads your token from the keychain and execs the real binary
|
|
55
55
|
|
|
56
56
|
No `.bashrc` edits. No copying API keys into env vars. No "why isn't Bedrock routing?" at 2am.
|
|
@@ -87,14 +87,51 @@ No `.bashrc` edits. No copying API keys into env vars. No "why isn't Bedrock rou
|
|
|
87
87
|
|
|
88
88
|
## Default models
|
|
89
89
|
|
|
90
|
-
| Tier | Model |
|
|
91
|
-
|
|
92
|
-
| **Primary** | Claude Sonnet 4.6 |
|
|
93
|
-
| **Opus** | Claude Opus 4.
|
|
94
|
-
| **Fast** | Claude Haiku 4.5 |
|
|
90
|
+
| Tier | Model | Global inference profile |
|
|
91
|
+
|------|-------|--------------------------|
|
|
92
|
+
| **Primary** | Claude Sonnet 4.6 | `global.anthropic.claude-sonnet-4-6` |
|
|
93
|
+
| **Opus** | Claude Opus 4.8 | `global.anthropic.claude-opus-4-8` |
|
|
94
|
+
| **Fast** | Claude Haiku 4.5 | `global.anthropic.claude-haiku-4-5-20251001-v1:0` |
|
|
95
95
|
|
|
96
96
|
Override any tier: `juggernaut apply --auth=iam --model=global.anthropic.claude-sonnet-4-6`
|
|
97
97
|
|
|
98
|
+
## Effort levels
|
|
99
|
+
|
|
100
|
+
Controls adaptive thinking depth. Valid values: `low | medium | high | xhigh | max`
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
juggernaut apply --auth=iam --effort=max
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Default: `xhigh`. On Opus 4.8/4.7, effort level controls adaptive thinking depth — manual thinking mode is not supported.
|
|
107
|
+
|
|
108
|
+
## Permission modes
|
|
109
|
+
|
|
110
|
+
Controls how Claude Code handles tool-use approvals:
|
|
111
|
+
|
|
112
|
+
| Mode | Behavior |
|
|
113
|
+
|------|----------|
|
|
114
|
+
| `default` | Prompts for each action |
|
|
115
|
+
| `acceptEdits` | Auto-approves file edits |
|
|
116
|
+
| `plan` | Propose only, no execution |
|
|
117
|
+
| `auto` | Agentic safety classifier |
|
|
118
|
+
| `bypassPermissions` | Skip all prompts (containers/VMs only) |
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
juggernaut apply --auth=iam --mode=auto
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Auto mode on Bedrock requires `CLAUDE_CODE_ENABLE_AUTO_MODE=1` — Juggernaut sets this automatically.
|
|
125
|
+
|
|
126
|
+
## Other options
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
--always-thinking # enable extended thinking by default (alwaysThinkingEnabled)
|
|
130
|
+
--service-tier=flex # Bedrock service tier: default | flex | priority
|
|
131
|
+
--opusplan # route /plan to Opus 4.8, execution to Sonnet 4.6
|
|
132
|
+
--scope=project # write to ./.claude/settings.json instead of ~/.claude/
|
|
133
|
+
```
|
|
134
|
+
|
|
98
135
|
## Troubleshooting
|
|
99
136
|
|
|
100
137
|
Stuck? Start here:
|
|
@@ -115,4 +152,4 @@ Full docs, IAM policy, migration guide, and platform notes:
|
|
|
115
152
|
|
|
116
153
|
MIT — see [LICENSE](https://github.com/jpvelasco/juggernaut/blob/main/LICENSE).
|
|
117
154
|
|
|
118
|
-
Juggernaut is an independent tool, not affiliated with Anthropic or Amazon Web Services.
|
|
155
|
+
Juggernaut is an independent tool, not affiliated with Anthropic or Amazon Web Services.
|
package/install.js
CHANGED
|
@@ -3,17 +3,55 @@
|
|
|
3
3
|
const https = require("https");
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
|
-
const
|
|
6
|
+
const os = require("os");
|
|
7
7
|
const crypto = require("crypto");
|
|
8
|
+
const { execFile, execFileSync } = require("child_process");
|
|
9
|
+
const { promisify } = require("util");
|
|
10
|
+
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
8
12
|
|
|
9
13
|
const REPO = "jpvelasco/juggernaut";
|
|
10
|
-
const
|
|
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";
|
|
11
20
|
const ALLOWED_HOSTS = new Set([
|
|
12
21
|
"github.com",
|
|
13
22
|
"api.github.com",
|
|
14
23
|
"release-assets.githubusercontent.com"
|
|
15
24
|
]);
|
|
16
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
|
+
|
|
17
55
|
function getPlatform() {
|
|
18
56
|
const osMap = { darwin: "darwin", linux: "linux", win32: "windows" };
|
|
19
57
|
const archMap = { x64: "amd64", arm64: "arm64" };
|
|
@@ -58,18 +96,93 @@ async function getLatestVersion() {
|
|
|
58
96
|
return release.tag_name.replace(/^v/, "");
|
|
59
97
|
}
|
|
60
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
|
+
|
|
61
175
|
async function main() {
|
|
62
|
-
const { platform
|
|
176
|
+
const { platform } = getPlatform();
|
|
63
177
|
const version = await getLatestVersion();
|
|
64
|
-
const ext = osName === "windows" ? "zip" : "tar.gz";
|
|
65
|
-
const archive = `juggernaut_${platform}.${ext}`;
|
|
66
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);
|
|
67
182
|
|
|
68
183
|
console.log(`Downloading Juggernaut v${version} (${platform})...`);
|
|
69
184
|
const archiveBuf = await httpsGetBuffer(`${baseUrl}/${archive}`);
|
|
70
|
-
const checksumsBuf = await httpsGetBuffer(`${baseUrl}/checksums.txt`);
|
|
71
185
|
|
|
72
|
-
const checksums = checksumsBuf.toString("utf8");
|
|
73
186
|
const line = checksums.split("\n").find((l) => l.includes(archive));
|
|
74
187
|
if (!line) throw new Error(`Checksum not found for ${archive}`);
|
|
75
188
|
const expected = line.trim().split(/\s+/)[0].toLowerCase();
|
|
@@ -78,14 +191,8 @@ async function main() {
|
|
|
78
191
|
throw new Error(`Checksum mismatch for ${archive}\n expected: ${expected}\n got: ${actual}`);
|
|
79
192
|
}
|
|
80
193
|
|
|
81
|
-
if (
|
|
82
|
-
|
|
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));
|
|
88
|
-
}
|
|
194
|
+
if (kind === "zip") await extractZip(archiveBuf);
|
|
195
|
+
else extractTarGz(archiveBuf);
|
|
89
196
|
|
|
90
197
|
console.log(`Juggernaut v${version} installed successfully.`);
|
|
91
198
|
console.log(`Run: juggernaut apply`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juggernaut-bedrock",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
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
6
|
"juggernaut": "./bin/juggernaut"
|
|
@@ -8,12 +8,15 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"postinstall": "node install.js"
|
|
10
10
|
},
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"cpu": [
|
|
11
|
+
"os": [
|
|
12
|
+
"darwin",
|
|
13
|
+
"linux",
|
|
14
|
+
"win32"
|
|
15
|
+
],
|
|
16
|
+
"cpu": [
|
|
17
|
+
"x64",
|
|
18
|
+
"arm64"
|
|
19
|
+
],
|
|
17
20
|
"keywords": [
|
|
18
21
|
"claude",
|
|
19
22
|
"claude-code",
|