claude-scionos 3.0.5 → 3.0.7

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/CHANGELOG.md CHANGED
@@ -1,143 +1,176 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
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
-
33
- ## [3.0.2] - 2026-01-11
34
-
35
- ### Added
36
- - **Robust Proxy**: Integrated `undici` library for advanced HTTP agent control in the local proxy.
37
- - **SSL Bypass**: Added support for internal/self-signed certificates (`rejectUnauthorized: false`) when using the proxy.
38
-
39
- ### Fixed
40
- - **Proxy Connectivity**: Fixed `fetch failed` and protocol errors by cleaning conflicting headers (`Host`, `Content-Length`) before upstream forwarding.
41
- - **Code Quality**: Removed unused variables and dead code for cleaner execution.
42
-
43
- ## [3.0.1] - 2026-01-11
44
-
45
- ### Added
46
- - **Model Mapping & Proxy**: Integrated local proxy to transparently map Claude models to **GLM-4.7** or **MiniMax-M2.1**.
47
- - **Active Token Validation**: Now validates the `ANTHROPIC_AUTH_TOKEN` against the `routerlab.ch` API in real-time before launching.
48
- - **Interactive Menu**: Added a selection menu at startup to choose the model strategy (Default vs Mapped).
49
- - **Pro Branding**: New professional "ScioNos ✕ Claude Code" banner with corporate colors.
50
-
51
- ### Improved
52
- - **Error Handling**: Better distinction between missing executable (`ENOENT`) and permission errors (`EACCES`).
53
- - **User Interface**: Clearer validation steps and visual feedback.
54
-
55
- ## [2.2.0] - 2026-01-06
56
-
57
- ### Added
58
- - **Auto-Installation**: Prompts users to automatically install Claude Code CLI (`npm install -g`) if missing.
59
- - **Native Path Detection**: Now detects Claude Code installations in native paths (`~/.local/bin`, Windows Apps, etc.) per official docs.
60
- - **SIGTERM Support**: Added handling for `SIGTERM` signals (Docker, CI/CD) to cleanly stop the child process.
61
-
62
- ### Fixed
63
- - **Crash on Config-Only**: Fixed a critical bug where the wrapper would crash if a configuration file existed but the CLI executable was missing.
64
- - **Recursion Safety**: Now launches the detected absolute path of the executable instead of the generic command name, preventing potential loop issues.
65
- - **Error Logging**: Errors are now correctly sent to `stderr` instead of `stdout`.
66
-
67
- ## [2.1.0] - 2026-01-06
68
-
69
- ### Added
70
- - **Debug Mode**: New `--scionos-debug` flag for detailed diagnostic output
71
- - **Test Infrastructure**: Added Vitest test suite covering core detection logic
72
- - **Linting**: Fixed development environment and linting rules
73
-
74
- ### Fixed
75
- - **Windows Path Handling**: Fixed an issue where `where claude` returned multiple paths on Windows
76
- - **Signal Handling**: Improved `SIGINT` (Ctrl+C) handling to prevent wrapper from killing Claude prematurely
77
-
78
- ## [2.0.0] - 2025-12-12
79
-
80
- ### ⚠️ BREAKING CHANGES
81
- - Enhanced detection system replaces basic checks
82
- - Improved error handling with detailed diagnostics
83
- - Better cross-platform support and user guidance
84
-
85
- ### Added
86
- - **Advanced System Detection**: Comprehensive OS, shell, and environment detection
87
- - **Enhanced Claude Code Detection**: Checks both directory and CLI availability with detailed status
88
- - **Improved Git Bash Detection**: Better Windows support with automatic path discovery
89
- - **Detailed Error Messages**: Actionable troubleshooting information for users
90
- - **System Information Display**: Shows detected OS and shell during startup
91
- - **Modular Architecture**: New `src/detectors/` directory for extensible detection logic
92
-
93
- ### Improved
94
- - **User Experience**: Much clearer feedback when something goes wrong
95
- - **Cross-Platform Compatibility**: Better handling of Windows, macOS, and Linux
96
- - **Installation Guidance**: OS-specific instructions when dependencies are missing
97
- - **Code Organization**: Cleaner separation of concerns with dedicated detection modules
98
-
99
- ### Technical Changes
100
- - Replaced basic `which()` checks with comprehensive detection system
101
- - Added support for detecting Claude Code configuration files
102
- - Enhanced shell detection (PowerShell, CMD, bash, zsh, fish)
103
- - Improved error recovery and user guidance
104
-
105
- ## [1.0.1] - 2025-11-29
106
-
107
- ### Added
108
- - Git Bash detection for Windows users
109
- - Clear error message when Git Bash is missing on Windows
110
- - Troubleshooting section for Git Bash in README.md
111
- - Section de dépannage pour Git Bash dans README.fr.md
112
- - Support for `CLAUDE_CODE_GIT_BASH_PATH` environment variable
113
-
114
- ### Fixed
115
- - Windows users no longer blocked after entering token due to missing Git Bash
116
- - Better error handling for Windows environment requirements
117
-
118
- ### Improved
119
- - User experience for Windows beta testers
120
- - Documentation clarity for Windows-specific requirements
121
- - Error messages now provide actionable solutions
122
-
123
- ## [1.0.0] - 2025-11-28
124
-
125
- ### Added
126
- - Initial release
127
- - Ephemeral and secure token handling (memory-only storage)
128
- - Support for ScioNos environment (`https://routerlab.ch`)
129
- - Bilingual documentation (English and French)
130
- - Command-line interface with `--version` flag
131
- - Secure token input with masking
132
- - Automatic cleanup on process exit
133
- - Zero persistence (no files written to disk)
134
-
135
- ### Security
136
- - Tokens stored only in memory
137
- - No configuration files created
138
- - Automatic credential cleanup on exit
139
- - Environment variable isolation
140
-
141
- [2.0.0]: https://github.com/ScioNos/claude-scionos/compare/v1.0.1...v2.0.0
142
- [1.0.1]: https://github.com/ScioNos/claude-scionos/compare/v1.0.0...v1.0.1
143
- [1.0.0]: https://github.com/ScioNos/claude-scionos/releases/tag/v1.0.0
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [3.0.7] - 2026-03-23
9
+
10
+ ### Fixed
11
+ - **Auto-Installation**: Replaced the broken `spawn.sync(...)` call with `spawnSync(...)` so Claude Code can be installed automatically when missing.
12
+ - **Model Compatibility**: Updated GLM-5 and MiniMax M2.5 mappings to use the RouterLab model IDs `claude-glm-5` and `claude-minimax-m2.5`.
13
+ - **Windows Warning**: Removed `shell: true` from child process launches to avoid the `DEP0190` deprecation warning.
14
+ - **Test Stability**: Fixed the Windows detector test so it no longer depends on `PSModulePath` leaking from the host environment.
15
+
16
+ ### Changed
17
+ - **Packaging**: Limited published npm files to the runtime assets and removed `update-notifier` from runtime dependencies to keep the CLI ephemeral.
18
+ - **Documentation**: Updated the README files to match the current model strategy names and RouterLab identifiers.
19
+
20
+ ## [3.0.6] - 2026-03-17
21
+
22
+ ### Fixed
23
+ - **Proxy Connectivity**: Replaced `undici` with native `node:https` module to fix "Cannot find package 'undici'" and "fetch failed" errors.
24
+ - **Model Compatibility**: Updated GLM-5 and MiniMax to use the Anthropic-compatible proxy model IDs available at the time.
25
+ - **Error Handling**: Improved error logging with detailed error codes and causes for easier debugging.
26
+
27
+ ### Improved
28
+ - **Timeouts**: Increased token validation timeout from 10s to 30s; added 60-120s timeouts for proxy requests.
29
+ - **Retry Logic**: Added exponential backoff retry for transient 502/503/504 errors.
30
+ - **Debug Mode**: Enhanced `--scionos-debug` output with request/response status logging.
31
+
32
+ ## [3.0.5] - 2026-03-16
33
+
34
+ ### Added
35
+ - **Dynamic AWS Models**: Integrated dynamic routing for AWS 50% discount models (`haiku`, `sonnet`, `opus`) in the local proxy.
36
+ - **Model Selection**: Cleaned up the initial prompt menu to offer 4 robust strategic choices including GLM-5 and MiniMax M2.5.
37
+
38
+ ## [3.0.4] - 2026-03-16
39
+
40
+ ### Changed
41
+ - **Dependencies**: Replaced `chalk`, `cross-spawn`, and `undici` with Node.js >= 22 native APIs (`util.styleText`, `child_process.spawn`, `fetch`).
42
+ - **Dependencies**: Updated development and production packages to the latest versions.
43
+
44
+ ### Fixed
45
+ - **Code Quality**: Fixed `Token` ESLint assignment warnings and improved formatting compatibility.
46
+
47
+ ## [3.0.3] - 2026-02-18
48
+
49
+ ### Changed
50
+ - **Model**: Renamed `GLM-4.7` to `Kimi K2.5` (`kimi-k2.5`) in the model selection menu.
51
+
52
+ ### Fixed
53
+ - **Token Validation**: Added a 10-second `AbortController` timeout on `validateToken()` to prevent the prompt from hanging on unresponsive network.
54
+ - **Proxy Memory**: Added a 100 MB cap on incoming request buffers; oversized requests now return HTTP 413 instead of causing a memory overflow.
55
+ - **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.
56
+
57
+ ## [3.0.2] - 2026-01-11
58
+
59
+ ### Added
60
+ - **Robust Proxy**: Integrated `undici` library for advanced HTTP agent control in the local proxy.
61
+ - **SSL Bypass**: Added support for internal/self-signed certificates (`rejectUnauthorized: false`) when using the proxy.
62
+
63
+ ### Fixed
64
+ - **Proxy Connectivity**: Fixed `fetch failed` and protocol errors by cleaning conflicting headers (`Host`, `Content-Length`) before upstream forwarding.
65
+ - **Code Quality**: Removed unused variables and dead code for cleaner execution.
66
+
67
+ ## [3.0.1] - 2026-01-11
68
+
69
+ ### Added
70
+ - **Model Mapping & Proxy**: Integrated local proxy to transparently map Claude models to **GLM-4.7** or **MiniMax-M2.1**.
71
+ - **Active Token Validation**: Now validates the `ANTHROPIC_AUTH_TOKEN` against the `routerlab.ch` API in real-time before launching.
72
+ - **Interactive Menu**: Added a selection menu at startup to choose the model strategy (Default vs Mapped).
73
+ - **Pro Branding**: New professional "ScioNos ✕ Claude Code" banner with corporate colors.
74
+
75
+ ### Improved
76
+ - **Error Handling**: Better distinction between missing executable (`ENOENT`) and permission errors (`EACCES`).
77
+ - **User Interface**: Clearer validation steps and visual feedback.
78
+
79
+ ## [2.2.0] - 2026-01-06
80
+
81
+ ### Added
82
+ - **Auto-Installation**: Prompts users to automatically install Claude Code CLI (`npm install -g`) if missing.
83
+ - **Native Path Detection**: Now detects Claude Code installations in native paths (`~/.local/bin`, Windows Apps, etc.) per official docs.
84
+ - **SIGTERM Support**: Added handling for `SIGTERM` signals (Docker, CI/CD) to cleanly stop the child process.
85
+
86
+ ### Fixed
87
+ - **Crash on Config-Only**: Fixed a critical bug where the wrapper would crash if a configuration file existed but the CLI executable was missing.
88
+ - **Recursion Safety**: Now launches the detected absolute path of the executable instead of the generic command name, preventing potential loop issues.
89
+ - **Error Logging**: Errors are now correctly sent to `stderr` instead of `stdout`.
90
+
91
+ ## [2.1.0] - 2026-01-06
92
+
93
+ ### Added
94
+ - **Debug Mode**: New `--scionos-debug` flag for detailed diagnostic output
95
+ - **Test Infrastructure**: Added Vitest test suite covering core detection logic
96
+ - **Linting**: Fixed development environment and linting rules
97
+
98
+ ### Fixed
99
+ - **Windows Path Handling**: Fixed an issue where `where claude` returned multiple paths on Windows
100
+ - **Signal Handling**: Improved `SIGINT` (Ctrl+C) handling to prevent wrapper from killing Claude prematurely
101
+
102
+ ## [2.0.0] - 2025-12-12
103
+
104
+ ### ⚠️ BREAKING CHANGES
105
+ - Enhanced detection system replaces basic checks
106
+ - Improved error handling with detailed diagnostics
107
+ - Better cross-platform support and user guidance
108
+
109
+ ### Added
110
+ - **Advanced System Detection**: Comprehensive OS, shell, and environment detection
111
+ - **Enhanced Claude Code Detection**: Checks both directory and CLI availability with detailed status
112
+ - **Improved Git Bash Detection**: Better Windows support with automatic path discovery
113
+ - **Detailed Error Messages**: Actionable troubleshooting information for users
114
+ - **System Information Display**: Shows detected OS and shell during startup
115
+ - **Modular Architecture**: New `src/detectors/` directory for extensible detection logic
116
+
117
+ ### Improved
118
+ - **User Experience**: Much clearer feedback when something goes wrong
119
+ - **Cross-Platform Compatibility**: Better handling of Windows, macOS, and Linux
120
+ - **Installation Guidance**: OS-specific instructions when dependencies are missing
121
+ - **Code Organization**: Cleaner separation of concerns with dedicated detection modules
122
+
123
+ ### Technical Changes
124
+ - Replaced basic `which()` checks with comprehensive detection system
125
+ - Added support for detecting Claude Code configuration files
126
+ - Enhanced shell detection (PowerShell, CMD, bash, zsh, fish)
127
+ - Improved error recovery and user guidance
128
+
129
+ ## [1.0.1] - 2025-11-29
130
+
131
+ ### Added
132
+ - Git Bash detection for Windows users
133
+ - Clear error message when Git Bash is missing on Windows
134
+ - Troubleshooting section for Git Bash in README.md
135
+ - Section de dépannage pour Git Bash dans README.fr.md
136
+ - Support for `CLAUDE_CODE_GIT_BASH_PATH` environment variable
137
+
138
+ ### Fixed
139
+ - Windows users no longer blocked after entering token due to missing Git Bash
140
+ - Better error handling for Windows environment requirements
141
+
142
+ ### Improved
143
+ - User experience for Windows beta testers
144
+ - Documentation clarity for Windows-specific requirements
145
+ - Error messages now provide actionable solutions
146
+
147
+ ## [1.0.0] - 2025-11-28
148
+
149
+ ### Added
150
+ - Initial release
151
+ - Ephemeral and secure token handling (memory-only storage)
152
+ - Support for ScioNos environment (`https://routerlab.ch`)
153
+ - Bilingual documentation (English and French)
154
+ - Command-line interface with `--version` flag
155
+ - Secure token input with masking
156
+ - Automatic cleanup on process exit
157
+ - Zero persistence (no files written to disk)
158
+
159
+ ### Security
160
+ - Tokens stored only in memory
161
+ - No configuration files created
162
+ - Automatic credential cleanup on exit
163
+ - Environment variable isolation
164
+
165
+ [3.0.6]: https://github.com/ScioNos/claude-scionos/compare/v3.0.5...v3.0.6
166
+ [3.0.7]: https://github.com/ScioNos/claude-scionos/compare/v3.0.6...v3.0.7
167
+ [3.0.5]: https://github.com/ScioNos/claude-scionos/compare/v3.0.4...v3.0.5
168
+ [3.0.4]: https://github.com/ScioNos/claude-scionos/compare/v3.0.3...v3.0.4
169
+ [3.0.3]: https://github.com/ScioNos/claude-scionos/compare/v3.0.2...v3.0.3
170
+ [3.0.2]: https://github.com/ScioNos/claude-scionos/compare/v3.0.1...v3.0.2
171
+ [3.0.1]: https://github.com/ScioNos/claude-scionos/compare/v2.2.0...v3.0.1
172
+ [2.2.0]: https://github.com/ScioNos/claude-scionos/compare/v2.1.0...v2.2.0
173
+ [2.1.0]: https://github.com/ScioNos/claude-scionos/compare/v2.0.0...v2.1.0
174
+ [2.0.0]: https://github.com/ScioNos/claude-scionos/compare/v1.0.1...v2.0.0
175
+ [1.0.1]: https://github.com/ScioNos/claude-scionos/compare/v1.0.0...v1.0.1
176
+ [1.0.0]: https://github.com/ScioNos/claude-scionos/releases/tag/v1.0.0
package/README.fr.md CHANGED
@@ -41,7 +41,7 @@ L'objectif est d'offrir une couche d'exécution propre, isolée et professionnel
41
41
  ### 📌 Points clés
42
42
 
43
43
  - 🔒 **Isolation du jeton** — Le jeton d'authentification n'est jamais écrit sur le disque
44
- - 🔄 **Mapping de Modèles** — Redirection transparente vers **GLM-4.7** ou **MiniMax-M2.1** via proxy local
44
+ - 🔄 **Mapping de Modèles** — Redirection transparente vers **claude-glm-5** ou **claude-minimax-m2.5** via proxy local
45
45
  - 💾 **Zéro persistance** — Aucun fichier temporaire ni configuration locale stockés
46
46
  - 🧩 **Compatibilité totale** — Fonctionne parfaitement avec la CLI officielle Claude Code
47
47
  - 🔐 **Stockage en mémoire uniquement** — Toutes les informations d'identification sont détruites à la fin du processus
@@ -102,8 +102,8 @@ npx claude-scionos
102
102
  2. Vous invite à saisir votre `ANTHROPIC_AUTH_TOKEN` et le valide instantanément
103
103
  3. **Menu de Sélection** : Vous choisissez la stratégie de modèle :
104
104
  - *Default* : Utilise les modèles Anthropic (Opus/Sonnet/Haiku)
105
- - *Force GLM-4.7* : Mappe toutes les requêtes vers GLM-4.7
106
- - *Force MiniMax-M2.1* : Mappe toutes les requêtes vers MiniMax-M2.1
105
+ - *GLM-5* : Mappe toutes les requêtes vers `claude-glm-5`
106
+ - *MiniMax M2.5* : Mappe toutes les requêtes vers `claude-minimax-m2.5`
107
107
  4. Lance Claude Code (avec un proxy local transparent si un mapping est choisi)
108
108
  5. Nettoie automatiquement les informations d'identification à la sortie
109
109
 
package/README.md CHANGED
@@ -41,7 +41,7 @@ The goal is to offer a clean, isolated, and professional execution layer fully c
41
41
  ### 📌 Key Features
42
42
 
43
43
  - 🔒 **Token Isolation** — Authentication token never written to disk
44
- - 🔄 **Model Mapping** — Transparently route requests to **GLM-4.7** or **MiniMax-M2.1** via local proxy
44
+ - 🔄 **Model Mapping** — Transparently route requests to **claude-glm-5** or **claude-minimax-m2.5** via local proxy
45
45
  - 💾 **Zero Persistence** — No temporary files or local configuration stored
46
46
  - 🧩 **Full Compatibility** — Works seamlessly with the official Claude Code CLI
47
47
  - 🔐 **Memory-Only Storage** — All credentials destroyed on process exit
@@ -102,8 +102,8 @@ npx claude-scionos
102
102
  2. Prompts for your `ANTHROPIC_AUTH_TOKEN` and validates it instantly
103
103
  3. **Selection Menu**: Choose your model strategy:
104
104
  - *Default*: Use standard Anthropic models (Opus/Sonnet/Haiku)
105
- - *Force GLM-4.7*: Maps all requests to GLM-4.7
106
- - *Force MiniMax-M2.1*: Maps all requests to MiniMax-M2.1
105
+ - *GLM-5*: Maps all requests to `claude-glm-5`
106
+ - *MiniMax M2.5*: Maps all requests to `claude-minimax-m2.5`
107
107
  4. Launches Claude Code (starting a transparent local proxy if needed)
108
108
  5. Automatically cleans credentials on exit
109
109
 
package/index.js CHANGED
@@ -20,8 +20,7 @@ const chalk = {
20
20
  bold: (t) => styleText('bold', t)
21
21
  };
22
22
  import { password, confirm, select } from '@inquirer/prompts';
23
- import { spawn } from 'node:child_process';
24
- import updateNotifier from 'update-notifier';
23
+ import { spawn, spawnSync } from 'node:child_process';
25
24
  import process from 'node:process';
26
25
  import http from 'node:http';
27
26
  import { createRequire } from 'node:module';
@@ -29,9 +28,7 @@ import { isClaudeCodeInstalled, detectOS, checkGitBashOnWindows, getInstallation
29
28
 
30
29
  const require = createRequire(import.meta.url);
31
30
  const pkg = require('./package.json');
32
-
33
- // Initialize update notifier
34
- updateNotifier({ pkg }).notify();
31
+ const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
35
32
 
36
33
  // --- CONFIGURATION ---
37
34
  const BASE_URL = "https://routerlab.ch";
@@ -66,7 +63,7 @@ function showBanner() {
66
63
  async function validateToken(apiKey) {
67
64
  try {
68
65
  const controller = new AbortController();
69
- const timeoutId = setTimeout(() => controller.abort(), 10000);
66
+ const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout
70
67
  const response = await fetch(`${BASE_URL}/v1/models`, {
71
68
  method: 'GET',
72
69
  headers: {
@@ -85,13 +82,16 @@ async function validateToken(apiKey) {
85
82
  return { valid: false, reason: 'server_error', status: response.status, statusText: response.statusText };
86
83
  }
87
84
  } catch (error) {
85
+ if (error.name === 'AbortError') {
86
+ return { valid: false, reason: 'timeout', message: 'Request timed out after 30s' };
87
+ }
88
88
  return { valid: false, reason: 'network_error', message: error.message };
89
89
  }
90
90
  }
91
91
 
92
92
  /**
93
93
  * Starts a local proxy server to map models
94
- * @param {string} targetModel - The model ID to map to (e.g., 'glm-4.7')
94
+ * @param {string} targetModel - The model ID to map to (e.g., 'claude-glm-5')
95
95
  * @param {string} validToken - The validated Auth Token
96
96
  * @returns {Promise<string>} - The local URL (e.g., http://127.0.0.1:45321)
97
97
  */
@@ -132,7 +132,6 @@ function startProxyServer(targetModel, validToken) {
132
132
  try {
133
133
  bodyJson = JSON.parse(bodyBuffer.toString());
134
134
  } catch {
135
- // If not JSON, forward as is
136
135
  bodyJson = null;
137
136
  }
138
137
 
@@ -145,54 +144,73 @@ function startProxyServer(targetModel, validToken) {
145
144
  } else if (bodyJson.model.includes('opus')) {
146
145
  newModel = 'aws-claude-opus-4-6';
147
146
  } else {
148
- // Default to sonnet for AWS if not haiku or opus
149
147
  newModel = 'aws-claude-sonnet-4-6';
150
148
  }
151
149
  }
152
-
150
+
153
151
  if (process.argv.includes('--scionos-debug')) {
154
152
  console.log(chalk.yellow(`[Proxy] Swapping model ${bodyJson.model} -> ${newModel}`));
155
153
  }
156
154
  bodyJson.model = newModel;
157
155
  }
158
156
 
159
- // Prepare upstream request
160
- // 1. Create an agent that ignores SSL errors (CRITICAL for internal/testing environments)
161
- const { Agent } = await import('undici');
162
- const dispatcher = new Agent({
163
- connect: {
164
- rejectUnauthorized: false // WARNING: Ignores SSL certificate errors
157
+ // Forward request using https directly
158
+ const https = await import('node:https');
159
+ const url = new URL(`${BASE_URL}${req.url}`);
160
+
161
+ const options = {
162
+ hostname: url.hostname,
163
+ port: url.port || 443,
164
+ path: url.pathname + url.search,
165
+ method: 'POST',
166
+ headers: {
167
+ 'Content-Type': 'application/json',
168
+ 'x-api-key': validToken,
169
+ 'anthropic-version': '2023-06-01',
170
+ 'Content-Length': bodyJson ? Buffer.byteLength(JSON.stringify(bodyJson)) : bodyBuffer.length
171
+ },
172
+ rejectUnauthorized: false,
173
+ timeout: 120000
174
+ };
175
+
176
+ const proxyReq = https.request(options, (proxyRes) => {
177
+ if (process.argv.includes('--scionos-debug')) {
178
+ console.log(chalk.yellow(`[Proxy] Upstream response status: ${proxyRes.statusCode}`));
165
179
  }
180
+ res.writeHead(proxyRes.statusCode, proxyRes.headers);
181
+ proxyRes.pipe(res);
166
182
  });
167
183
 
168
- // 2. Remove problematic headers that fetch handles automatically
169
- const upstreamHeaders = { ...req.headers };
170
- delete upstreamHeaders['host']; // Let fetch set the correct Host based on URL
171
- delete upstreamHeaders['content-length']; // Let fetch calculate length based on body
172
- upstreamHeaders['x-api-key'] = validToken;
173
-
174
- // 3. Execute request with the permissive dispatcher
175
- const upstreamRes = await fetch(`${BASE_URL}${req.url}`, {
176
- method: 'POST',
177
- headers: upstreamHeaders,
178
- body: bodyJson ? JSON.stringify(bodyJson) : bodyBuffer,
179
- dispatcher: dispatcher // <--- Apply the custom agent here
184
+ proxyReq.on('error', (error) => {
185
+ console.error(chalk.red(`[Proxy Error] POST /messages: ${error.message}`));
186
+ console.error(chalk.red(` Code: ${error.code}`));
187
+ if (!res.headersSent) {
188
+ res.writeHead(500);
189
+ res.end(JSON.stringify({ error: { message: "Proxy Error", details: error.message, code: error.code } }));
190
+ }
180
191
  });
181
192
 
182
- // Pipe response back
183
- res.writeHead(upstreamRes.status, upstreamRes.headers);
184
- if (upstreamRes.body) {
185
- // @ts-ignore - Node fetch body is iterable
186
- for await (const chunk of upstreamRes.body) {
187
- res.write(chunk);
193
+ proxyReq.on('timeout', () => {
194
+ console.error(chalk.red(`[Proxy] Request timeout`));
195
+ proxyReq.destroy();
196
+ if (!res.headersSent) {
197
+ res.writeHead(504);
198
+ res.end(JSON.stringify({ error: { message: "Gateway Timeout" } }));
188
199
  }
189
- }
190
- res.end();
200
+ });
191
201
 
202
+ proxyReq.write(bodyJson ? JSON.stringify(bodyJson) : bodyBuffer);
203
+ proxyReq.end();
204
+
205
+ if (process.argv.includes('--scionos-debug')) {
206
+ console.log(chalk.yellow(`[Proxy] Request sent to upstream`));
207
+ }
192
208
  } catch (error) {
193
- console.error(chalk.red(`[Proxy Error] ${error.message}`));
194
- res.writeHead(500);
195
- res.end(JSON.stringify({ error: { message: "Scionos Proxy Error" } }));
209
+ console.error(chalk.red(`[Proxy Error] POST /messages: ${error.message}`));
210
+ if (!res.headersSent) {
211
+ res.writeHead(500);
212
+ res.end(JSON.stringify({ error: { message: "Scionos Proxy Error", details: error.message } }));
213
+ }
196
214
  }
197
215
  });
198
216
  } else {
@@ -204,32 +222,58 @@ function startProxyServer(targetModel, validToken) {
204
222
 
205
223
  // Simple Redirect implementation for non-body requests
206
224
  try {
207
- // 1. Create agent (SSL bypass)
208
- const { Agent } = await import('undici');
209
- const dispatcher = new Agent({ connect: { rejectUnauthorized: false } });
210
-
211
- // 2. Clean headers
212
- const upstreamHeaders = { ...req.headers };
213
- delete upstreamHeaders['host'];
214
- delete upstreamHeaders['content-length'];
215
- upstreamHeaders['x-api-key'] = validToken;
216
-
217
- const upstreamRes = await fetch(`${BASE_URL}${req.url}`, {
225
+ const https = await import('node:https');
226
+ const url = new URL(`${BASE_URL}${req.url}`);
227
+
228
+ const options = {
229
+ hostname: url.hostname,
230
+ port: url.port || 443,
231
+ path: url.pathname + url.search,
218
232
  method: req.method,
219
- headers: upstreamHeaders,
220
- dispatcher: dispatcher
233
+ headers: {
234
+ 'Content-Type': 'application/json',
235
+ 'x-api-key': validToken,
236
+ 'anthropic-version': '2023-06-01'
237
+ },
238
+ rejectUnauthorized: false,
239
+ timeout: 60000
240
+ };
241
+
242
+ const proxyReq = https.request(options, (proxyRes) => {
243
+ res.writeHead(proxyRes.statusCode, proxyRes.headers);
244
+ proxyRes.pipe(res);
221
245
  });
222
- res.writeHead(upstreamRes.status, upstreamRes.headers);
223
- if (upstreamRes.body) {
224
- // @ts-ignore
225
- for await (const chunk of upstreamRes.body) {
226
- res.write(chunk);
246
+
247
+ proxyReq.on('error', (error) => {
248
+ console.error(chalk.red(`[Proxy Error] ${req.method} ${req.url}: ${error.message}`));
249
+ if (!res.headersSent) {
250
+ res.writeHead(502);
251
+ res.end(JSON.stringify({
252
+ error: {
253
+ message: "Scionos Proxy Error: Failed to connect to upstream",
254
+ details: error.message
255
+ }
256
+ }));
227
257
  }
228
- }
229
- res.end();
230
- } catch {
231
- res.writeHead(502);
232
- res.end();
258
+ });
259
+
260
+ // Forward body for PUT/POST requests
261
+ if (req.method === 'POST' || req.method === 'PUT') {
262
+ req.pipe(proxyReq);
263
+ } else {
264
+ proxyReq.end();
265
+ }
266
+ } catch (error) {
267
+ console.error(chalk.red(`[Proxy Error] ${req.method} ${req.url}: ${error.message}`));
268
+ if (!res.headersSent) {
269
+ res.writeHead(502);
270
+ res.end(JSON.stringify({
271
+ error: {
272
+ message: "Scionos Proxy Error: Failed to connect to upstream",
273
+ details: error.message
274
+ }
275
+ }));
276
+ }
233
277
  }
234
278
  }
235
279
  });
@@ -274,7 +318,15 @@ if (!claudeStatus.installed) {
274
318
  if (shouldInstall) {
275
319
  try {
276
320
  console.log(chalk.cyan('\n📦 Installing @anthropic-ai/claude-code...'));
277
- spawn.sync('npm', ['install', '-g', '@anthropic-ai/claude-code'], { stdio: 'inherit', shell: process.platform === 'win32' });
321
+ const installResult = spawnSync(npmCommand, ['install', '-g', '@anthropic-ai/claude-code'], {
322
+ stdio: 'inherit'
323
+ });
324
+ if (installResult.error) {
325
+ throw installResult.error;
326
+ }
327
+ if (installResult.status !== 0) {
328
+ throw new Error(`npm install exited with code ${installResult.status}`);
329
+ }
278
330
  claudeStatus = isClaudeCodeInstalled();
279
331
  if (!claudeStatus.installed) {
280
332
  console.warn(chalk.yellow('⚠ Installation finished, but executable not found immediately. Restart terminal recommended.'));
@@ -341,13 +393,13 @@ const modelChoice = await select({
341
393
  },
342
394
  {
343
395
  name: 'GLM-5',
344
- value: 'glm-5',
345
- description: 'Remplace tous les modèles par glm-5'
396
+ value: 'claude-glm-5',
397
+ description: 'Remplace tous les modèles par claude-glm-5'
346
398
  },
347
399
  {
348
400
  name: 'MiniMax M2.5',
349
- value: 'minimax-m2.5',
350
- description: 'Remplace tous les modèles par minimax-m2.5'
401
+ value: 'claude-minimax-m2.5',
402
+ description: 'Remplace tous les modèles par claude-minimax-m2.5'
351
403
  }
352
404
  ]
353
405
  });
@@ -389,8 +441,7 @@ console.log(chalk.green(`\n🚀 Launching Claude Code [${modelChoice}]...\n`));
389
441
 
390
442
  const child = spawn(claudeStatus.cliPath, args, {
391
443
  stdio: 'inherit',
392
- env: env,
393
- shell: process.platform === 'win32'
444
+ env: env
394
445
  });
395
446
 
396
447
  // 7. Cleanup Handlers
@@ -428,4 +479,4 @@ process.on('SIGTERM', () => {
428
479
  if (child) child.kill('SIGTERM');
429
480
  cleanup();
430
481
  process.exit(0);
431
- });
482
+ });
package/package.json CHANGED
@@ -1,48 +1,55 @@
1
- {
2
- "name": "claude-scionos",
3
- "version": "3.0.5",
4
- "description": "Ephemeral and secure runner for Claude Code CLI in ScioNos environment",
5
- "type": "module",
6
- "main": "index.js",
7
- "bin": {
8
- "claude-scionos": "index.js"
9
- },
10
- "scripts": {
11
- "test": "vitest run",
12
- "lint": "eslint .",
13
- "release:patch": "npm version patch -m \"Chore: Bump version to %s\"",
14
- "release:minor": "npm version minor -m \"Chore: Bump version to %s\"",
15
- "release:major": "npm version major -m \"Chore: Bump version to %s\""
16
- },
17
- "keywords": [
18
- "claude",
19
- "scionos",
20
- "cli",
21
- "wrapper"
22
- ],
23
- "author": "ScioNos",
24
- "license": "MIT",
25
- "homepage": "https://scionos.ch",
26
- "repository": {
27
- "type": "git",
28
- "url": "git+https://github.com/ScioNos/claude-scionos.git"
29
- },
30
- "bugs": {
31
- "url": "https://github.com/ScioNos/claude-scionos/issues"
32
- },
33
- "engines": {
34
- "node": ">=22"
35
- },
36
- "private": false,
37
- "dependencies": {
38
- "@inquirer/prompts": "^8.3.2",
39
- "update-notifier": "^7.3.1",
40
- "which": "^6.0.1"
41
- },
42
- "devDependencies": {
43
- "@eslint/js": "^10.0.1",
44
- "eslint": "^10.0.3",
45
- "globals": "^17.4.0",
46
- "vitest": "^4.1.0"
47
- }
48
- }
1
+ {
2
+ "name": "claude-scionos",
3
+ "version": "3.0.7",
4
+ "description": "Ephemeral and secure runner for Claude Code CLI in ScioNos environment",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "claude-scionos": "index.js"
9
+ },
10
+ "files": [
11
+ "index.js",
12
+ "src/",
13
+ "README.md",
14
+ "README.fr.md",
15
+ "CHANGELOG.md",
16
+ "LICENSE"
17
+ ],
18
+ "scripts": {
19
+ "test": "vitest run",
20
+ "lint": "eslint .",
21
+ "release:patch": "npm version patch -m \"Chore: Bump version to %s\"",
22
+ "release:minor": "npm version minor -m \"Chore: Bump version to %s\"",
23
+ "release:major": "npm version major -m \"Chore: Bump version to %s\""
24
+ },
25
+ "keywords": [
26
+ "claude",
27
+ "scionos",
28
+ "cli",
29
+ "wrapper"
30
+ ],
31
+ "author": "ScioNos",
32
+ "license": "MIT",
33
+ "homepage": "https://scionos.ch",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/ScioNos/claude-scionos.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/ScioNos/claude-scionos/issues"
40
+ },
41
+ "engines": {
42
+ "node": ">=22"
43
+ },
44
+ "private": false,
45
+ "dependencies": {
46
+ "@inquirer/prompts": "^8.3.2",
47
+ "which": "^6.0.1"
48
+ },
49
+ "devDependencies": {
50
+ "@eslint/js": "^10.0.1",
51
+ "eslint": "^10.1.0",
52
+ "globals": "^17.4.0",
53
+ "vitest": "^4.1.0"
54
+ }
55
+ }
@@ -1,9 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(node -p:*)",
5
- "Bash(git add:*)",
6
- "Bash(git commit:*)"
7
- ]
8
- }
9
- }
package/.gitattributes DELETED
@@ -1,2 +0,0 @@
1
- # Auto detect text files and perform LF normalization
2
- * text=auto
package/1.png DELETED
Binary file
package/eslint.config.js DELETED
@@ -1,20 +0,0 @@
1
- import globals from 'globals';
2
- import pluginJs from '@eslint/js';
3
-
4
- export default [
5
- {
6
- languageOptions: {
7
- globals: {
8
- ...globals.node,
9
- ...globals.builtin,
10
- }
11
- }
12
- },
13
- pluginJs.configs.recommended,
14
- {
15
- rules: {
16
- 'no-console': 'off',
17
- 'no-process-exit': 'off',
18
- }
19
- }
20
- ];
@@ -1,166 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import fs from 'fs';
3
- import os from 'os';
4
- import path from 'path';
5
- import child_process from 'child_process';
6
- import { isClaudeCodeInstalled, detectOS, checkGitBashOnWindows } from '../src/detectors/claude-only.js';
7
-
8
- // Mock modules
9
- vi.mock('fs');
10
- vi.mock('os');
11
- vi.mock('child_process');
12
-
13
- describe('System Detectors', () => {
14
- const originalPlatform = process.platform;
15
- const originalEnv = process.env;
16
-
17
- beforeEach(() => {
18
- vi.resetAllMocks();
19
- process.env = { ...originalEnv };
20
- });
21
-
22
- afterEach(() => {
23
- Object.defineProperty(process, 'platform', { value: originalPlatform });
24
- process.env = originalEnv;
25
- });
26
-
27
- describe('detectOS', () => {
28
- it('should detect Windows', () => {
29
- vi.mocked(os.platform).mockReturnValue('win32');
30
- vi.mocked(os.arch).mockReturnValue('x64');
31
- Object.defineProperty(process, 'platform', { value: 'win32' });
32
- process.env.WINDIR = 'C:\\Windows'; // Simulate CMD
33
-
34
- const result = detectOS();
35
- expect(result.type).toBe('Windows');
36
- expect(result.shell).toBe('Command Prompt (CMD)');
37
- });
38
-
39
- it('should detect macOS', () => {
40
- vi.mocked(os.platform).mockReturnValue('darwin');
41
- vi.mocked(os.arch).mockReturnValue('arm64');
42
- Object.defineProperty(process, 'platform', { value: 'darwin' });
43
- process.env.SHELL = '/bin/zsh';
44
-
45
- const result = detectOS();
46
- expect(result.type).toBe('macOS');
47
- expect(result.shell).toBe('Zsh');
48
- });
49
- });
50
-
51
- describe('checkGitBashOnWindows', () => {
52
- it('should return available if Git Bash exists in standard path on Windows', () => {
53
- Object.defineProperty(process, 'platform', { value: 'win32' });
54
- vi.mocked(fs.existsSync).mockImplementation((p) => p === 'C:\\Program Files\\Git\\bin\\bash.exe');
55
-
56
- const result = checkGitBashOnWindows();
57
- expect(result.available).toBe(true);
58
- expect(result.path).toBe('C:\\Program Files\\Git\\bin\\bash.exe');
59
- });
60
-
61
- it('should return not available if no Git Bash found on Windows', () => {
62
- Object.defineProperty(process, 'platform', { value: 'win32' });
63
- vi.mocked(fs.existsSync).mockReturnValue(false);
64
-
65
- const result = checkGitBashOnWindows();
66
- expect(result.available).toBe(false);
67
- });
68
-
69
- it('should not require Git Bash on non-Windows', () => {
70
- Object.defineProperty(process, 'platform', { value: 'darwin' });
71
-
72
- const result = checkGitBashOnWindows();
73
- expect(result.available).toBe(true);
74
- expect(result.message).toContain('Not required');
75
- });
76
- });
77
-
78
- describe('isClaudeCodeInstalled', () => {
79
- it('should detect Claude in PATH', () => {
80
- // Mock homedir to avoid finding config file
81
- vi.mocked(os.homedir).mockReturnValue('/home/user');
82
-
83
- // Mock execSync to return a path
84
- vi.mocked(child_process.execSync).mockImplementation((cmd) => {
85
- if (cmd.includes('which claude') || cmd.includes('where claude')) {
86
- return '/usr/local/bin/claude';
87
- }
88
- if (cmd.includes('--version')) {
89
- return '0.0.1';
90
- }
91
- return '';
92
- });
93
-
94
- // Mock existsSync: false for config, true for CLI path
95
- vi.mocked(fs.existsSync).mockImplementation((p) => {
96
- if (p === '/usr/local/bin/claude') return true;
97
- return false;
98
- });
99
-
100
- const result = isClaudeCodeInstalled();
101
- expect(result.installed).toBe(true);
102
- expect(result.cliAvailable).toBe(true);
103
- expect(result.cliPath).toBe('/usr/local/bin/claude');
104
- });
105
-
106
- it('should detect Claude from config file ONLY if CLI is also found', () => {
107
- vi.mocked(os.homedir).mockReturnValue('/home/user');
108
- // Simulate .claude directory and settings.json existence
109
- vi.mocked(fs.existsSync).mockImplementation((p) => {
110
- if (p.endsWith('.claude')) return true;
111
- if (p.endsWith('settings.json')) return true;
112
- return false;
113
- });
114
-
115
- // Mock reading config
116
- vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
117
- env: { ANTHROPIC_BASE_URL: 'https://api.anthropic.com' }
118
- }));
119
-
120
- // BUT execSync fails (CLI not found)
121
- vi.mocked(child_process.execSync).mockImplementation(() => {
122
- throw new Error('not found');
123
- });
124
-
125
- const result = isClaudeCodeInstalled();
126
- // Updated logic: installed should be false if CLI is missing, even if config exists
127
- expect(result.installed).toBe(false);
128
- expect(result.configFound).toBe(true);
129
- expect(result.cliAvailable).toBe(false);
130
- expect(result.details).toContain('Executable \'claude\' not found');
131
- });
132
-
133
- it('should detect Claude in native path (Linux/Mac)', () => {
134
- // Force Linux platform to ensure implementation looks for 'claude' not 'claude.exe'
135
- Object.defineProperty(process, 'platform', { value: 'linux' });
136
-
137
- // We simulate logic but path.join will use OS separators.
138
- // On Windows, path.join produces backslashes.
139
- // The code under test uses path.join.
140
- const mockHome = '/home/user';
141
- const expectedPath = path.join(mockHome, '.local', 'bin', 'claude');
142
-
143
- vi.mocked(os.homedir).mockReturnValue(mockHome);
144
-
145
- // Simulate native path existence
146
- vi.mocked(fs.existsSync).mockImplementation((p) => {
147
- return p === expectedPath;
148
- });
149
-
150
- const result = isClaudeCodeInstalled();
151
- expect(result.installed).toBe(true);
152
- expect(result.cliPath).toBe(expectedPath);
153
- });
154
-
155
- it('should return false if neither CLI nor config found', () => {
156
- vi.mocked(os.homedir).mockReturnValue('/home/user');
157
- vi.mocked(fs.existsSync).mockReturnValue(false);
158
- vi.mocked(child_process.execSync).mockImplementation(() => {
159
- throw new Error('not found');
160
- });
161
-
162
- const result = isClaudeCodeInstalled();
163
- expect(result.installed).toBe(false);
164
- });
165
- });
166
- });