claude-scionos 4.1.9 → 4.1.10
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 +59 -59
- package/README.md +59 -59
- package/package.json +23 -23
- package/src/routerlab.js +184 -178
package/README.fr.md
CHANGED
|
@@ -7,10 +7,10 @@ _[🇬🇧 Read in English](./README.md)_
|
|
|
7
7
|
## Points clés
|
|
8
8
|
|
|
9
9
|
- lancement guidé pour les nouveaux utilisateurs
|
|
10
|
-
- `--strategy` pour choisir une stratégie sans menu
|
|
11
|
-
- `--service llm` pour l'accès RouterLab LLM sur invitation
|
|
12
|
-
- `--no-prompt` pour l'automatisation et la CI
|
|
13
|
-
- `--list-strategies` pour voir les routes disponibles
|
|
10
|
+
- `--strategy` pour choisir une stratégie sans menu
|
|
11
|
+
- `--service llm` pour l'accès RouterLab LLM sur invitation
|
|
12
|
+
- `--no-prompt` pour l'automatisation et la CI
|
|
13
|
+
- `--list-strategies` pour voir les routes disponibles
|
|
14
14
|
- `doctor` pour diagnostiquer rapidement un poste client
|
|
15
15
|
- `auth login|status|change|logout|test` pour gérer le token
|
|
16
16
|
- proxy local lancé uniquement si une stratégie mappée est choisie
|
|
@@ -18,8 +18,8 @@ _[🇬🇧 Read in English](./README.md)_
|
|
|
18
18
|
## Prérequis
|
|
19
19
|
|
|
20
20
|
- Node.js 22 ou plus
|
|
21
|
-
- un token RouterLab depuis [routerlab.ch/keys](https://routerlab.ch/keys)
|
|
22
|
-
- ou un token d'invitation pour `--service llm`
|
|
21
|
+
- un token RouterLab depuis [routerlab.ch/keys](https://routerlab.ch/keys)
|
|
22
|
+
- ou un token d'invitation pour `--service llm`
|
|
23
23
|
- sous Windows, Git Bash doit être installé pour Claude Code
|
|
24
24
|
|
|
25
25
|
## Installation
|
|
@@ -48,52 +48,52 @@ npx claude-scionos
|
|
|
48
48
|
Commandes utiles :
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
npx claude-scionos --list-strategies
|
|
52
|
-
npx claude-scionos doctor
|
|
53
|
-
npx claude-scionos auth login
|
|
54
|
-
npx claude-scionos auth login --service llm
|
|
55
|
-
npx claude-scionos auth test
|
|
56
|
-
npx claude-scionos --strategy aws
|
|
57
|
-
npx claude-scionos --service llm --strategy claude-glm-5
|
|
58
|
-
npx claude-scionos --strategy aws --no-prompt -p "Résume ce dépôt"
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Services
|
|
62
|
-
|
|
63
|
-
- le comportement par défaut utilise `https://routerlab.ch`
|
|
64
|
-
- `--service llm` bascule le lanceur vers `https://llm.routerlab.ch`
|
|
65
|
-
- `llm` est prévu pour un accès sur invitation
|
|
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`
|
|
69
|
-
|
|
70
|
-
## Stratégies
|
|
71
|
-
|
|
72
|
-
- `default` : utilise Claude Code normalement sans proxy local
|
|
73
|
-
- `aws` : remappe les familles de modèles Claude vers les variantes Claude AWS de RouterLab
|
|
74
|
-
- `claude-glm-5` : force toutes les requêtes vers `claude-glm-5`
|
|
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`
|
|
77
|
-
- `claude-qwen3.6-plus` : force toutes les requêtes vers `claude-qwen3.6-plus`
|
|
78
|
-
|
|
79
|
-
Utilise `--list-strategies` pour voir les stratégies disponibles pour le service choisi et leur disponibilité réelle quand un token est disponible.
|
|
51
|
+
npx claude-scionos --list-strategies
|
|
52
|
+
npx claude-scionos doctor
|
|
53
|
+
npx claude-scionos auth login
|
|
54
|
+
npx claude-scionos auth login --service llm
|
|
55
|
+
npx claude-scionos auth test
|
|
56
|
+
npx claude-scionos --strategy aws
|
|
57
|
+
npx claude-scionos --service llm --strategy claude-glm-5
|
|
58
|
+
npx claude-scionos --strategy aws --no-prompt -p "Résume ce dépôt"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Services
|
|
62
|
+
|
|
63
|
+
- le comportement par défaut utilise `https://routerlab.ch`
|
|
64
|
+
- `--service llm` bascule le lanceur vers `https://llm.routerlab.ch`
|
|
65
|
+
- `llm` est prévu pour un accès sur invitation
|
|
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`
|
|
69
|
+
|
|
70
|
+
## Stratégies
|
|
71
|
+
|
|
72
|
+
- `default` : utilise Claude Code normalement sans proxy local
|
|
73
|
+
- `aws` : remappe les familles de modèles Claude vers les variantes Claude AWS de RouterLab
|
|
74
|
+
- `claude-glm-5` : force toutes les requêtes vers `claude-glm-5`
|
|
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`
|
|
77
|
+
- `claude-qwen3.6-plus` : force toutes les requêtes vers `claude-qwen3.6-plus`
|
|
78
|
+
|
|
79
|
+
Utilise `--list-strategies` pour voir les stratégies disponibles pour le service choisi et leur disponibilité réelle quand un token est disponible.
|
|
80
80
|
|
|
81
81
|
## Gestion du token
|
|
82
82
|
|
|
83
83
|
Ordre de résolution du token :
|
|
84
84
|
|
|
85
|
-
1. `ANTHROPIC_AUTH_TOKEN`
|
|
86
|
-
2. stockage local sécurisé via `claude-scionos auth login`
|
|
87
|
-
3. stockage local sécurisé spécifique au service via `claude-scionos auth login --service llm`
|
|
88
|
-
4. saisie manuelle en mode guidé
|
|
85
|
+
1. `ANTHROPIC_AUTH_TOKEN`
|
|
86
|
+
2. stockage local sécurisé via `claude-scionos auth login`
|
|
87
|
+
3. stockage local sécurisé spécifique au service via `claude-scionos auth login --service llm`
|
|
88
|
+
4. saisie manuelle en mode guidé
|
|
89
|
+
|
|
90
|
+
Backends de stockage sécurisés :
|
|
91
|
+
|
|
92
|
+
- Windows : fichier local chiffré via DPAPI, lié à l'utilisateur courant
|
|
93
|
+
- macOS : Keychain
|
|
94
|
+
- Linux : Secret Service via `secret-tool`
|
|
89
95
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
- Windows : fichier local chiffré via DPAPI, lié à l'utilisateur courant
|
|
93
|
-
- macOS : Keychain
|
|
94
|
-
- Linux : Secret Service via `secret-tool`
|
|
95
|
-
|
|
96
|
-
Sous Windows, `claude-scionos` laisse maintenant PowerShell gérer uniquement le chiffrement et le déchiffrement DPAPI, tandis que Node.js écrit et lit directement le fichier sécurisé. Cela corrige le cas où le fichier du token était créé mais restait vide. Si un ancien fichier local est vide ou corrompu, `claude-scionos` le traite comme absent au lieu d'essayer de l'utiliser. Il faut relancer `claude-scionos auth login` pour enregistrer un nouveau token.
|
|
96
|
+
Sous Windows, `claude-scionos` laisse maintenant PowerShell gérer uniquement le chiffrement et le déchiffrement DPAPI, tandis que Node.js écrit et lit directement le fichier sécurisé. Cela corrige le cas où le fichier du token était créé mais restait vide. Si un ancien fichier local est vide ou corrompu, `claude-scionos` le traite comme absent au lieu d'essayer de l'utiliser. Il faut relancer `claude-scionos auth login` pour enregistrer un nouveau token.
|
|
97
97
|
|
|
98
98
|
Gestion du token :
|
|
99
99
|
|
|
@@ -105,11 +105,11 @@ claude-scionos auth logout
|
|
|
105
105
|
claude-scionos auth test
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
## Ce que veulent dire `--strategy` et `--no-prompt`
|
|
109
|
-
|
|
110
|
-
- `--strategy <value>` évite le menu interactif et choisit directement la route
|
|
111
|
-
- `--service <value>` change la cible RouterLab. `routerlab` reste le défaut et `llm` est réservé à l'accès sur invitation
|
|
112
|
-
- `--no-prompt` désactive toutes les questions interactives
|
|
108
|
+
## Ce que veulent dire `--strategy` et `--no-prompt`
|
|
109
|
+
|
|
110
|
+
- `--strategy <value>` évite le menu interactif et choisit directement la route
|
|
111
|
+
- `--service <value>` change la cible RouterLab. `routerlab` reste le défaut et `llm` est réservé à l'accès sur invitation
|
|
112
|
+
- `--no-prompt` désactive toutes les questions interactives
|
|
113
113
|
|
|
114
114
|
Quand `--no-prompt` est utilisé, le lanceur doit déjà avoir un token via `ANTHROPIC_AUTH_TOKEN` ou via le stockage sécurisé.
|
|
115
115
|
|
|
@@ -132,14 +132,14 @@ Le wrapper transmet les arguments Claude Code habituels. Le proxy local n'est la
|
|
|
132
132
|
|
|
133
133
|
`claude-scionos doctor` doit être la première commande à lancer quand un client signale un problème.
|
|
134
134
|
|
|
135
|
-
Cas courants :
|
|
136
|
-
|
|
137
|
-
- `Claude Code CLI not found` : installer `@anthropic-ai/claude-code`
|
|
138
|
-
- `Git Bash is required on Windows` : installer Git for Windows
|
|
139
|
-
- `ANTHROPIC_AUTH_TOKEN ... is required when using --no-prompt` : définir la variable d'environnement ou stocker le token au préalable
|
|
140
|
-
- `Secure token file was created but no encrypted content was written` : mettre à jour vers `4.1.
|
|
141
|
-
- `Stored token` est indiqué comme absent sous Windows alors qu'un login a déjà été fait : relancer `claude-scionos auth login`, car le fichier DPAPI local peut être vide ou corrompu
|
|
142
|
-
- `secret-tool not found` : installer un client Secret Service sous Linux ou utiliser la variable d'environnement
|
|
135
|
+
Cas courants :
|
|
136
|
+
|
|
137
|
+
- `Claude Code CLI not found` : installer `@anthropic-ai/claude-code`
|
|
138
|
+
- `Git Bash is required on Windows` : installer Git for Windows
|
|
139
|
+
- `ANTHROPIC_AUTH_TOKEN ... is required when using --no-prompt` : définir la variable d'environnement ou stocker le token au préalable
|
|
140
|
+
- `Secure token file was created but no encrypted content was written` : mettre à jour vers `4.1.10` ou plus récent, puis relancer `claude-scionos auth login`
|
|
141
|
+
- `Stored token` est indiqué comme absent sous Windows alors qu'un login a déjà été fait : relancer `claude-scionos auth login`, car le fichier DPAPI local peut être vide ou corrompu
|
|
142
|
+
- `secret-tool not found` : installer un client Secret Service sous Linux ou utiliser la variable d'environnement
|
|
143
143
|
|
|
144
144
|
## Développement
|
|
145
145
|
|
package/README.md
CHANGED
|
@@ -7,10 +7,10 @@ _[🇫🇷 Lire en français](./README.fr.md)_
|
|
|
7
7
|
## Highlights
|
|
8
8
|
|
|
9
9
|
- Guided launch for first-time users
|
|
10
|
-
- `--strategy` to preselect a routing strategy
|
|
11
|
-
- `--service llm` for invitation-only RouterLab LLM access
|
|
12
|
-
- `--no-prompt` for automation and CI
|
|
13
|
-
- `--list-strategies` to inspect available routes
|
|
10
|
+
- `--strategy` to preselect a routing strategy
|
|
11
|
+
- `--service llm` for invitation-only RouterLab LLM access
|
|
12
|
+
- `--no-prompt` for automation and CI
|
|
13
|
+
- `--list-strategies` to inspect available routes
|
|
14
14
|
- `doctor` to diagnose local setup quickly
|
|
15
15
|
- `auth login|status|change|logout|test` for secure token management
|
|
16
16
|
- Local proxy only when a mapped strategy is selected
|
|
@@ -18,8 +18,8 @@ _[🇫🇷 Lire en français](./README.fr.md)_
|
|
|
18
18
|
## Requirements
|
|
19
19
|
|
|
20
20
|
- Node.js 22 or later
|
|
21
|
-
- A RouterLab token from [routerlab.ch/keys](https://routerlab.ch/keys)
|
|
22
|
-
- Or an invitation token for `--service llm`
|
|
21
|
+
- A RouterLab token from [routerlab.ch/keys](https://routerlab.ch/keys)
|
|
22
|
+
- Or an invitation token for `--service llm`
|
|
23
23
|
- On Windows, Git Bash must be installed for Claude Code
|
|
24
24
|
|
|
25
25
|
## Installation
|
|
@@ -48,52 +48,52 @@ npx claude-scionos
|
|
|
48
48
|
Useful commands:
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
npx claude-scionos --list-strategies
|
|
52
|
-
npx claude-scionos doctor
|
|
53
|
-
npx claude-scionos auth login
|
|
54
|
-
npx claude-scionos auth login --service llm
|
|
55
|
-
npx claude-scionos auth test
|
|
56
|
-
npx claude-scionos --strategy aws
|
|
57
|
-
npx claude-scionos --service llm --strategy claude-glm-5
|
|
58
|
-
npx claude-scionos --strategy aws --no-prompt -p "Summarize this repo"
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Services
|
|
62
|
-
|
|
63
|
-
- Default behavior uses `https://routerlab.ch`
|
|
64
|
-
- `--service llm` switches the launcher to `https://llm.routerlab.ch`
|
|
65
|
-
- `llm` is intended for invitation-only access
|
|
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`
|
|
69
|
-
|
|
70
|
-
## Strategies
|
|
71
|
-
|
|
72
|
-
- `default`: use Claude Code normally without the local proxy
|
|
73
|
-
- `aws`: remap Claude model families to RouterLab AWS-backed Claude variants
|
|
74
|
-
- `claude-glm-5`: force all requests to `claude-glm-5`
|
|
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`
|
|
77
|
-
- `claude-qwen3.6-plus`: force all requests to `claude-qwen3.6-plus`
|
|
78
|
-
|
|
79
|
-
Use `--list-strategies` to see the strategies available for the selected service and their live availability when a token is available.
|
|
51
|
+
npx claude-scionos --list-strategies
|
|
52
|
+
npx claude-scionos doctor
|
|
53
|
+
npx claude-scionos auth login
|
|
54
|
+
npx claude-scionos auth login --service llm
|
|
55
|
+
npx claude-scionos auth test
|
|
56
|
+
npx claude-scionos --strategy aws
|
|
57
|
+
npx claude-scionos --service llm --strategy claude-glm-5
|
|
58
|
+
npx claude-scionos --strategy aws --no-prompt -p "Summarize this repo"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Services
|
|
62
|
+
|
|
63
|
+
- Default behavior uses `https://routerlab.ch`
|
|
64
|
+
- `--service llm` switches the launcher to `https://llm.routerlab.ch`
|
|
65
|
+
- `llm` is intended for invitation-only access
|
|
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`
|
|
69
|
+
|
|
70
|
+
## Strategies
|
|
71
|
+
|
|
72
|
+
- `default`: use Claude Code normally without the local proxy
|
|
73
|
+
- `aws`: remap Claude model families to RouterLab AWS-backed Claude variants
|
|
74
|
+
- `claude-glm-5`: force all requests to `claude-glm-5`
|
|
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`
|
|
77
|
+
- `claude-qwen3.6-plus`: force all requests to `claude-qwen3.6-plus`
|
|
78
|
+
|
|
79
|
+
Use `--list-strategies` to see the strategies available for the selected service and their live availability when a token is available.
|
|
80
80
|
|
|
81
81
|
## Token Handling
|
|
82
82
|
|
|
83
83
|
Token resolution order:
|
|
84
84
|
|
|
85
|
-
1. `ANTHROPIC_AUTH_TOKEN`
|
|
86
|
-
2. Secure local storage from `claude-scionos auth login`
|
|
87
|
-
3. Service-specific secure local storage from `claude-scionos auth login --service llm`
|
|
88
|
-
4. Manual prompt in guided mode
|
|
85
|
+
1. `ANTHROPIC_AUTH_TOKEN`
|
|
86
|
+
2. Secure local storage from `claude-scionos auth login`
|
|
87
|
+
3. Service-specific secure local storage from `claude-scionos auth login --service llm`
|
|
88
|
+
4. Manual prompt in guided mode
|
|
89
|
+
|
|
90
|
+
Secure storage backends:
|
|
91
|
+
|
|
92
|
+
- Windows: DPAPI-encrypted local file bound to the current user
|
|
93
|
+
- macOS: Keychain
|
|
94
|
+
- Linux: Secret Service via `secret-tool`
|
|
89
95
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
- Windows: DPAPI-encrypted local file bound to the current user
|
|
93
|
-
- macOS: Keychain
|
|
94
|
-
- Linux: Secret Service via `secret-tool`
|
|
95
|
-
|
|
96
|
-
On Windows, `claude-scionos` now lets PowerShell handle only DPAPI encryption and decryption, while Node.js writes and reads the secure token file directly. This fixes the case where the secure token file was created but left empty. If an older file is empty or corrupted, `claude-scionos` treats it as missing instead of trying to use it. Run `claude-scionos auth login` again to store a fresh token.
|
|
96
|
+
On Windows, `claude-scionos` now lets PowerShell handle only DPAPI encryption and decryption, while Node.js writes and reads the secure token file directly. This fixes the case where the secure token file was created but left empty. If an older file is empty or corrupted, `claude-scionos` treats it as missing instead of trying to use it. Run `claude-scionos auth login` again to store a fresh token.
|
|
97
97
|
|
|
98
98
|
Manage the token with:
|
|
99
99
|
|
|
@@ -105,11 +105,11 @@ claude-scionos auth logout
|
|
|
105
105
|
claude-scionos auth test
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
## What `--strategy` and `--no-prompt` mean
|
|
109
|
-
|
|
110
|
-
- `--strategy <value>` skips the interactive strategy menu and selects the route directly
|
|
111
|
-
- `--service <value>` switches between RouterLab targets. `routerlab` is the default and `llm` is invitation-only
|
|
112
|
-
- `--no-prompt` disables every interactive question
|
|
108
|
+
## What `--strategy` and `--no-prompt` mean
|
|
109
|
+
|
|
110
|
+
- `--strategy <value>` skips the interactive strategy menu and selects the route directly
|
|
111
|
+
- `--service <value>` switches between RouterLab targets. `routerlab` is the default and `llm` is invitation-only
|
|
112
|
+
- `--no-prompt` disables every interactive question
|
|
113
113
|
|
|
114
114
|
When `--no-prompt` is used, the launcher must already have a token from `ANTHROPIC_AUTH_TOKEN` or secure storage.
|
|
115
115
|
|
|
@@ -132,14 +132,14 @@ The wrapper forwards regular Claude Code flags and arguments. The local proxy is
|
|
|
132
132
|
|
|
133
133
|
`claude-scionos doctor` should be the first command to run when a client reports an issue.
|
|
134
134
|
|
|
135
|
-
Common cases:
|
|
136
|
-
|
|
137
|
-
- `Claude Code CLI not found`: install `@anthropic-ai/claude-code`
|
|
138
|
-
- `Git Bash is required on Windows`: install Git for Windows
|
|
139
|
-
- `ANTHROPIC_AUTH_TOKEN ... is required when using --no-prompt`: set the environment variable or store the token first
|
|
140
|
-
- `Secure token file was created but no encrypted content was written`: update to `4.1.
|
|
141
|
-
- `Stored token` is missing on Windows even though you already logged in: re-run `claude-scionos auth login` because the local DPAPI token file may be empty or corrupted
|
|
142
|
-
- `secret-tool not found`: install a Secret Service client on Linux or rely on the environment variable
|
|
135
|
+
Common cases:
|
|
136
|
+
|
|
137
|
+
- `Claude Code CLI not found`: install `@anthropic-ai/claude-code`
|
|
138
|
+
- `Git Bash is required on Windows`: install Git for Windows
|
|
139
|
+
- `ANTHROPIC_AUTH_TOKEN ... is required when using --no-prompt`: set the environment variable or store the token first
|
|
140
|
+
- `Secure token file was created but no encrypted content was written`: update to `4.1.10` or later, then re-run `claude-scionos auth login`
|
|
141
|
+
- `Stored token` is missing on Windows even though you already logged in: re-run `claude-scionos auth login` because the local DPAPI token file may be empty or corrupted
|
|
142
|
+
- `secret-tool not found`: install a Secret Service client on Linux or rely on the environment variable
|
|
143
143
|
|
|
144
144
|
## Development
|
|
145
145
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "claude-scionos",
|
|
3
|
-
"version": "4.1.
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-scionos",
|
|
3
|
+
"version": "4.1.10",
|
|
4
4
|
"description": "RouterLab launcher, strategy proxy and secure token wrapper for Claude Code CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"release:patch": "npm version patch -m \"Chore: Bump version to %s\"",
|
|
21
21
|
"release:minor": "npm version minor -m \"Chore: Bump version to %s\"",
|
|
22
22
|
"release:major": "npm version major -m \"Chore: Bump version to %s\""
|
|
23
|
-
},
|
|
23
|
+
},
|
|
24
24
|
"keywords": [
|
|
25
25
|
"claude",
|
|
26
26
|
"scionos",
|
|
@@ -28,27 +28,27 @@
|
|
|
28
28
|
"cli",
|
|
29
29
|
"wrapper"
|
|
30
30
|
],
|
|
31
|
-
"author": "ScioNos",
|
|
32
|
-
"license": "MIT",
|
|
31
|
+
"author": "ScioNos",
|
|
32
|
+
"license": "MIT",
|
|
33
33
|
"homepage": "https://routerlab.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
|
-
},
|
|
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
44
|
"private": false,
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@inquirer/prompts": "^8.4.1"
|
|
47
47
|
},
|
|
48
|
-
"devDependencies": {
|
|
49
|
-
"@eslint/js": "^10.0.1",
|
|
50
|
-
"eslint": "^10.2.0",
|
|
51
|
-
"globals": "^17.5.0",
|
|
52
|
-
"vitest": "^4.1.4"
|
|
53
|
-
}
|
|
54
|
-
}
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@eslint/js": "^10.0.1",
|
|
50
|
+
"eslint": "^10.2.0",
|
|
51
|
+
"globals": "^17.5.0",
|
|
52
|
+
"vitest": "^4.1.4"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/routerlab.js
CHANGED
|
@@ -36,6 +36,10 @@ const BASE_URL = SERVICES[DEFAULT_SERVICE].baseUrl;
|
|
|
36
36
|
const TOKEN_HELP_URL = SERVICES[DEFAULT_SERVICE].tokenHelpUrl;
|
|
37
37
|
const DEFAULT_ANTHROPIC_VERSION = '2023-06-01';
|
|
38
38
|
const SECURE_STORAGE_SERVICE = 'claude-scionos';
|
|
39
|
+
const WINDOWS_POWERSHELL_MODULE_PATHS = [
|
|
40
|
+
'C:\\Program Files\\WindowsPowerShell\\Modules',
|
|
41
|
+
'C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\Modules',
|
|
42
|
+
];
|
|
39
43
|
const SECURE_STORAGE_ACCOUNT = SERVICES[DEFAULT_SERVICE].secureStorageAccount;
|
|
40
44
|
const DEFAULT_CLAUDE_MODELS = [
|
|
41
45
|
'claude-haiku-4-5-20251001',
|
|
@@ -69,10 +73,10 @@ const STRATEGIES = [
|
|
|
69
73
|
{
|
|
70
74
|
value: 'claude-glm-5',
|
|
71
75
|
name: 'GLM-5',
|
|
72
|
-
description: 'Forces all requests to claude-glm-5.',
|
|
73
|
-
selectionDescription: 'Forces all requests to claude-glm-5.',
|
|
74
|
-
mappedModels: ['claude-glm-5'],
|
|
75
|
-
},
|
|
76
|
+
description: 'Forces all requests to claude-glm-5.',
|
|
77
|
+
selectionDescription: 'Forces all requests to claude-glm-5.',
|
|
78
|
+
mappedModels: ['claude-glm-5'],
|
|
79
|
+
},
|
|
76
80
|
{
|
|
77
81
|
value: 'claude-minimax-m2.5',
|
|
78
82
|
name: 'MiniMax M2.5',
|
|
@@ -95,85 +99,85 @@ const STRATEGIES = [
|
|
|
95
99
|
mappedModels: ['claude-qwen3.6-plus'],
|
|
96
100
|
},
|
|
97
101
|
];
|
|
98
|
-
|
|
102
|
+
|
|
99
103
|
async function fetchModels(apiKey, options = {}) {
|
|
100
104
|
const {
|
|
101
105
|
baseUrl = BASE_URL,
|
|
102
|
-
anthropicVersion = DEFAULT_ANTHROPIC_VERSION,
|
|
103
|
-
timeoutMs = 30000,
|
|
104
|
-
} = options;
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
const controller = new AbortController();
|
|
108
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
109
|
-
const response = await fetch(`${baseUrl}/v1/models`, {
|
|
110
|
-
method: 'GET',
|
|
111
|
-
headers: {
|
|
112
|
-
'x-api-key': apiKey,
|
|
113
|
-
'anthropic-version': anthropicVersion,
|
|
114
|
-
},
|
|
115
|
-
signal: controller.signal,
|
|
116
|
-
});
|
|
117
|
-
clearTimeout(timeoutId);
|
|
118
|
-
|
|
119
|
-
if (response.ok) {
|
|
120
|
-
let payload = null;
|
|
121
|
-
try {
|
|
122
|
-
payload = await response.json();
|
|
123
|
-
} catch {
|
|
124
|
-
payload = null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
valid: true,
|
|
129
|
-
models: payload ? extractModelIds(payload) : null,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (response.status === 401 || response.status === 403) {
|
|
134
|
-
return {valid: false, reason: 'auth_failed'};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
valid: false,
|
|
139
|
-
reason: 'server_error',
|
|
140
|
-
status: response.status,
|
|
141
|
-
statusText: response.statusText,
|
|
142
|
-
message: `Server responded with ${response.status} ${response.statusText}`.trim(),
|
|
143
|
-
};
|
|
144
|
-
} catch (error) {
|
|
145
|
-
if (error.name === 'AbortError') {
|
|
146
|
-
return {valid: false, reason: 'timeout', message: `Request timed out after ${Math.round(timeoutMs / 1000)}s`};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return {valid: false, reason: 'network_error', message: error.message};
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
async function validateToken(apiKey, options = {}) {
|
|
154
|
-
return fetchModels(apiKey, options);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function extractModelIds(payload) {
|
|
158
|
-
if (!payload) {
|
|
159
|
-
return [];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (Array.isArray(payload)) {
|
|
163
|
-
return payload.map((entry) => entry?.id ?? entry?.name ?? entry).filter(Boolean);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (Array.isArray(payload.data)) {
|
|
167
|
-
return payload.data.map((entry) => entry?.id ?? entry?.name ?? entry).filter(Boolean);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (Array.isArray(payload.models)) {
|
|
171
|
-
return payload.models.map((entry) => entry?.id ?? entry?.name ?? entry).filter(Boolean);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
176
|
-
|
|
106
|
+
anthropicVersion = DEFAULT_ANTHROPIC_VERSION,
|
|
107
|
+
timeoutMs = 30000,
|
|
108
|
+
} = options;
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const controller = new AbortController();
|
|
112
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
113
|
+
const response = await fetch(`${baseUrl}/v1/models`, {
|
|
114
|
+
method: 'GET',
|
|
115
|
+
headers: {
|
|
116
|
+
'x-api-key': apiKey,
|
|
117
|
+
'anthropic-version': anthropicVersion,
|
|
118
|
+
},
|
|
119
|
+
signal: controller.signal,
|
|
120
|
+
});
|
|
121
|
+
clearTimeout(timeoutId);
|
|
122
|
+
|
|
123
|
+
if (response.ok) {
|
|
124
|
+
let payload = null;
|
|
125
|
+
try {
|
|
126
|
+
payload = await response.json();
|
|
127
|
+
} catch {
|
|
128
|
+
payload = null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
valid: true,
|
|
133
|
+
models: payload ? extractModelIds(payload) : null,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (response.status === 401 || response.status === 403) {
|
|
138
|
+
return {valid: false, reason: 'auth_failed'};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
valid: false,
|
|
143
|
+
reason: 'server_error',
|
|
144
|
+
status: response.status,
|
|
145
|
+
statusText: response.statusText,
|
|
146
|
+
message: `Server responded with ${response.status} ${response.statusText}`.trim(),
|
|
147
|
+
};
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (error.name === 'AbortError') {
|
|
150
|
+
return {valid: false, reason: 'timeout', message: `Request timed out after ${Math.round(timeoutMs / 1000)}s`};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {valid: false, reason: 'network_error', message: error.message};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function validateToken(apiKey, options = {}) {
|
|
158
|
+
return fetchModels(apiKey, options);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function extractModelIds(payload) {
|
|
162
|
+
if (!payload) {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (Array.isArray(payload)) {
|
|
167
|
+
return payload.map((entry) => entry?.id ?? entry?.name ?? entry).filter(Boolean);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (Array.isArray(payload.data)) {
|
|
171
|
+
return payload.data.map((entry) => entry?.id ?? entry?.name ?? entry).filter(Boolean);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (Array.isArray(payload.models)) {
|
|
175
|
+
return payload.models.map((entry) => entry?.id ?? entry?.name ?? entry).filter(Boolean);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
|
|
177
181
|
function canProceedWithValidation(validation) {
|
|
178
182
|
return validation.valid === true;
|
|
179
183
|
}
|
|
@@ -256,9 +260,9 @@ function assessStrategy(strategyValue, modelIds = [], serviceValue = DEFAULT_SER
|
|
|
256
260
|
const strategy = findStrategy(strategyValue, serviceValue);
|
|
257
261
|
if (!strategy) {
|
|
258
262
|
return {
|
|
259
|
-
available: false,
|
|
260
|
-
level: 'unavailable',
|
|
261
|
-
note: 'Unknown strategy.',
|
|
263
|
+
available: false,
|
|
264
|
+
level: 'unavailable',
|
|
265
|
+
note: 'Unknown strategy.',
|
|
262
266
|
strategy: null,
|
|
263
267
|
};
|
|
264
268
|
}
|
|
@@ -293,17 +297,17 @@ function assessStrategy(strategyValue, modelIds = [], serviceValue = DEFAULT_SER
|
|
|
293
297
|
strategy,
|
|
294
298
|
};
|
|
295
299
|
}
|
|
296
|
-
|
|
297
|
-
if (presentCount > 0) {
|
|
298
|
-
return {
|
|
300
|
+
|
|
301
|
+
if (presentCount > 0) {
|
|
302
|
+
return {
|
|
299
303
|
available: true,
|
|
300
304
|
level: 'partial',
|
|
301
305
|
note: `Partially available on ${serviceLabel}.`,
|
|
302
306
|
strategy,
|
|
303
307
|
};
|
|
304
308
|
}
|
|
305
|
-
|
|
306
|
-
return {
|
|
309
|
+
|
|
310
|
+
return {
|
|
307
311
|
available: false,
|
|
308
312
|
level: 'unavailable',
|
|
309
313
|
note: `Not reported by ${serviceLabel}.`,
|
|
@@ -383,30 +387,30 @@ function getStrategyChoices(modelIds = [], serviceValue = DEFAULT_SERVICE) {
|
|
|
383
387
|
description: strategy.selectionDescription ?? strategy.description,
|
|
384
388
|
}));
|
|
385
389
|
}
|
|
386
|
-
|
|
387
|
-
function getSecureStorageBackend() {
|
|
388
|
-
if (process.platform === 'win32') {
|
|
389
|
-
return {supported: true, backend: 'Windows DPAPI'};
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
if (process.platform === 'darwin') {
|
|
393
|
-
return {supported: true, backend: 'macOS Keychain'};
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (process.platform === 'linux') {
|
|
397
|
-
return commandExists('secret-tool')
|
|
398
|
-
? {supported: true, backend: 'Linux Secret Service'}
|
|
399
|
-
: {supported: false, backend: 'Linux Secret Service', reason: '`secret-tool` not found'};
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
return {supported: false, backend: 'Unknown', reason: 'Unsupported operating system'};
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function commandExists(command) {
|
|
406
|
-
const result = spawnSync(command, ['--help'], {encoding: 'utf8'});
|
|
407
|
-
return !result.error;
|
|
408
|
-
}
|
|
409
|
-
|
|
390
|
+
|
|
391
|
+
function getSecureStorageBackend() {
|
|
392
|
+
if (process.platform === 'win32') {
|
|
393
|
+
return {supported: true, backend: 'Windows DPAPI'};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (process.platform === 'darwin') {
|
|
397
|
+
return {supported: true, backend: 'macOS Keychain'};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (process.platform === 'linux') {
|
|
401
|
+
return commandExists('secret-tool')
|
|
402
|
+
? {supported: true, backend: 'Linux Secret Service'}
|
|
403
|
+
: {supported: false, backend: 'Linux Secret Service', reason: '`secret-tool` not found'};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return {supported: false, backend: 'Unknown', reason: 'Unsupported operating system'};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function commandExists(command) {
|
|
410
|
+
const result = spawnSync(command, ['--help'], {encoding: 'utf8'});
|
|
411
|
+
return !result.error;
|
|
412
|
+
}
|
|
413
|
+
|
|
410
414
|
function runPowerShell(command, options = {}) {
|
|
411
415
|
const {
|
|
412
416
|
env = {},
|
|
@@ -415,41 +419,43 @@ function runPowerShell(command, options = {}) {
|
|
|
415
419
|
const powershell = process.env.SystemRoot
|
|
416
420
|
? path.join(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe')
|
|
417
421
|
: 'powershell.exe';
|
|
422
|
+
const childEnv = {
|
|
423
|
+
...process.env,
|
|
424
|
+
...(process.platform === 'win32' ? {PSModulePath: WINDOWS_POWERSHELL_MODULE_PATHS.join(';')} : {}),
|
|
425
|
+
...env,
|
|
426
|
+
};
|
|
418
427
|
|
|
419
428
|
const result = spawnSync(powershell, ['-NoProfile', '-NonInteractive', '-Command', command], {
|
|
420
429
|
encoding: 'utf8',
|
|
421
430
|
input,
|
|
422
|
-
env:
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
return result;
|
|
451
|
-
}
|
|
452
|
-
|
|
431
|
+
env: childEnv,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
if (result.error) {
|
|
435
|
+
throw result.error;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (result.status !== 0) {
|
|
439
|
+
throw new Error((result.stderr || result.stdout || 'PowerShell command failed').trim());
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return result.stdout.trim();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function runCommand(command, args, options = {}) {
|
|
446
|
+
const result = spawnSync(command, args, {
|
|
447
|
+
encoding: 'utf8',
|
|
448
|
+
input: options.input,
|
|
449
|
+
env: options.env,
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
if (result.error) {
|
|
453
|
+
throw result.error;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return result;
|
|
457
|
+
}
|
|
458
|
+
|
|
453
459
|
function storeToken(token, serviceValue = DEFAULT_SERVICE) {
|
|
454
460
|
const service = getServiceConfig(serviceValue);
|
|
455
461
|
if (!service) {
|
|
@@ -459,7 +465,7 @@ function storeToken(token, serviceValue = DEFAULT_SERVICE) {
|
|
|
459
465
|
const storage = getSecureStorageBackend();
|
|
460
466
|
if (!storage.supported) {
|
|
461
467
|
throw new Error(storage.reason || 'Secure storage is not available on this machine');
|
|
462
|
-
}
|
|
468
|
+
}
|
|
463
469
|
|
|
464
470
|
if (process.platform === 'win32') {
|
|
465
471
|
const tokenFile = getWindowsTokenFile(service.value);
|
|
@@ -476,9 +482,9 @@ function storeToken(token, serviceValue = DEFAULT_SERVICE) {
|
|
|
476
482
|
|
|
477
483
|
return storage;
|
|
478
484
|
}
|
|
479
|
-
|
|
480
|
-
if (process.platform === 'darwin') {
|
|
481
|
-
const result = runCommand('security', [
|
|
485
|
+
|
|
486
|
+
if (process.platform === 'darwin') {
|
|
487
|
+
const result = runCommand('security', [
|
|
482
488
|
'add-generic-password',
|
|
483
489
|
'-U',
|
|
484
490
|
'-a',
|
|
@@ -486,16 +492,16 @@ function storeToken(token, serviceValue = DEFAULT_SERVICE) {
|
|
|
486
492
|
'-s',
|
|
487
493
|
SECURE_STORAGE_SERVICE,
|
|
488
494
|
'-w',
|
|
489
|
-
token,
|
|
490
|
-
]);
|
|
491
|
-
|
|
492
|
-
if (result.status !== 0) {
|
|
493
|
-
throw new Error((result.stderr || result.stdout || 'Unable to store token in Keychain').trim());
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
return storage;
|
|
497
|
-
}
|
|
498
|
-
|
|
495
|
+
token,
|
|
496
|
+
]);
|
|
497
|
+
|
|
498
|
+
if (result.status !== 0) {
|
|
499
|
+
throw new Error((result.stderr || result.stdout || 'Unable to store token in Keychain').trim());
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return storage;
|
|
503
|
+
}
|
|
504
|
+
|
|
499
505
|
const result = runCommand('secret-tool', [
|
|
500
506
|
'store',
|
|
501
507
|
`--label=${service.secureStorageLabel}`,
|
|
@@ -506,14 +512,14 @@ function storeToken(token, serviceValue = DEFAULT_SERVICE) {
|
|
|
506
512
|
], {
|
|
507
513
|
input: token,
|
|
508
514
|
});
|
|
509
|
-
|
|
510
|
-
if (result.status !== 0) {
|
|
511
|
-
throw new Error((result.stderr || result.stdout || 'Unable to store token in Secret Service').trim());
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
return storage;
|
|
515
|
-
}
|
|
516
|
-
|
|
515
|
+
|
|
516
|
+
if (result.status !== 0) {
|
|
517
|
+
throw new Error((result.stderr || result.stdout || 'Unable to store token in Secret Service').trim());
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return storage;
|
|
521
|
+
}
|
|
522
|
+
|
|
517
523
|
function getStoredToken(serviceValue = DEFAULT_SERVICE) {
|
|
518
524
|
const service = getServiceConfig(serviceValue);
|
|
519
525
|
if (!service) {
|
|
@@ -546,8 +552,8 @@ function getStoredToken(serviceValue = DEFAULT_SERVICE) {
|
|
|
546
552
|
);
|
|
547
553
|
return token || null;
|
|
548
554
|
}
|
|
549
|
-
|
|
550
|
-
if (process.platform === 'darwin') {
|
|
555
|
+
|
|
556
|
+
if (process.platform === 'darwin') {
|
|
551
557
|
const result = runCommand('security', [
|
|
552
558
|
'find-generic-password',
|
|
553
559
|
'-a',
|
|
@@ -555,10 +561,10 @@ function getStoredToken(serviceValue = DEFAULT_SERVICE) {
|
|
|
555
561
|
'-s',
|
|
556
562
|
SECURE_STORAGE_SERVICE,
|
|
557
563
|
'-w',
|
|
558
|
-
]);
|
|
559
|
-
return result.status === 0 ? result.stdout.trim() || null : null;
|
|
560
|
-
}
|
|
561
|
-
|
|
564
|
+
]);
|
|
565
|
+
return result.status === 0 ? result.stdout.trim() || null : null;
|
|
566
|
+
}
|
|
567
|
+
|
|
562
568
|
const result = runCommand('secret-tool', [
|
|
563
569
|
'lookup',
|
|
564
570
|
'service',
|
|
@@ -598,9 +604,9 @@ function deleteStoredToken(serviceValue = DEFAULT_SERVICE) {
|
|
|
598
604
|
'-s',
|
|
599
605
|
SECURE_STORAGE_SERVICE,
|
|
600
606
|
]);
|
|
601
|
-
return result.status === 0;
|
|
602
|
-
}
|
|
603
|
-
|
|
607
|
+
return result.status === 0;
|
|
608
|
+
}
|
|
609
|
+
|
|
604
610
|
const result = runCommand('secret-tool', [
|
|
605
611
|
'clear',
|
|
606
612
|
'service',
|
|
@@ -616,10 +622,10 @@ function getStoredTokenStatus(serviceValue = DEFAULT_SERVICE) {
|
|
|
616
622
|
const storedToken = getStoredToken(serviceValue);
|
|
617
623
|
return {
|
|
618
624
|
...storage,
|
|
619
|
-
stored: Boolean(storedToken),
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
|
|
625
|
+
stored: Boolean(storedToken),
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
623
629
|
export {
|
|
624
630
|
BASE_URL,
|
|
625
631
|
DEFAULT_CLAUDE_MODELS,
|