claude-scionos 4.2.0 → 4.3.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.fr.md CHANGED
@@ -54,7 +54,7 @@ npx claude-scionos auth login
54
54
  npx claude-scionos auth login --service llm
55
55
  npx claude-scionos auth test
56
56
  npx claude-scionos --strategy aws
57
- npx claude-scionos --service llm --strategy claude-glm-5
57
+ npx claude-scionos --service llm --strategy claude-gpt
58
58
  npx claude-scionos --strategy aws --no-prompt -p "Résume ce dépôt"
59
59
  ```
60
60
 
@@ -64,8 +64,8 @@ npx claude-scionos --strategy aws --no-prompt -p "Résume ce dépôt"
64
64
  - `--service llm` bascule le lanceur vers `https://llm.routerlab.ch`
65
65
  - `llm` est prévu pour un accès sur invitation
66
66
  - les tokens enregistrés avec `auth login --service llm` sont stockés séparément du token RouterLab par défaut
67
- - `llm` expose pour l'instant `claude-glm-5`, `claude-gpt-5.4` et `claude-qwen3.6-plus`
68
- - `routerlab` expose aussi `claude-gpt-5.4`
67
+ - `llm` expose pour l'instant `claude-gpt`, `claude-qwen3.6-plus`, `claude-minimax-m2.7` et `claude-glm-5.1`
68
+ - `routerlab` expose aussi `claude-gpt`
69
69
 
70
70
  ## Stratégies
71
71
 
@@ -73,8 +73,11 @@ npx claude-scionos --strategy aws --no-prompt -p "Résume ce dépôt"
73
73
  - `aws` : remappe les familles de modèles Claude vers les variantes Claude AWS de RouterLab
74
74
  - `claude-glm-5` : force toutes les requêtes vers `claude-glm-5`
75
75
  - `claude-minimax-m2.5` : force toutes les requêtes vers `claude-minimax-m2.5`
76
- - `claude-gpt-5.4` : force toutes les requêtes vers `claude-gpt-5.4`
76
+ - `claude-gpt` : mappe les requêtes Claude vers la famille `claude-gpt`
77
+ `claude-gpt-5.5 ==> claude-opus-4.7`, `claude-gpt-5.4 ==> claude-sonnet-4.6`, `claude-gpt-5.4-mini ==> claude-gpt-5.4-mini`
77
78
  - `claude-qwen3.6-plus` : force toutes les requêtes vers `claude-qwen3.6-plus`
79
+ - `claude-minimax-m2.7` : force toutes les requêtes vers `claude-minimax-m2.7`
80
+ - `claude-glm-5.1` : force toutes les requêtes vers `claude-glm-5.1`
78
81
 
79
82
  Utilise `--list-strategies` pour voir les stratégies disponibles pour le service choisi et leur disponibilité réelle quand un token est disponible.
80
83
 
package/README.md CHANGED
@@ -54,7 +54,7 @@ npx claude-scionos auth login
54
54
  npx claude-scionos auth login --service llm
55
55
  npx claude-scionos auth test
56
56
  npx claude-scionos --strategy aws
57
- npx claude-scionos --service llm --strategy claude-glm-5
57
+ npx claude-scionos --service llm --strategy claude-gpt
58
58
  npx claude-scionos --strategy aws --no-prompt -p "Summarize this repo"
59
59
  ```
60
60
 
@@ -64,8 +64,8 @@ npx claude-scionos --strategy aws --no-prompt -p "Summarize this repo"
64
64
  - `--service llm` switches the launcher to `https://llm.routerlab.ch`
65
65
  - `llm` is intended for invitation-only access
66
66
  - Tokens stored with `auth login --service llm` are kept separate from the default RouterLab token
67
- - `llm` currently exposes `claude-glm-5`, `claude-gpt-5.4`, and `claude-qwen3.6-plus`
68
- - `routerlab` also exposes `claude-gpt-5.4`
67
+ - `llm` currently exposes `claude-gpt`, `claude-qwen3.6-plus`, `claude-minimax-m2.7`, and `claude-glm-5.1`
68
+ - `routerlab` also exposes `claude-gpt`
69
69
 
70
70
  ## Strategies
71
71
 
@@ -73,8 +73,11 @@ npx claude-scionos --strategy aws --no-prompt -p "Summarize this repo"
73
73
  - `aws`: remap Claude model families to RouterLab AWS-backed Claude variants
74
74
  - `claude-glm-5`: force all requests to `claude-glm-5`
75
75
  - `claude-minimax-m2.5`: force all requests to `claude-minimax-m2.5`
76
- - `claude-gpt-5.4`: force all requests to `claude-gpt-5.4`
76
+ - `claude-gpt`: map Claude requests to the `claude-gpt` family
77
+ `claude-gpt-5.5 ==> claude-opus-4.7`, `claude-gpt-5.4 ==> claude-sonnet-4.6`, `claude-gpt-5.4-mini ==> claude-gpt-5.4-mini`
77
78
  - `claude-qwen3.6-plus`: force all requests to `claude-qwen3.6-plus`
79
+ - `claude-minimax-m2.7`: force all requests to `claude-minimax-m2.7`
80
+ - `claude-glm-5.1`: force all requests to `claude-glm-5.1`
78
81
 
79
82
  Use `--list-strategies` to see the strategies available for the selected service and their live availability when a token is available.
80
83
 
package/index.js CHANGED
@@ -118,7 +118,7 @@ function showHelp() {
118
118
  console.log(chalk.gray("Examples"));
119
119
  console.log(` ${chalk.cyan("claude-scionos --strategy aws")}`);
120
120
  console.log(` ${chalk.cyan("claude-scionos auth login --service llm")}`);
121
- console.log(` ${chalk.cyan("claude-scionos --service llm --strategy claude-glm-5")}`);
121
+ console.log(` ${chalk.cyan("claude-scionos --service llm --strategy claude-gpt")}`);
122
122
  console.log(` ${chalk.cyan('claude-scionos --strategy aws --no-prompt -p "Summarize this repo"')}`);
123
123
  console.log(` ${chalk.cyan("claude-scionos auth test")}`);
124
124
  console.log("");
@@ -159,11 +159,15 @@ function getStrategyIndicator(strategyValue, modelIds, serviceValue) {
159
159
  }
160
160
 
161
161
  function getStrategyMenuLabel(strategyValue) {
162
- if (strategyValue === 'aws') {
162
+ const strategy = getServiceStrategies(DEFAULT_SERVICE)
163
+ .concat(getServiceStrategies('llm'))
164
+ .find((entry) => entry.value === strategyValue || entry.aliases?.includes(strategyValue));
165
+
166
+ if (strategy?.value === 'aws') {
163
167
  return '💰 aws 50%';
164
168
  }
165
169
 
166
- return strategyValue;
170
+ return strategy?.selectionName || strategy?.name || strategyValue;
167
171
  }
168
172
 
169
173
  function normalizeStrategyValue(strategy) {
@@ -427,7 +431,9 @@ async function resolveStrategyChoice(parsed, modelIds, serviceConfig) {
427
431
  };
428
432
 
429
433
  if (parsed.strategy) {
430
- const strategy = getServiceStrategies(serviceConfig.value).find((entry) => entry.value === parsed.strategy);
434
+ const strategy = getServiceStrategies(serviceConfig.value).find((entry) => (
435
+ entry.value === parsed.strategy || entry.aliases?.includes(parsed.strategy)
436
+ ));
431
437
  if (!strategy) {
432
438
  throw new Error(`Unknown strategy "${parsed.strategy}". Use --list-strategies to inspect the supported values.`);
433
439
  }
@@ -442,12 +448,13 @@ async function resolveStrategyChoice(parsed, modelIds, serviceConfig) {
442
448
  const strategyChoices = getStrategyChoices(modelIds, serviceConfig.value).map((choice) => {
443
449
  const launchReadiness = assessStrategyLaunch(choice.value, modelIds, serviceConfig.value);
444
450
  const disabled = hasVerifiedModelIds(modelIds) && !launchReadiness.ready ? launchReadiness.note : false;
451
+ const menuLabel = getStrategyMenuLabel(choice.value);
445
452
  return {
446
453
  ...choice,
447
454
  disabled,
448
- name: `${getStrategyIndicator(choice.value, modelIds, serviceConfig.value)} ${getStrategyMenuLabel(choice.value)}`,
449
- short: getStrategyMenuLabel(choice.value),
450
- description: launchReadiness.note
455
+ name: `${getStrategyIndicator(choice.value, modelIds, serviceConfig.value)} ${menuLabel}`,
456
+ short: menuLabel,
457
+ description: choice.description
451
458
  };
452
459
  });
453
460
 
@@ -472,7 +479,7 @@ function showStrategies(modelIds = null, serviceConfig) {
472
479
  const strategies = listStrategies(modelIds, serviceConfig.value);
473
480
  showSection('Strategies', strategies.map((strategy) => {
474
481
  const indicator = getStrategyIndicator(strategy.value, modelIds, serviceConfig.value);
475
- return `${indicator} ${chalk.white(getStrategyMenuLabel(strategy.value))} ${chalk.gray(`(${strategy.value})`)}\n ${strategy.description}`;
482
+ return `${indicator} ${chalk.white(getStrategyMenuLabel(strategy.value))} ${chalk.gray(`(${strategy.value})`)}\n ${[strategy.description, strategy.availability.note].filter(Boolean).join(' ')}`;
476
483
  }));
477
484
  }
478
485
 
@@ -694,6 +701,7 @@ async function main() {
694
701
  }
695
702
 
696
703
  const proxyInfo = await startProxyServer(modelChoice, token, {
704
+ availableModels: validation.models,
697
705
  baseUrl: resolveServiceBaseUrl(serviceConfig.value),
698
706
  debug: isDebug,
699
707
  onDebug: (message) => console.log(chalk.yellow(message)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-scionos",
3
- "version": "4.2.0",
3
+ "version": "4.3.0",
4
4
  "description": "RouterLab launcher, strategy proxy and secure token wrapper for Claude Code CLI",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -43,12 +43,12 @@
43
43
  },
44
44
  "private": false,
45
45
  "dependencies": {
46
- "@inquirer/prompts": "^8.4.1"
46
+ "@inquirer/prompts": "^8.4.2"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@eslint/js": "^10.0.1",
50
- "eslint": "^10.2.0",
50
+ "eslint": "^10.2.1",
51
51
  "globals": "^17.5.0",
52
- "vitest": "^4.1.4"
52
+ "vitest": "^4.1.5"
53
53
  }
54
54
  }
package/src/proxy.js CHANGED
@@ -52,9 +52,42 @@ function buildProxyRequestOptions(url, method, upstreamHeaders, validToken, body
52
52
  };
53
53
  }
54
54
 
55
- function resolveMappedModel(targetModel, requestedModel = '') {
55
+ function getPreferredClaudeGptModel(requestedModel = '') {
56
+ if (requestedModel.includes('haiku') || requestedModel.includes('mini')) {
57
+ return 'claude-gpt-5.4-mini';
58
+ }
59
+
60
+ if (requestedModel.includes('opus')) {
61
+ return 'claude-gpt-5.5';
62
+ }
63
+
64
+ return 'claude-gpt-5.4';
65
+ }
66
+
67
+ function resolveMappedModel(targetModel, requestedModel = '', availableModels = []) {
56
68
  if (targetModel !== 'aws') {
57
- return targetModel;
69
+ if (targetModel !== 'claude-gpt') {
70
+ return targetModel;
71
+ }
72
+
73
+ const preferredModel = getPreferredClaudeGptModel(requestedModel);
74
+ const availableClaudeGptModels = Array.isArray(availableModels)
75
+ ? availableModels.filter((model) => model.startsWith('claude-gpt-'))
76
+ : [];
77
+
78
+ if (availableClaudeGptModels.length === 0) {
79
+ return preferredModel;
80
+ }
81
+
82
+ if (availableClaudeGptModels.includes(preferredModel)) {
83
+ return preferredModel;
84
+ }
85
+
86
+ return (
87
+ availableClaudeGptModels.find((model) => model === 'claude-gpt-5.4')
88
+ ?? availableClaudeGptModels[0]
89
+ ?? preferredModel
90
+ );
58
91
  }
59
92
 
60
93
  if (requestedModel.includes('haiku')) {
@@ -94,7 +127,7 @@ function isAllowedProxyRoute(req) {
94
127
  }
95
128
 
96
129
  async function handleMessageRequest(req, res, options) {
97
- const {baseUrl, debug, onDebug, onError, targetModel, validToken} = options;
130
+ const {availableModels = [], baseUrl, debug, onDebug, onError, targetModel, validToken} = options;
98
131
  const chunks = [];
99
132
  const maxSize = 100 * 1024 * 1024;
100
133
  let totalSize = 0;
@@ -122,9 +155,13 @@ async function handleMessageRequest(req, res, options) {
122
155
  }
123
156
 
124
157
  if (bodyJson?.model) {
125
- const newModel = resolveMappedModel(targetModel, bodyJson.model);
158
+ const preferredModel = resolveMappedModel(targetModel, bodyJson.model);
159
+ const newModel = resolveMappedModel(targetModel, bodyJson.model, availableModels);
126
160
  if (debug) {
127
161
  onDebug(`[Proxy] Swapping model ${bodyJson.model} -> ${newModel}`);
162
+ if (preferredModel !== newModel) {
163
+ onDebug(`[Proxy] Fallback applied because ${preferredModel} is not available for this token`);
164
+ }
128
165
  }
129
166
 
130
167
  bodyJson.model = newModel;
@@ -216,6 +253,7 @@ async function forwardRequest(req, res, options) {
216
253
 
217
254
  function startProxyServer(targetModel, validToken, options = {}) {
218
255
  const {
256
+ availableModels = [],
219
257
  baseUrl = BASE_URL,
220
258
  debug = false,
221
259
  onDebug = () => {},
@@ -236,6 +274,7 @@ function startProxyServer(targetModel, validToken, options = {}) {
236
274
  }
237
275
 
238
276
  handleMessageRequest(req, res, {
277
+ availableModels,
239
278
  baseUrl,
240
279
  debug,
241
280
  onDebug,
@@ -261,4 +300,3 @@ export {
261
300
  resolveMappedModel,
262
301
  startProxyServer,
263
302
  };
264
-
package/src/routerlab.js CHANGED
@@ -15,7 +15,7 @@ const SERVICES = {
15
15
  secureStorageAccount: 'routerlab-token',
16
16
  secureStorageLabel: 'RouterLab Token',
17
17
  secureStorageFileName: 'routerlab-token.secure.txt',
18
- strategyValues: ['default', 'aws', 'claude-gpt-5.4', 'claude-glm-5', 'claude-minimax-m2.5'],
18
+ strategyValues: ['default', 'aws', 'claude-gpt', 'claude-glm-5', 'claude-minimax-m2.5'],
19
19
  },
20
20
  llm: {
21
21
  value: 'llm',
@@ -28,7 +28,7 @@ const SERVICES = {
28
28
  secureStorageAccount: 'routerlab-llm-token',
29
29
  secureStorageLabel: 'RouterLab LLM Token',
30
30
  secureStorageFileName: 'routerlab-llm-token.secure.txt',
31
- strategyValues: ['claude-glm-5', 'claude-gpt-5.4', 'claude-qwen3.6-plus'],
31
+ strategyValues: ['claude-gpt', 'claude-qwen3.6-plus', 'claude-minimax-m2.7', 'claude-glm-5.1'],
32
32
  },
33
33
  };
34
34
  const DEFAULT_SERVICE = 'routerlab';
@@ -85,11 +85,13 @@ const STRATEGIES = [
85
85
  mappedModels: ['claude-minimax-m2.5'],
86
86
  },
87
87
  {
88
- value: 'claude-gpt-5.4',
89
- name: 'GPT-5.4',
90
- description: 'Forces all requests to claude-gpt-5.4.',
91
- selectionDescription: 'Forces all requests to claude-gpt-5.4.',
92
- mappedModels: ['claude-gpt-5.4'],
88
+ value: 'claude-gpt',
89
+ name: 'claude-gpt',
90
+ selectionName: 'claude-gpt',
91
+ description: 'Maps Claude requests to the claude-gpt family. Opus 4.7 => claude-gpt-5.5, Sonnet 4.6 => claude-gpt-5.4, Haiku => claude-gpt-5.4-mini.',
92
+ selectionDescription: 'Opus 4.7 => claude-gpt-5.5, Sonnet 4.6 => claude-gpt-5.4, Haiku => claude-gpt-5.4-mini.',
93
+ aliases: ['claude-gpt-5.4'],
94
+ verificationModels: ['claude-gpt-5.4'],
93
95
  },
94
96
  {
95
97
  value: 'claude-qwen3.6-plus',
@@ -98,6 +100,20 @@ const STRATEGIES = [
98
100
  selectionDescription: 'Forces all requests to claude-qwen3.6-plus.',
99
101
  mappedModels: ['claude-qwen3.6-plus'],
100
102
  },
103
+ {
104
+ value: 'claude-minimax-m2.7',
105
+ name: 'MiniMax M2.7',
106
+ description: 'Forces all requests to claude-minimax-m2.7.',
107
+ selectionDescription: 'Forces all requests to claude-minimax-m2.7.',
108
+ mappedModels: ['claude-minimax-m2.7'],
109
+ },
110
+ {
111
+ value: 'claude-glm-5.1',
112
+ name: 'GLM-5.1',
113
+ description: 'Forces all requests to claude-glm-5.1.',
114
+ selectionDescription: 'Forces all requests to claude-glm-5.1.',
115
+ mappedModels: ['claude-glm-5.1'],
116
+ },
101
117
  ];
102
118
 
103
119
  async function fetchModels(apiKey, options = {}) {
@@ -191,7 +207,7 @@ function getServiceConfig(serviceValue = DEFAULT_SERVICE) {
191
207
  return SERVICES[normalizeServiceValue(serviceValue)] ?? null;
192
208
  }
193
209
 
194
- function resolveServiceBaseUrl(serviceValue = DEFAULT_SERVICE, env = process.env) {
210
+ function resolveServiceBaseUrl(serviceValue = DEFAULT_SERVICE, env = {}) {
195
211
  return env.ANTHROPIC_BASE_URL?.trim() || getServiceConfig(serviceValue)?.baseUrl || BASE_URL;
196
212
  }
197
213
 
@@ -245,12 +261,19 @@ function getServiceStrategies(serviceValue = DEFAULT_SERVICE) {
245
261
  .filter(Boolean);
246
262
  }
247
263
 
264
+ function normalizeStrategyValue(strategyValue) {
265
+ return strategyValue === 'claude-gpt-5.4' ? 'claude-gpt' : strategyValue;
266
+ }
267
+
248
268
  function findStrategy(strategyValue, serviceValue = DEFAULT_SERVICE) {
249
- return getServiceStrategies(serviceValue).find((strategy) => strategy.value === strategyValue) ?? null;
269
+ const normalizedValue = normalizeStrategyValue(strategyValue);
270
+ return getServiceStrategies(serviceValue).find((strategy) => (
271
+ strategy.value === normalizedValue || strategy.aliases?.includes(strategyValue)
272
+ )) ?? null;
250
273
  }
251
274
 
252
275
  function getRequiredModels(strategy) {
253
- return strategy?.requiredModels ?? strategy?.mappedModels ?? [];
276
+ return strategy?.requiredModels ?? strategy?.verificationModels ?? strategy?.mappedModels ?? [];
254
277
  }
255
278
 
256
279
  function hasVerifiedModelIds(modelIds) {
@@ -391,17 +414,18 @@ function assessStrategyLaunch(strategyValue, modelIds = [], serviceValue = DEFAU
391
414
  }
392
415
 
393
416
  function getFallbackStrategy(strategyValue, modelIds = [], serviceValue = DEFAULT_SERVICE) {
417
+ const normalizedValue = normalizeStrategyValue(strategyValue);
394
418
  if (hasExploitableModelIds(modelIds, serviceValue)) {
395
- return assessStrategyLaunch(strategyValue, modelIds, serviceValue).ready ? strategyValue : null;
419
+ return assessStrategyLaunch(normalizedValue, modelIds, serviceValue).ready ? normalizedValue : null;
396
420
  }
397
421
 
398
- const availability = assessStrategy(strategyValue, modelIds, serviceValue);
399
- return availability.level === 'unavailable' ? null : strategyValue;
422
+ const availability = assessStrategy(normalizedValue, modelIds, serviceValue);
423
+ return availability.level === 'unavailable' ? null : normalizedValue;
400
424
  }
401
425
 
402
426
  function getStrategyChoices(modelIds = [], serviceValue = DEFAULT_SERVICE) {
403
427
  return listStrategies(modelIds, serviceValue).map((strategy) => ({
404
- name: strategy.value,
428
+ name: strategy.selectionName ?? strategy.name ?? strategy.value,
405
429
  value: strategy.value,
406
430
  description: strategy.selectionDescription ?? strategy.description,
407
431
  }));