claude-scionos 3.0.2 → 3.0.5

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,31 @@ 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.5] - 2026-03-16
9
+
10
+ ### Added
11
+ - **Dynamic AWS Models**: Integrated dynamic routing for AWS 50% discount models (`haiku`, `sonnet`, `opus`) in the local proxy.
12
+ - **Model Selection**: Cleaned up the initial prompt menu to offer 4 robust strategic choices including GLM-5 and MiniMax M2.5.
13
+
14
+ ## [3.0.4] - 2026-03-16
15
+
16
+ ### Changed
17
+ - **Dependencies**: Replaced `chalk`, `cross-spawn`, and `undici` with Node.js >= 22 native APIs (`util.styleText`, `child_process.spawn`, `fetch`).
18
+ - **Dependencies**: Updated development and production packages to the latest versions.
19
+
20
+ ### Fixed
21
+ - **Code Quality**: Fixed `Token` ESLint assignment warnings and improved formatting compatibility.
22
+
23
+ ## [3.0.3] - 2026-02-18
24
+
25
+ ### Changed
26
+ - **Model**: Renamed `GLM-4.7` to `Kimi K2.5` (`kimi-k2.5`) in the model selection menu.
27
+
28
+ ### Fixed
29
+ - **Token Validation**: Added a 10-second `AbortController` timeout on `validateToken()` to prevent the prompt from hanging on unresponsive network.
30
+ - **Proxy Memory**: Added a 100 MB cap on incoming request buffers; oversized requests now return HTTP 413 instead of causing a memory overflow.
31
+ - **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.
32
+
8
33
  ## [3.0.2] - 2026-01-11
9
34
 
10
35
  ### Added
package/index.js CHANGED
@@ -1,8 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import chalk from 'chalk';
3
+ import { styleText } from 'node:util';
4
+ const chalk = {
5
+ hex: (color) => {
6
+ if (color === '#3b82f6') return (t) => styleText('blueBright', t);
7
+ if (color === '#a855f7') return (t) => styleText('magentaBright', t);
8
+ if (color === '#D97757') return (t) => styleText('redBright', t);
9
+ return (t) => t;
10
+ },
11
+ white: (t) => styleText('white', t),
12
+ gray: (t) => styleText('gray', t),
13
+ yellow: (t) => styleText('yellow', t),
14
+ red: (t) => styleText('red', t),
15
+ cyan: (t) => styleText('cyan', t),
16
+ redBright: (t) => styleText('redBright', t),
17
+ blueBright: (t) => styleText('blueBright', t),
18
+ green: (t) => styleText('green', t),
19
+ magenta: (t) => styleText('magenta', t),
20
+ bold: (t) => styleText('bold', t)
21
+ };
4
22
  import { password, confirm, select } from '@inquirer/prompts';
5
- import spawn from 'cross-spawn';
23
+ import { spawn } from 'node:child_process';
6
24
  import updateNotifier from 'update-notifier';
7
25
  import process from 'node:process';
8
26
  import http from 'node:http';
@@ -24,7 +42,7 @@ const BASE_URL = "https://routerlab.ch";
24
42
  * Displays the application banner
25
43
  */
26
44
  function showBanner() {
27
- console.clear();
45
+ if (!process.env.CI && !process.argv.includes('--no-clear')) console.clear();
28
46
  const p = chalk.hex('#3b82f6'); // Primary (Scio)
29
47
  const s = chalk.hex('#a855f7'); // Secondary (Nos)
30
48
  const c = chalk.hex('#D97757'); // Claude Orange
@@ -35,7 +53,7 @@ function showBanner() {
35
53
  console.log("");
36
54
  console.log(border(" ┌──────────────────────────────────────────────────────────┐"));
37
55
  console.log(border(" │ │"));
38
- console.log(border(" │ ") + p.bold("Scio") + s.bold("Nos") + w.bold(" ✕ ") + c.bold("Claude Code") + border(" │"));
56
+ console.log(border(" │ ") + chalk.bold(p("Scio")) + chalk.bold(s("Nos")) + chalk.bold(w(" ✕ ")) + chalk.bold(c("Claude Code")) + border(" │"));
39
57
  console.log(border(" │ │"));
40
58
  console.log(border(" └──────────────────────────────────────────────────────────┘"));
41
59
  console.log(g(` v${pkg.version}`));
@@ -47,13 +65,17 @@ function showBanner() {
47
65
  */
48
66
  async function validateToken(apiKey) {
49
67
  try {
68
+ const controller = new AbortController();
69
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
50
70
  const response = await fetch(`${BASE_URL}/v1/models`, {
51
71
  method: 'GET',
52
72
  headers: {
53
73
  'x-api-key': apiKey,
54
74
  'anthropic-version': '2023-06-01'
55
- }
75
+ },
76
+ signal: controller.signal
56
77
  });
78
+ clearTimeout(timeoutId);
57
79
 
58
80
  if (response.ok) {
59
81
  return { valid: true };
@@ -91,7 +113,18 @@ function startProxyServer(targetModel, validToken) {
91
113
  // Claude Code uses /v1/messages
92
114
  if (req.method === 'POST' && req.url.includes('/messages')) {
93
115
  const chunks = [];
94
- req.on('data', chunk => chunks.push(chunk));
116
+ const MAX_SIZE = 100 * 1024 * 1024; // 100MB
117
+ let totalSize = 0;
118
+ req.on('data', chunk => {
119
+ totalSize += chunk.length;
120
+ if (totalSize > MAX_SIZE) {
121
+ res.writeHead(413);
122
+ res.end(JSON.stringify({ error: { message: 'Request too large' } }));
123
+ req.destroy();
124
+ return;
125
+ }
126
+ chunks.push(chunk);
127
+ });
95
128
  req.on('end', async () => {
96
129
  try {
97
130
  const bodyBuffer = Buffer.concat(chunks);
@@ -105,13 +138,22 @@ function startProxyServer(targetModel, validToken) {
105
138
 
106
139
  // THE MAGIC: Swap the model
107
140
  if (bodyJson && bodyJson.model) {
108
- // Map any Claude model to our target
109
- // Claude Code usually requests 'claude-3-opus-...' or 'claude-3-5-sonnet...'
110
- // We force the target.
141
+ let newModel = targetModel;
142
+ if (targetModel === 'aws') {
143
+ if (bodyJson.model.includes('haiku')) {
144
+ newModel = 'aws-claude-haiku-4-5-20251001';
145
+ } else if (bodyJson.model.includes('opus')) {
146
+ newModel = 'aws-claude-opus-4-6';
147
+ } else {
148
+ // Default to sonnet for AWS if not haiku or opus
149
+ newModel = 'aws-claude-sonnet-4-6';
150
+ }
151
+ }
152
+
111
153
  if (process.argv.includes('--scionos-debug')) {
112
- console.log(chalk.yellow(`[Proxy] Swapping model ${bodyJson.model} -> ${targetModel}`));
154
+ console.log(chalk.yellow(`[Proxy] Swapping model ${bodyJson.model} -> ${newModel}`));
113
155
  }
114
- bodyJson.model = targetModel;
156
+ bodyJson.model = newModel;
115
157
  }
116
158
 
117
159
  // Prepare upstream request
@@ -232,7 +274,7 @@ if (!claudeStatus.installed) {
232
274
  if (shouldInstall) {
233
275
  try {
234
276
  console.log(chalk.cyan('\n📦 Installing @anthropic-ai/claude-code...'));
235
- spawn.sync('npm', ['install', '-g', '@anthropic-ai/claude-code'], { stdio: 'inherit' });
277
+ spawn.sync('npm', ['install', '-g', '@anthropic-ai/claude-code'], { stdio: 'inherit', shell: process.platform === 'win32' });
236
278
  claudeStatus = isClaudeCodeInstalled();
237
279
  if (!claudeStatus.installed) {
238
280
  console.warn(chalk.yellow('⚠ Installation finished, but executable not found immediately. Restart terminal recommended.'));
@@ -260,7 +302,7 @@ if (process.platform === 'win32') {
260
302
  }
261
303
 
262
304
  // 3. Token Loop
263
- let token = "";
305
+ let token;
264
306
  while (true) {
265
307
  console.log(chalk.blueBright("To retrieve your token, visit: https://routerlab.ch/keys"));
266
308
  token = await password({
@@ -288,19 +330,24 @@ const modelChoice = await select({
288
330
  message: 'Select Model Strategy:',
289
331
  choices: [
290
332
  {
291
- name: 'Default (Use Claude Opus/Sonnet/Haiku natively)',
333
+ name: 'Default (Use Claude natively)',
292
334
  value: 'default',
293
335
  description: 'Standard behavior. Claude decides which model to use.'
294
336
  },
295
337
  {
296
- name: 'Force GLM-4.7 (Map all models to GLM-4.7)',
297
- value: 'glm-4.7',
298
- description: 'Intercepts traffic and routes everything to GLM-4.7'
338
+ name: 'Claude AWS (-50% du prix 💰)',
339
+ value: 'aws',
340
+ description: 'Map models to aws-claude-haiku, aws-claude-sonnet, aws-claude-opus'
341
+ },
342
+ {
343
+ name: 'GLM-5',
344
+ value: 'glm-5',
345
+ description: 'Remplace tous les modèles par glm-5'
299
346
  },
300
347
  {
301
- name: 'Force MiniMax-M2.1 (Map all models to MiniMax)',
302
- value: 'minimax-m2.1',
303
- description: 'Intercepts traffic and routes everything to MiniMax-M2.1'
348
+ name: 'MiniMax M2.5',
349
+ value: 'minimax-m2.5',
350
+ description: 'Remplace tous les modèles par minimax-m2.5'
304
351
  }
305
352
  ]
306
353
  });
@@ -342,7 +389,8 @@ console.log(chalk.green(`\n🚀 Launching Claude Code [${modelChoice}]...\n`));
342
389
 
343
390
  const child = spawn(claudeStatus.cliPath, args, {
344
391
  stdio: 'inherit',
345
- env: env
392
+ env: env,
393
+ shell: process.platform === 'win32'
346
394
  });
347
395
 
348
396
  // 7. Cleanup Handlers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-scionos",
3
- "version": "3.0.2",
3
+ "version": "3.0.5",
4
4
  "description": "Ephemeral and secure runner for Claude Code CLI in ScioNos environment",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -35,17 +35,14 @@
35
35
  },
36
36
  "private": false,
37
37
  "dependencies": {
38
- "@inquirer/prompts": "^8.1.0",
39
- "chalk": "^5.6.2",
40
- "cross-spawn": "^7.0.6",
41
- "undici": "^7.18.2",
38
+ "@inquirer/prompts": "^8.3.2",
42
39
  "update-notifier": "^7.3.1",
43
- "which": "^6.0.0"
40
+ "which": "^6.0.1"
44
41
  },
45
42
  "devDependencies": {
46
- "@eslint/js": "^9.39.2",
47
- "eslint": "^9.39.2",
48
- "globals": "^17.0.0",
49
- "vitest": "^4.0.16"
43
+ "@eslint/js": "^10.0.1",
44
+ "eslint": "^10.0.3",
45
+ "globals": "^17.4.0",
46
+ "vitest": "^4.1.0"
50
47
  }
51
48
  }