claude-connect 0.1.8 → 0.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.md +20 -3
- package/package.json +1 -1
- package/src/data/catalog-store.js +69 -0
- package/src/gateway/server.js +5 -0
- package/src/lib/claude-settings.js +19 -3
- package/src/lib/provider-rate-limit.js +109 -0
- package/src/lib/theme.js +23 -0
- package/src/wizard.js +23 -2
package/README.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# Claude Connect
|
|
2
2
|
|
|
3
|
-
> Conecta `Claude Code` con `OpenCode Go`, `Zen`, `Kimi`, `DeepSeek`, `Ollama`, `OpenAI`, `Inception Labs`, `OpenRouter` y `Qwen` desde una interfaz de consola clara, rápida y reversible.
|
|
3
|
+
> Conecta `Claude Code` con `OpenCode Go`, `Zen`, `Kimi`, `DeepSeek`, `Z.AI`, `Ollama`, `OpenAI`, `Inception Labs`, `OpenRouter` y `Qwen` desde una interfaz de consola clara, rápida y reversible.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/claude-connect)
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
7
|
[](./LICENSE)
|
|
8
|
-
[](https://www.npmjs.com/package/claude-connect)
|
|
8
|
+
[](https://www.npmjs.com/package/claude-connect)
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<img src="./ezgif-871b2bc9267494c5.gif" alt="Claude Connect demo" width="980" />
|
|
12
|
+
</p>
|
|
9
13
|
|
|
10
14
|
## Why Claude Connect
|
|
11
15
|
|
|
@@ -13,7 +17,7 @@
|
|
|
13
17
|
|
|
14
18
|
### Highlights
|
|
15
19
|
|
|
16
|
-
- `OpenCode Go`, `Zen`, `Kimi`, `DeepSeek`, `Ollama`, `OpenAI`, `Inception Labs`, `OpenRouter` y `Qwen` listos desde el primer arranque
|
|
20
|
+
- `OpenCode Go`, `Zen`, `Kimi`, `DeepSeek`, `Z.AI`, `Ollama`, `OpenAI`, `Inception Labs`, `OpenRouter` y `Qwen` listos desde el primer arranque
|
|
17
21
|
- soporte para `Token` y `OAuth` cuando el proveedor lo permite
|
|
18
22
|
- API keys compartidas por proveedor para no repetir el mismo token en cada modelo
|
|
19
23
|
- activación reversible sobre la instalación real de `Claude Code`
|
|
@@ -78,12 +82,14 @@ Al activar:
|
|
|
78
82
|
- `Zen` usa conexión directa o gateway según el modelo elegido
|
|
79
83
|
- `Kimi` usa gateway local y reenvia al endpoint Anthropic de `https://api.kimi.com/coding/`
|
|
80
84
|
- `DeepSeek` apunta a `https://api.deepseek.com/anthropic`
|
|
85
|
+
- `Z.AI` apunta a `https://api.z.ai/api/anthropic`
|
|
81
86
|
- `Ollama` pide una URL local o remota, valida `/api/tags` y usa el gateway local sobre `.../api/chat`
|
|
82
87
|
- `OpenAI` usa el gateway local sobre `https://api.openai.com/v1/chat/completions`
|
|
83
88
|
- `Inception Labs` usa el gateway local sobre `https://api.inceptionlabs.ai/v1/chat/completions`
|
|
84
89
|
- `OpenRouter` usa `openrouter/free` por gateway sobre `https://openrouter.ai/api/v1`
|
|
85
90
|
- `Qwen` apunta al gateway local `http://127.0.0.1:4310/anthropic`
|
|
86
91
|
- para algunos modelos con limites conocidos, el gateway ahora ajusta `max_tokens` y bloquea prompts sobredimensionados antes de que el upstream devuelva errores opacos
|
|
92
|
+
- para `Inception Labs`, el gateway tambien respeta un presupuesto local de input tokens por minuto para reducir errores de `Rate limit reached`
|
|
87
93
|
|
|
88
94
|
## Providers
|
|
89
95
|
|
|
@@ -93,6 +99,7 @@ Al activar:
|
|
|
93
99
|
| `Zen` | `Claude*` de Zen + modelos `chat/completions` de Zen | `Token` | Mixta |
|
|
94
100
|
| `Kimi` | `kimi-for-coding` | `Token` | Gateway local |
|
|
95
101
|
| `DeepSeek` | `deepseek-chat`, `deepseek-reasoner` | `Token` | Directa |
|
|
102
|
+
| `Z.AI` | `glm-5.1`, `glm-4.7`, `glm-4.5-air` | `Token` | Directa |
|
|
96
103
|
| `Ollama` | modelos descubiertos desde tu servidor | `Servidor Ollama` | Gateway local |
|
|
97
104
|
| `OpenAI` | `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.3-codex`, `gpt-5.2-codex`, `gpt-5.2`, `gpt-5.1-codex-max`, `gpt-5.1-codex-mini` | `Token` | Gateway local |
|
|
98
105
|
| `Inception Labs` | `mercury-2` | `Token` | Gateway local |
|
|
@@ -126,12 +133,14 @@ Nota sobre `Inception Labs`:
|
|
|
126
133
|
- esta primera integracion expone solo `mercury-2`, que es el modelo chat-compatible oficial en `v1/chat/completions`
|
|
127
134
|
- `mercury-2` se trata como modelo solo texto en Claude Connect; si envias una imagen, la app ahora corta la peticion con un mensaje claro
|
|
128
135
|
- Claude Connect aplica presupuesto preventivo de contexto para `mercury-2` usando ventana `128K` y salida maxima `16,384`
|
|
136
|
+
- Claude Connect tambien aplica una ventana deslizante local de `400,000` input tokens por minuto para reducir rechazos del upstream por rate limit
|
|
129
137
|
- `Mercury Edit 2` no se publica todavia en Claude Connect porque usa endpoints `fim/edit` que no encajan con Claude Code en esta arquitectura
|
|
130
138
|
- autenticacion soportada: `API key`
|
|
131
139
|
- referencias oficiales:
|
|
132
140
|
- https://docs.inceptionlabs.ai/get-started/get-started
|
|
133
141
|
- https://docs.inceptionlabs.ai/get-started/authentication
|
|
134
142
|
- https://docs.inceptionlabs.ai/get-started/models
|
|
143
|
+
- https://docs.inceptionlabs.ai/get-started/rate-limits
|
|
135
144
|
|
|
136
145
|
Nota sobre `DeepSeek`:
|
|
137
146
|
|
|
@@ -140,6 +149,14 @@ Nota sobre `DeepSeek`:
|
|
|
140
149
|
- https://api-docs.deepseek.com/quick_start/pricing/
|
|
141
150
|
- https://api-docs.deepseek.com/guides/reasoning_model
|
|
142
151
|
|
|
152
|
+
Nota sobre `Z.AI`:
|
|
153
|
+
|
|
154
|
+
- usa el endpoint Anthropic-compatible oficial `https://api.z.ai/api/anthropic`
|
|
155
|
+
- Claude Connect fija `API_TIMEOUT_MS=3000000`
|
|
156
|
+
- al activar un perfil de `Z.AI`, tambien mapea `ANTHROPIC_DEFAULT_HAIKU_MODEL`, `ANTHROPIC_DEFAULT_SONNET_MODEL` y `ANTHROPIC_DEFAULT_OPUS_MODEL` al modelo elegido para que Claude Code use `GLM` de forma consistente
|
|
157
|
+
- referencias oficiales:
|
|
158
|
+
- https://docs.z.ai/devpack/tool/claude
|
|
159
|
+
|
|
143
160
|
Nota sobre `Ollama`:
|
|
144
161
|
|
|
145
162
|
- la URL del servidor se define al crear la conexión
|
package/package.json
CHANGED
|
@@ -528,6 +528,75 @@ const seedProviders = [
|
|
|
528
528
|
}
|
|
529
529
|
]
|
|
530
530
|
},
|
|
531
|
+
{
|
|
532
|
+
id: 'zai',
|
|
533
|
+
name: 'Z.AI',
|
|
534
|
+
vendor: 'Zhipu AI',
|
|
535
|
+
description: 'GLM Coding Plan para Claude Code usando el endpoint Anthropic-compatible oficial de z.ai.',
|
|
536
|
+
docsUrl: 'https://docs.z.ai/devpack/tool/claude',
|
|
537
|
+
docsVerifiedAt: '2026-04-04',
|
|
538
|
+
baseUrl: 'https://api.z.ai/api/anthropic',
|
|
539
|
+
defaultModelId: 'glm-5.1',
|
|
540
|
+
defaultAuthMethodId: 'token',
|
|
541
|
+
defaultApiKeyEnvVar: 'ZAI_API_KEY',
|
|
542
|
+
models: [
|
|
543
|
+
{
|
|
544
|
+
id: 'glm-5.1',
|
|
545
|
+
name: 'GLM-5.1',
|
|
546
|
+
category: 'Coding',
|
|
547
|
+
contextWindow: 'Auto',
|
|
548
|
+
summary: 'Modelo recomendado de la documentacion oficial de z.ai para usuarios Max que quieren usar GLM-5.1 en Claude Code.',
|
|
549
|
+
upstreamModelId: 'glm-5.1',
|
|
550
|
+
transportMode: 'direct',
|
|
551
|
+
apiStyle: 'anthropic',
|
|
552
|
+
apiBaseUrl: 'https://api.z.ai/api/anthropic',
|
|
553
|
+
apiPath: '/v1/messages',
|
|
554
|
+
authEnvMode: 'auth_token',
|
|
555
|
+
sortOrder: 1,
|
|
556
|
+
isDefault: 1
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
id: 'glm-4.7',
|
|
560
|
+
name: 'GLM-4.7',
|
|
561
|
+
category: 'General',
|
|
562
|
+
contextWindow: 'Auto',
|
|
563
|
+
summary: 'Modelo default recomendado por z.ai para Opus y Sonnet dentro del GLM Coding Plan.',
|
|
564
|
+
upstreamModelId: 'glm-4.7',
|
|
565
|
+
transportMode: 'direct',
|
|
566
|
+
apiStyle: 'anthropic',
|
|
567
|
+
apiBaseUrl: 'https://api.z.ai/api/anthropic',
|
|
568
|
+
apiPath: '/v1/messages',
|
|
569
|
+
authEnvMode: 'auth_token',
|
|
570
|
+
sortOrder: 2,
|
|
571
|
+
isDefault: 0
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
id: 'glm-4.5-air',
|
|
575
|
+
name: 'GLM-4.5-Air',
|
|
576
|
+
category: 'Fast',
|
|
577
|
+
contextWindow: 'Auto',
|
|
578
|
+
summary: 'Modelo ligero recomendado por z.ai para la clase Haiku dentro del GLM Coding Plan.',
|
|
579
|
+
upstreamModelId: 'glm-4.5-air',
|
|
580
|
+
transportMode: 'direct',
|
|
581
|
+
apiStyle: 'anthropic',
|
|
582
|
+
apiBaseUrl: 'https://api.z.ai/api/anthropic',
|
|
583
|
+
apiPath: '/v1/messages',
|
|
584
|
+
authEnvMode: 'auth_token',
|
|
585
|
+
sortOrder: 3,
|
|
586
|
+
isDefault: 0
|
|
587
|
+
}
|
|
588
|
+
],
|
|
589
|
+
authMethods: [
|
|
590
|
+
{
|
|
591
|
+
id: 'token',
|
|
592
|
+
name: 'Token',
|
|
593
|
+
description: 'Conexion por API key contra el endpoint Anthropic-compatible oficial de z.ai.',
|
|
594
|
+
credentialKind: 'env_var',
|
|
595
|
+
sortOrder: 1,
|
|
596
|
+
isDefault: 1
|
|
597
|
+
}
|
|
598
|
+
]
|
|
599
|
+
},
|
|
531
600
|
{
|
|
532
601
|
id: 'ollama',
|
|
533
602
|
name: 'Ollama',
|
package/src/gateway/server.js
CHANGED
|
@@ -27,6 +27,7 @@ import { readSwitchState } from '../lib/claude-settings.js';
|
|
|
27
27
|
import { enforceModelTokenBudget } from '../lib/model-budget.js';
|
|
28
28
|
import { readOAuthToken, refreshOAuthToken } from '../lib/oauth.js';
|
|
29
29
|
import { readProfileFile } from '../lib/profile.js';
|
|
30
|
+
import { reserveProviderInputTokens } from '../lib/provider-rate-limit.js';
|
|
30
31
|
import { readManagedProviderTokenSecret, readManagedTokenSecret } from '../lib/secrets.js';
|
|
31
32
|
|
|
32
33
|
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
@@ -509,6 +510,10 @@ async function handleMessages(request, response) {
|
|
|
509
510
|
body: rawBody,
|
|
510
511
|
profile: context.profile
|
|
511
512
|
});
|
|
513
|
+
await reserveProviderInputTokens({
|
|
514
|
+
profile: context.profile,
|
|
515
|
+
inputTokens: estimateTokenCountFromAnthropicRequest(body)
|
|
516
|
+
});
|
|
512
517
|
|
|
513
518
|
if (context.upstreamApiStyle === 'anthropic') {
|
|
514
519
|
const upstreamResponse = await forwardAnthropicMessage({
|
|
@@ -154,6 +154,13 @@ export async function resolveClaudeTransportForProfile({
|
|
|
154
154
|
extraEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL = profile.model.id;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
if (profile.provider.id === 'zai') {
|
|
158
|
+
extraEnv.API_TIMEOUT_MS = '3000000';
|
|
159
|
+
extraEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL = profile.model.id;
|
|
160
|
+
extraEnv.ANTHROPIC_DEFAULT_SONNET_MODEL = profile.model.id;
|
|
161
|
+
extraEnv.ANTHROPIC_DEFAULT_OPUS_MODEL = profile.model.id;
|
|
162
|
+
}
|
|
163
|
+
|
|
157
164
|
if (profile.provider.id === 'kimi') {
|
|
158
165
|
extraEnv.ENABLE_TOOL_SEARCH = 'false';
|
|
159
166
|
}
|
|
@@ -220,6 +227,8 @@ export function buildClaudeSettingsForProfile({
|
|
|
220
227
|
delete env.ENABLE_TOOL_SEARCH;
|
|
221
228
|
delete env.ANTHROPIC_MODEL;
|
|
222
229
|
delete env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
230
|
+
delete env.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
231
|
+
delete env.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
223
232
|
|
|
224
233
|
Object.assign(env, extraEnv);
|
|
225
234
|
|
|
@@ -259,9 +268,16 @@ export async function activateClaudeProfile({ profile, gatewayBaseUrl = 'http://
|
|
|
259
268
|
const currentAccount = await readClaudeAccount();
|
|
260
269
|
const currentCredentials = await readJsonIfExists(claudeCredentialsPath);
|
|
261
270
|
const currentState = await readSwitchState();
|
|
262
|
-
const
|
|
263
|
-
const
|
|
264
|
-
|
|
271
|
+
const canReuseActiveSnapshot = currentState?.active === true;
|
|
272
|
+
const originalSettings = canReuseActiveSnapshot
|
|
273
|
+
? currentState?.originalSettings ?? currentSettings
|
|
274
|
+
: currentSettings;
|
|
275
|
+
const originalAccount = canReuseActiveSnapshot
|
|
276
|
+
? currentState?.originalAccount ?? currentAccount
|
|
277
|
+
: currentAccount;
|
|
278
|
+
const originalCredentials = canReuseActiveSnapshot
|
|
279
|
+
&& currentState
|
|
280
|
+
&& Object.prototype.hasOwnProperty.call(currentState, 'originalCredentials')
|
|
265
281
|
? currentState.originalCredentials
|
|
266
282
|
: currentCredentials;
|
|
267
283
|
const transport = await resolveClaudeTransportForProfile({
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
function getProviderId(profile) {
|
|
2
|
+
return profile?.provider?.id ?? '';
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function getProviderInputTokensPerMinute(profile) {
|
|
6
|
+
const providerId = getProviderId(profile);
|
|
7
|
+
|
|
8
|
+
if (providerId === 'inception') {
|
|
9
|
+
return 400_000;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const recentReservations = [];
|
|
16
|
+
|
|
17
|
+
function pruneReservations(now) {
|
|
18
|
+
for (let index = recentReservations.length - 1; index >= 0; index -= 1) {
|
|
19
|
+
if (now - recentReservations[index].timestamp >= 60_000) {
|
|
20
|
+
recentReservations.splice(index, 1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function sumReservedTokens(profile, now) {
|
|
26
|
+
const providerId = getProviderId(profile);
|
|
27
|
+
|
|
28
|
+
return recentReservations.reduce((total, entry) => {
|
|
29
|
+
if (entry.providerId !== providerId) {
|
|
30
|
+
return total;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (now - entry.timestamp >= 60_000) {
|
|
34
|
+
return total;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return total + entry.tokens;
|
|
38
|
+
}, 0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function earliestExpiryForProvider(profile, now) {
|
|
42
|
+
const providerId = getProviderId(profile);
|
|
43
|
+
let minExpiry = null;
|
|
44
|
+
|
|
45
|
+
for (const entry of recentReservations) {
|
|
46
|
+
if (entry.providerId !== providerId) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const expiry = entry.timestamp + 60_000;
|
|
51
|
+
|
|
52
|
+
if (expiry <= now) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (minExpiry == null || expiry < minExpiry) {
|
|
57
|
+
minExpiry = expiry;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return minExpiry;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function resetProviderRateLimitState() {
|
|
65
|
+
recentReservations.length = 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function reserveProviderInputTokens({
|
|
69
|
+
profile,
|
|
70
|
+
inputTokens,
|
|
71
|
+
now = () => Date.now(),
|
|
72
|
+
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
73
|
+
}) {
|
|
74
|
+
const tokensPerMinute = getProviderInputTokensPerMinute(profile);
|
|
75
|
+
|
|
76
|
+
if (!tokensPerMinute) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (inputTokens > tokensPerMinute) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`${profile.provider.name} rechazo la solicitud porque la entrada estimada excede el limite de ${tokensPerMinute.toLocaleString('en-US')} tokens por minuto. Usa /compact o /clear antes de continuar.`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
while (true) {
|
|
87
|
+
const currentTime = now();
|
|
88
|
+
pruneReservations(currentTime);
|
|
89
|
+
const usedTokens = sumReservedTokens(profile, currentTime);
|
|
90
|
+
|
|
91
|
+
if (usedTokens + inputTokens <= tokensPerMinute) {
|
|
92
|
+
recentReservations.push({
|
|
93
|
+
providerId: getProviderId(profile),
|
|
94
|
+
tokens: inputTokens,
|
|
95
|
+
timestamp: currentTime
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const nextExpiry = earliestExpiryForProvider(profile, currentTime);
|
|
101
|
+
|
|
102
|
+
if (nextExpiry == null) {
|
|
103
|
+
throw new Error(`No se pudo reservar presupuesto de tokens para ${profile.provider.name}.`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const waitMs = Math.max(250, nextExpiry - currentTime + 25);
|
|
107
|
+
await sleep(waitMs);
|
|
108
|
+
}
|
|
109
|
+
}
|
package/src/lib/theme.js
CHANGED
|
@@ -17,10 +17,33 @@ export const colors = {
|
|
|
17
17
|
bold: '\x1b[1m'
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
export function rgb(r, g, b) {
|
|
21
|
+
return `\x1b[38;2;${r};${g};${b}m`;
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
export function colorize(text, ...tokens) {
|
|
21
25
|
return `${tokens.join('')}${text}${RESET}`;
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
export function gradientizeLines(lines, palette) {
|
|
29
|
+
if (!Array.isArray(lines) || lines.length === 0) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!Array.isArray(palette) || palette.length === 0) {
|
|
34
|
+
return [...lines];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (palette.length === 1) {
|
|
38
|
+
return lines.map((line) => colorize(line, palette[0], colors.bold));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return lines.map((line, index) => {
|
|
42
|
+
const paletteIndex = Math.round((index / Math.max(1, lines.length - 1)) * (palette.length - 1));
|
|
43
|
+
return colorize(line, palette[paletteIndex], colors.bold);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
24
47
|
export function stripAnsi(value) {
|
|
25
48
|
return value.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '');
|
|
26
49
|
}
|
package/src/wizard.js
CHANGED
|
@@ -34,7 +34,26 @@ import {
|
|
|
34
34
|
selectFromList,
|
|
35
35
|
waitForAnyKey
|
|
36
36
|
} from './lib/terminal.js';
|
|
37
|
-
import { colorize, colors } from './lib/theme.js';
|
|
37
|
+
import { colorize, colors, gradientizeLines, rgb } from './lib/theme.js';
|
|
38
|
+
|
|
39
|
+
function buildBrandWordmark() {
|
|
40
|
+
const wordmark = [
|
|
41
|
+
' ██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗',
|
|
42
|
+
' ██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝',
|
|
43
|
+
' ██║ ██║ ███████║██║ ██║██║ ██║█████╗ ',
|
|
44
|
+
' ██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝ ',
|
|
45
|
+
' ╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗',
|
|
46
|
+
' ╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝',
|
|
47
|
+
' C L A U D E · C O N N E C T'
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
return gradientizeLines(wordmark, [
|
|
51
|
+
rgb(103, 232, 249),
|
|
52
|
+
rgb(56, 189, 248),
|
|
53
|
+
rgb(59, 130, 246),
|
|
54
|
+
rgb(14, 165, 233)
|
|
55
|
+
]);
|
|
56
|
+
}
|
|
38
57
|
|
|
39
58
|
function isBack(value) {
|
|
40
59
|
return value === navigation.BACK;
|
|
@@ -229,6 +248,8 @@ function renderWelcome() {
|
|
|
229
248
|
title: 'Conecta Claude Code con otros modelos',
|
|
230
249
|
subtitle: 'Flujo guiado, catalogo SQLite y perfiles locales listos para reutilizar.',
|
|
231
250
|
body: [
|
|
251
|
+
...buildBrandWordmark(),
|
|
252
|
+
'',
|
|
232
253
|
colorize('Experiencia inicial', colors.bold, colors.accentSoft),
|
|
233
254
|
colorize('1. Elegir proveedor desde la base local', colors.soft),
|
|
234
255
|
colorize('2. Elegir modelo y tipo de conexion', colors.soft),
|
|
@@ -236,7 +257,7 @@ function renderWelcome() {
|
|
|
236
257
|
colorize('4. Guardar perfil y credenciales locales', colors.soft),
|
|
237
258
|
'',
|
|
238
259
|
colorize('Catalogo actual', colors.bold, colors.accentSoft),
|
|
239
|
-
colorize('OpenCode Go, Zen, Kimi, DeepSeek, Ollama, OpenAI, OpenRouter y Qwen ya vienen almacenados en SQLite.', colors.soft),
|
|
260
|
+
colorize('OpenCode Go, Zen, Kimi, DeepSeek, Z.AI, Ollama, OpenAI, Inception Labs, OpenRouter y Qwen ya vienen almacenados en SQLite.', colors.soft),
|
|
240
261
|
'',
|
|
241
262
|
colorize('Seguridad', colors.bold, colors.accentSoft),
|
|
242
263
|
colorize('El token OAuth se guarda localmente y el modo Token puede guardarse una sola vez por proveedor.', colors.soft)
|