claude-scionos 3.0.1 → 3.0.3

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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(node -p:*)",
5
+ "Bash(git add:*)",
6
+ "Bash(git commit:*)"
7
+ ]
8
+ }
9
+ }
package/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.0.3] - 2026-02-18
9
+
10
+ ### Changed
11
+ - **Model**: Renamed `GLM-4.7` to `Kimi K2.5` (`kimi-k2.5`) in the model selection menu.
12
+
13
+ ### Fixed
14
+ - **Token Validation**: Added a 10-second `AbortController` timeout on `validateToken()` to prevent the prompt from hanging on unresponsive network.
15
+ - **Proxy Memory**: Added a 100 MB cap on incoming request buffers; oversized requests now return HTTP 413 instead of causing a memory overflow.
16
+ - **CI/CD Compatibility**: `console.clear()` is now skipped when the `CI` environment variable is set or `--no-clear` is passed, preventing broken output in pipelines.
17
+
18
+ ## [3.0.2] - 2026-01-11
19
+
20
+ ### Added
21
+ - **Robust Proxy**: Integrated `undici` library for advanced HTTP agent control in the local proxy.
22
+ - **SSL Bypass**: Added support for internal/self-signed certificates (`rejectUnauthorized: false`) when using the proxy.
23
+
24
+ ### Fixed
25
+ - **Proxy Connectivity**: Fixed `fetch failed` and protocol errors by cleaning conflicting headers (`Host`, `Content-Length`) before upstream forwarding.
26
+ - **Code Quality**: Removed unused variables and dead code for cleaner execution.
27
+
8
28
  ## [3.0.1] - 2026-01-11
9
29
 
10
30
  ### Added
package/index.js CHANGED
@@ -24,7 +24,7 @@ const BASE_URL = "https://routerlab.ch";
24
24
  * Displays the application banner
25
25
  */
26
26
  function showBanner() {
27
- console.clear();
27
+ if (!process.env.CI && !process.argv.includes('--no-clear')) console.clear();
28
28
  const p = chalk.hex('#3b82f6'); // Primary (Scio)
29
29
  const s = chalk.hex('#a855f7'); // Secondary (Nos)
30
30
  const c = chalk.hex('#D97757'); // Claude Orange
@@ -47,13 +47,17 @@ function showBanner() {
47
47
  */
48
48
  async function validateToken(apiKey) {
49
49
  try {
50
+ const controller = new AbortController();
51
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
50
52
  const response = await fetch(`${BASE_URL}/v1/models`, {
51
53
  method: 'GET',
52
54
  headers: {
53
55
  'x-api-key': apiKey,
54
56
  'anthropic-version': '2023-06-01'
55
- }
57
+ },
58
+ signal: controller.signal
56
59
  });
60
+ clearTimeout(timeoutId);
57
61
 
58
62
  if (response.ok) {
59
63
  return { valid: true };
@@ -91,14 +95,25 @@ function startProxyServer(targetModel, validToken) {
91
95
  // Claude Code uses /v1/messages
92
96
  if (req.method === 'POST' && req.url.includes('/messages')) {
93
97
  const chunks = [];
94
- req.on('data', chunk => chunks.push(chunk));
98
+ const MAX_SIZE = 100 * 1024 * 1024; // 100MB
99
+ let totalSize = 0;
100
+ req.on('data', chunk => {
101
+ totalSize += chunk.length;
102
+ if (totalSize > MAX_SIZE) {
103
+ res.writeHead(413);
104
+ res.end(JSON.stringify({ error: { message: 'Request too large' } }));
105
+ req.destroy();
106
+ return;
107
+ }
108
+ chunks.push(chunk);
109
+ });
95
110
  req.on('end', async () => {
96
111
  try {
97
112
  const bodyBuffer = Buffer.concat(chunks);
98
113
  let bodyJson;
99
114
  try {
100
115
  bodyJson = JSON.parse(bodyBuffer.toString());
101
- } catch (e) {
116
+ } catch {
102
117
  // If not JSON, forward as is
103
118
  bodyJson = null;
104
119
  }
@@ -115,14 +130,26 @@ function startProxyServer(targetModel, validToken) {
115
130
  }
116
131
 
117
132
  // Prepare upstream request
133
+ // 1. Create an agent that ignores SSL errors (CRITICAL for internal/testing environments)
134
+ const { Agent } = await import('undici');
135
+ const dispatcher = new Agent({
136
+ connect: {
137
+ rejectUnauthorized: false // WARNING: Ignores SSL certificate errors
138
+ }
139
+ });
140
+
141
+ // 2. Remove problematic headers that fetch handles automatically
142
+ const upstreamHeaders = { ...req.headers };
143
+ delete upstreamHeaders['host']; // Let fetch set the correct Host based on URL
144
+ delete upstreamHeaders['content-length']; // Let fetch calculate length based on body
145
+ upstreamHeaders['x-api-key'] = validToken;
146
+
147
+ // 3. Execute request with the permissive dispatcher
118
148
  const upstreamRes = await fetch(`${BASE_URL}${req.url}`, {
119
149
  method: 'POST',
120
- headers: {
121
- ...req.headers,
122
- 'host': new URL(BASE_URL).host,
123
- 'x-api-key': validToken // Ensure we use the validated token
124
- },
125
- body: bodyJson ? JSON.stringify(bodyJson) : bodyBuffer
150
+ headers: upstreamHeaders,
151
+ body: bodyJson ? JSON.stringify(bodyJson) : bodyBuffer,
152
+ dispatcher: dispatcher // <--- Apply the custom agent here
126
153
  });
127
154
 
128
155
  // Pipe response back
@@ -150,13 +177,20 @@ function startProxyServer(targetModel, validToken) {
150
177
 
151
178
  // Simple Redirect implementation for non-body requests
152
179
  try {
180
+ // 1. Create agent (SSL bypass)
181
+ const { Agent } = await import('undici');
182
+ const dispatcher = new Agent({ connect: { rejectUnauthorized: false } });
183
+
184
+ // 2. Clean headers
185
+ const upstreamHeaders = { ...req.headers };
186
+ delete upstreamHeaders['host'];
187
+ delete upstreamHeaders['content-length'];
188
+ upstreamHeaders['x-api-key'] = validToken;
189
+
153
190
  const upstreamRes = await fetch(`${BASE_URL}${req.url}`, {
154
191
  method: req.method,
155
- headers: {
156
- ...req.headers,
157
- 'host': new URL(BASE_URL).host,
158
- 'x-api-key': validToken
159
- }
192
+ headers: upstreamHeaders,
193
+ dispatcher: dispatcher
160
194
  });
161
195
  res.writeHead(upstreamRes.status, upstreamRes.headers);
162
196
  if (upstreamRes.body) {
@@ -166,7 +200,7 @@ function startProxyServer(targetModel, validToken) {
166
200
  }
167
201
  }
168
202
  res.end();
169
- } catch (e) {
203
+ } catch {
170
204
  res.writeHead(502);
171
205
  res.end();
172
206
  }
@@ -231,7 +265,6 @@ if (!claudeStatus.installed) {
231
265
  }
232
266
 
233
267
  // Check Git Bash on Windows
234
- let gitBashPath = null;
235
268
  if (process.platform === 'win32') {
236
269
  const gitBashStatus = checkGitBashOnWindows();
237
270
  if (!gitBashStatus.available) {
@@ -239,7 +272,6 @@ if (process.platform === 'win32') {
239
272
  console.log(chalk.cyan('Please install Git for Windows: https://git-scm.com/downloads/win'));
240
273
  process.exit(1);
241
274
  }
242
- gitBashPath = gitBashStatus.path;
243
275
  }
244
276
 
245
277
  // 3. Token Loop
@@ -276,9 +308,9 @@ const modelChoice = await select({
276
308
  description: 'Standard behavior. Claude decides which model to use.'
277
309
  },
278
310
  {
279
- name: 'Force GLM-4.7 (Map all models to GLM-4.7)',
280
- value: 'glm-4.7',
281
- description: 'Intercepts traffic and routes everything to GLM-4.7'
311
+ name: 'Kimi K2.5',
312
+ value: 'kimi-k2.5',
313
+ description: 'Force all requests to Kimi K2.5'
282
314
  },
283
315
  {
284
316
  name: 'Force MiniMax-M2.1 (Map all models to MiniMax)',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-scionos",
3
- "version": "3.0.1",
3
+ "version": "3.0.3",
4
4
  "description": "Ephemeral and secure runner for Claude Code CLI in ScioNos environment",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -38,6 +38,7 @@
38
38
  "@inquirer/prompts": "^8.1.0",
39
39
  "chalk": "^5.6.2",
40
40
  "cross-spawn": "^7.0.6",
41
+ "undici": "^7.18.2",
41
42
  "update-notifier": "^7.3.1",
42
43
  "which": "^6.0.0"
43
44
  },
@@ -58,7 +58,7 @@ function isClaudeCodeInstalled() {
58
58
  cliPath = foundPath;
59
59
  details.push(`✓ Found in PATH: ${foundPath}`);
60
60
  }
61
- } catch (e) {
61
+ } catch {
62
62
  // Ignore error if not found in PATH
63
63
  }
64
64
  }