juggernaut-bedrock 4.0.4 → 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.
Files changed (3) hide show
  1. package/README.md +44 -7
  2. package/install.js +72 -25
  3. package/package.json +11 -4
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.7 |
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
@@ -11,13 +11,47 @@ const { promisify } = require("util");
11
11
  const execFileAsync = promisify(execFile);
12
12
 
13
13
  const REPO = "jpvelasco/juggernaut";
14
- const BIN_DIR = path.join(__dirname, "bin");
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";
15
20
  const ALLOWED_HOSTS = new Set([
16
21
  "github.com",
17
22
  "api.github.com",
18
23
  "release-assets.githubusercontent.com"
19
24
  ]);
20
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
+
21
55
  function getPlatform() {
22
56
  const osMap = { darwin: "darwin", linux: "linux", win32: "windows" };
23
57
  const archMap = { x64: "amd64", arm64: "arm64" };
@@ -64,9 +98,7 @@ async function getLatestVersion() {
64
98
 
65
99
  function pickArchive(platform, checksumsText) {
66
100
  const tarArchive = `juggernaut_${platform}.tar.gz`;
67
- if (checksumsText.includes(tarArchive)) {
68
- return { name: tarArchive, kind: "tar.gz" };
69
- }
101
+ if (checksumsText.includes(tarArchive)) return { name: tarArchive, kind: "tar.gz" };
70
102
  const zipArchive = `juggernaut_${platform}.zip`;
71
103
  if (process.platform === "win32" && checksumsText.includes(zipArchive)) {
72
104
  return { name: zipArchive, kind: "zip" };
@@ -75,11 +107,14 @@ function pickArchive(platform, checksumsText) {
75
107
  }
76
108
 
77
109
  function extractTarGz(archiveBuf) {
78
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "juggernaut-install-"));
79
- const archivePath = path.join(tmpDir, "archive.tar.gz");
110
+ const tmpDir = fs.mkdtempSync(TMP_PREFIX);
111
+ const archivePath = joinUnder(tmpDir, ARCHIVE_TAR);
112
+ const prevCwd = process.cwd();
80
113
  try {
81
- fs.writeFileSync(archivePath, archiveBuf);
82
- fs.mkdirSync(BIN_DIR, { recursive: true });
114
+ process.chdir(tmpDir);
115
+ fs.writeFileSync(ARCHIVE_TAR, archiveBuf);
116
+ process.chdir(PACKAGE_DIR);
117
+ fs.mkdirSync("bin", { recursive: true });
83
118
  try {
84
119
  execFileSync("tar", ["-xzf", archivePath, "-C", BIN_DIR], { stdio: "pipe" });
85
120
  } catch (err) {
@@ -92,32 +127,47 @@ function extractTarGz(archiveBuf) {
92
127
  throw new Error(`tar extraction failed: ${detail}`);
93
128
  }
94
129
  if (process.platform !== "win32") {
95
- const binPath = path.join(BIN_DIR, "juggernaut");
96
- if (fs.existsSync(binPath)) {
97
- fs.chmodSync(binPath, 0o700);
130
+ process.chdir(BIN_DIR);
131
+ if (fs.existsSync(BIN_NAME)) {
132
+ fs.chmodSync(BIN_NAME, 0o700);
98
133
  }
99
134
  }
100
135
  } finally {
136
+ process.chdir(prevCwd);
101
137
  fs.rmSync(tmpDir, { recursive: true, force: true });
102
138
  }
103
139
  }
104
140
 
105
141
  async function extractZip(archiveBuf) {
106
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "juggernaut-install-"));
107
- const archivePath = path.join(tmpDir, "archive.zip");
142
+ const tmpDir = fs.mkdtempSync(TMP_PREFIX);
143
+ const archivePath = joinUnder(tmpDir, ARCHIVE_ZIP);
144
+ const prevCwd = process.cwd();
108
145
  try {
109
- fs.writeFileSync(archivePath, archiveBuf);
110
- fs.mkdirSync(BIN_DIR, { recursive: true });
146
+ process.chdir(tmpDir);
147
+ fs.writeFileSync(ARCHIVE_ZIP, archiveBuf);
148
+ process.chdir(PACKAGE_DIR);
149
+ fs.mkdirSync("bin", { recursive: true });
111
150
  const script = [
112
151
  "$ErrorActionPreference = 'Stop'",
113
152
  `Expand-Archive -LiteralPath '${archivePath.replace(/'/g, "''")}' -DestinationPath '${BIN_DIR.replace(/'/g, "''")}' -Force`
114
153
  ].join("; ");
115
- await execFileAsync(
116
- "powershell",
117
- ["-NoProfile", "-NonInteractive", "-Command", script],
118
- { stdio: "pipe" }
119
- );
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
+ }
120
169
  } finally {
170
+ process.chdir(prevCwd);
121
171
  fs.rmSync(tmpDir, { recursive: true, force: true });
122
172
  }
123
173
  }
@@ -141,11 +191,8 @@ async function main() {
141
191
  throw new Error(`Checksum mismatch for ${archive}\n expected: ${expected}\n got: ${actual}`);
142
192
  }
143
193
 
144
- if (kind === "zip") {
145
- await extractZip(archiveBuf);
146
- } else {
147
- extractTarGz(archiveBuf);
148
- }
194
+ if (kind === "zip") await extractZip(archiveBuf);
195
+ else extractTarGz(archiveBuf);
149
196
 
150
197
  console.log(`Juggernaut v${version} installed successfully.`);
151
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.4",
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,8 +8,15 @@
8
8
  "scripts": {
9
9
  "postinstall": "node install.js"
10
10
  },
11
- "os": ["darwin", "linux", "win32"],
12
- "cpu": ["x64", "arm64"],
11
+ "os": [
12
+ "darwin",
13
+ "linux",
14
+ "win32"
15
+ ],
16
+ "cpu": [
17
+ "x64",
18
+ "arm64"
19
+ ],
13
20
  "keywords": [
14
21
  "claude",
15
22
  "claude-code",
@@ -35,4 +42,4 @@
35
42
  "url": "https://github.com/jpvelasco/juggernaut/issues"
36
43
  },
37
44
  "homepage": "https://github.com/jpvelasco/juggernaut#readme"
38
- }
45
+ }