claude-connect 0.1.6 → 0.1.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/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # Claude Connect
2
2
 
3
- > Conecta `Claude Code` con `OpenCode Go`, `Zen`, `Kimi`, `DeepSeek`, `Ollama`, `OpenAI`, `OpenRouter` y `Qwen` desde una interfaz de consola clara, rápida y reversible.
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.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/claude-connect?style=for-the-badge&logo=npm&color=cb3837)](https://www.npmjs.com/package/claude-connect)
6
6
  [![node](https://img.shields.io/badge/node-%3E%3D22-2f7d32?style=for-the-badge&logo=node.js&logoColor=white)](https://nodejs.org/)
7
7
  [![license](https://img.shields.io/badge/license-MIT-0f172a?style=for-the-badge)](./LICENSE)
8
- [![providers](https://img.shields.io/badge/providers-OpenCode%20Go%20%7C%20Zen%20%7C%20Kimi%20%7C%20DeepSeek%20%7C%20Ollama%20%7C%20OpenAI%20%7C%20OpenRouter%20%7C%20Qwen-0ea5e9?style=for-the-badge)](https://www.npmjs.com/package/claude-connect)
8
+ [![providers](https://img.shields.io/badge/providers-OpenCode%20Go%20%7C%20Zen%20%7C%20Kimi%20%7C%20DeepSeek%20%7C%20Ollama%20%7C%20OpenAI%20%7C%20Inception%20Labs%20%7C%20OpenRouter%20%7C%20Qwen-0ea5e9?style=for-the-badge)](https://www.npmjs.com/package/claude-connect)
9
9
 
10
10
  ## Why Claude Connect
11
11
 
@@ -13,7 +13,7 @@
13
13
 
14
14
  ### Highlights
15
15
 
16
- - `OpenCode Go`, `Zen`, `Kimi`, `DeepSeek`, `Ollama`, `OpenAI`, `OpenRouter` y `Qwen` listos desde el primer arranque
16
+ - `OpenCode Go`, `Zen`, `Kimi`, `DeepSeek`, `Ollama`, `OpenAI`, `Inception Labs`, `OpenRouter` y `Qwen` listos desde el primer arranque
17
17
  - soporte para `Token` y `OAuth` cuando el proveedor lo permite
18
18
  - API keys compartidas por proveedor para no repetir el mismo token en cada modelo
19
19
  - activación reversible sobre la instalación real de `Claude Code`
@@ -80,6 +80,7 @@ Al activar:
80
80
  - `DeepSeek` apunta a `https://api.deepseek.com/anthropic`
81
81
  - `Ollama` pide una URL local o remota, valida `/api/tags` y usa el gateway local sobre `.../api/chat`
82
82
  - `OpenAI` usa el gateway local sobre `https://api.openai.com/v1/chat/completions`
83
+ - `Inception Labs` usa el gateway local sobre `https://api.inceptionlabs.ai/v1/chat/completions`
83
84
  - `OpenRouter` usa `openrouter/free` por gateway sobre `https://openrouter.ai/api/v1`
84
85
  - `Qwen` apunta al gateway local `http://127.0.0.1:4310/anthropic`
85
86
 
@@ -93,6 +94,7 @@ Al activar:
93
94
  | `DeepSeek` | `deepseek-chat`, `deepseek-reasoner` | `Token` | Directa |
94
95
  | `Ollama` | modelos descubiertos desde tu servidor | `Servidor Ollama` | Gateway local |
95
96
  | `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 |
97
+ | `Inception Labs` | `mercury-2` | `Token` | Gateway local |
96
98
  | `OpenRouter` | `openrouter/free` | `Token` | Gateway local |
97
99
  | `Qwen` | `qwen3-coder-plus` | `OAuth`, `Token` | Gateway local |
98
100
 
@@ -118,6 +120,16 @@ Nota sobre `OpenAI`:
118
120
  - https://platform.openai.com/docs/api-reference/authentication
119
121
  - https://developers.openai.com/api/docs/models
120
122
 
123
+ Nota sobre `Inception Labs`:
124
+
125
+ - esta primera integracion expone solo `mercury-2`, que es el modelo chat-compatible oficial en `v1/chat/completions`
126
+ - `Mercury Edit 2` no se publica todavia en Claude Connect porque usa endpoints `fim/edit` que no encajan con Claude Code en esta arquitectura
127
+ - autenticacion soportada: `API key`
128
+ - referencias oficiales:
129
+ - https://docs.inceptionlabs.ai/get-started/get-started
130
+ - https://docs.inceptionlabs.ai/get-started/authentication
131
+ - https://docs.inceptionlabs.ai/get-started/models
132
+
121
133
  Nota sobre `Ollama`:
122
134
 
123
135
  - la URL del servidor se define al crear la conexión
@@ -151,14 +163,16 @@ Ahí viven:
151
163
  El catálogo SQLite local se genera automáticamente en:
152
164
 
153
165
  ```text
154
- storage/claude-connect.sqlite
166
+ Linux: ~/.claude-connect/storage/claude-connect.sqlite
167
+ Windows: %APPDATA%\claude-connect\storage\claude-connect.sqlite
155
168
  ```
156
169
 
157
170
  Importante:
158
171
 
159
172
  - esa base ya no se versiona en git
160
173
  - el catálogo se siembra desde `src/data/catalog-store.js`
161
- - esto evita conflictos molestos al hacer `git pull`
174
+ - ya no se crea en la carpeta donde ejecutas el comando
175
+ - esto evita conflictos molestos al hacer `git pull` y carpetas `storage/` accidentales en proyectos ajenos
162
176
 
163
177
  ## Claude Code Switching
164
178
 
@@ -175,6 +189,7 @@ Eso permite:
175
189
  - activar otro proveedor sin tocar archivos manualmente
176
190
  - evitar el `Auth conflict` entre sesión `claude.ai` y `API key`
177
191
  - volver a tu estado original con `Revertir Claude`
192
+ - bloquear la activación si `Claude Code` no está realmente instalado todavía
178
193
 
179
194
  ## Qwen OAuth
180
195
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-connect",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "CLI para configurar Claude Code con proveedores de modelos externos",
5
5
  "author": "wmcarlosv",
6
6
  "type": "module",
@@ -1,8 +1,14 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { DatabaseSync } from 'node:sqlite';
4
+ import { resolveClaudeConnectHomeSync } from '../lib/app-paths.js';
4
5
 
5
- export const defaultCatalogDbPath = path.join(process.cwd(), 'storage', 'claude-connect.sqlite');
6
+ export function getDefaultCatalogDbPath(options = {}) {
7
+ const pathModule = options.platform === 'win32' ? path.win32 : path.posix;
8
+ return pathModule.join(resolveClaudeConnectHomeSync(options), 'storage', 'claude-connect.sqlite');
9
+ }
10
+
11
+ export const defaultCatalogDbPath = getDefaultCatalogDbPath();
6
12
 
7
13
  const schemaSql = `
8
14
  PRAGMA foreign_keys = ON;
@@ -673,6 +679,45 @@ const seedProviders = [
673
679
  }
674
680
  ]
675
681
  },
682
+ {
683
+ id: 'inception',
684
+ name: 'Inception Labs',
685
+ vendor: 'Inception Labs',
686
+ description: 'Inception Platform con Mercury 2 sobre un endpoint OpenAI-compatible. Claude Code se conecta a traves del gateway local para mantener compatibilidad Anthropic y herramientas.',
687
+ docsUrl: 'https://docs.inceptionlabs.ai/get-started/get-started',
688
+ docsVerifiedAt: '2026-04-03',
689
+ baseUrl: 'https://api.inceptionlabs.ai/v1',
690
+ defaultModelId: 'mercury-2',
691
+ defaultAuthMethodId: 'token',
692
+ defaultApiKeyEnvVar: 'INCEPTION_API_KEY',
693
+ models: [
694
+ {
695
+ id: 'mercury-2',
696
+ name: 'Mercury 2',
697
+ category: 'OpenAI Chat Completions',
698
+ contextWindow: '128K',
699
+ summary: 'Modelo generalista y de razonamiento de Inception Labs expuesto por v1/chat/completions.',
700
+ upstreamModelId: 'mercury-2',
701
+ transportMode: 'gateway',
702
+ apiStyle: 'openai-chat',
703
+ apiBaseUrl: 'https://api.inceptionlabs.ai/v1',
704
+ apiPath: '/chat/completions',
705
+ authEnvMode: 'auth_token',
706
+ sortOrder: 1,
707
+ isDefault: 1
708
+ }
709
+ ],
710
+ authMethods: [
711
+ {
712
+ id: 'token',
713
+ name: 'Token',
714
+ description: 'Conexion por API key de Inception Labs.',
715
+ credentialKind: 'env_var',
716
+ sortOrder: 1,
717
+ isDefault: 1
718
+ }
719
+ ]
720
+ },
676
721
  {
677
722
  id: 'openrouter',
678
723
  name: 'OpenRouter',
@@ -1021,7 +1066,7 @@ function mapOAuthRow(row) {
1021
1066
  };
1022
1067
  }
1023
1068
 
1024
- export function createCatalogStore({ filename = defaultCatalogDbPath } = {}) {
1069
+ export function createCatalogStore({ filename = getDefaultCatalogDbPath() } = {}) {
1025
1070
  if (filename !== ':memory:') {
1026
1071
  fs.mkdirSync(path.dirname(filename), { recursive: true });
1027
1072
  }
@@ -1,4 +1,5 @@
1
1
  import fs from 'node:fs/promises';
2
+ import fsSync from 'node:fs';
2
3
  import os from 'node:os';
3
4
  import path from 'node:path';
4
5
  import process from 'node:process';
@@ -48,6 +49,14 @@ async function pathExists(targetPath) {
48
49
  }
49
50
  }
50
51
 
52
+ function pathExistsSync(targetPath) {
53
+ try {
54
+ return fsSync.existsSync(targetPath);
55
+ } catch (_error) {
56
+ return false;
57
+ }
58
+ }
59
+
51
60
  function defaultHomedir(env, fallbackHomedir) {
52
61
  const pathModule = getPathModule(process.platform);
53
62
 
@@ -179,6 +188,18 @@ export async function resolveClaudeConnectHome(options = {}) {
179
188
  return candidates[0];
180
189
  }
181
190
 
191
+ export function resolveClaudeConnectHomeSync(options = {}) {
192
+ const candidates = buildClaudeConnectHomeCandidates(options);
193
+
194
+ for (const candidate of candidates) {
195
+ if (pathExistsSync(candidate)) {
196
+ return candidate;
197
+ }
198
+ }
199
+
200
+ return candidates[0];
201
+ }
202
+
182
203
  export async function resolveClaudeSettingsPath(options = {}) {
183
204
  if (typeof options.env?.CLAUDE_SETTINGS_PATH === 'string' && options.env.CLAUDE_SETTINGS_PATH.trim().length > 0) {
184
205
  return buildClaudeSettingsPathCandidates(options)[0];
@@ -244,6 +265,8 @@ export async function resolveClaudeConnectPaths(options = {}) {
244
265
 
245
266
  return {
246
267
  claudeConnectHome,
268
+ storageDir: path.join(claudeConnectHome, 'storage'),
269
+ catalogDbPath: path.join(claudeConnectHome, 'storage', 'claude-connect.sqlite'),
247
270
  profilesDir: path.join(claudeConnectHome, 'profiles'),
248
271
  tokensDir: path.join(claudeConnectHome, 'tokens'),
249
272
  secretsDir: path.join(claudeConnectHome, 'secrets'),
@@ -269,3 +292,90 @@ export async function resolveClaudePaths(options = {}) {
269
292
  ...claudeConnectPaths
270
293
  };
271
294
  }
295
+
296
+ function buildExecutableNames(command, platform = process.platform, env = process.env) {
297
+ if (platform !== 'win32') {
298
+ return [command];
299
+ }
300
+
301
+ const pathext = typeof env.PATHEXT === 'string' && env.PATHEXT.length > 0
302
+ ? env.PATHEXT.split(';').filter(Boolean)
303
+ : ['.EXE', '.CMD', '.BAT', '.COM'];
304
+ const hasExt = path.win32.extname(command).length > 0;
305
+
306
+ if (hasExt) {
307
+ return [command];
308
+ }
309
+
310
+ return pathext.map((ext) => `${command}${ext.toLowerCase()}`);
311
+ }
312
+
313
+ export async function findExecutableOnPath(command, {
314
+ platform = process.platform,
315
+ env = process.env
316
+ } = {}) {
317
+ const pathModule = getPathModule(platform);
318
+ const pathValue = typeof env.PATH === 'string' ? env.PATH : '';
319
+ const pathEntries = pathValue.split(path.delimiter).filter(Boolean);
320
+ const commandNames = buildExecutableNames(command, platform, env);
321
+
322
+ for (const directory of pathEntries) {
323
+ for (const commandName of commandNames) {
324
+ const candidate = pathModule.join(directory, commandName);
325
+
326
+ if (await pathExists(candidate)) {
327
+ return candidate;
328
+ }
329
+ }
330
+ }
331
+
332
+ return null;
333
+ }
334
+
335
+ export async function detectClaudeCodeInstallation(options = {}) {
336
+ const settingsCandidates = buildClaudeSettingsPathCandidates(options);
337
+ const accountCandidates = buildClaudeAccountPathCandidates(options);
338
+ const credentialsCandidates = buildClaudeCredentialsPathCandidates(options);
339
+ const executablePath = await findExecutableOnPath('claude', options);
340
+
341
+ const [existingSettingsPath, existingAccountPath, existingCredentialsPath] = await Promise.all([
342
+ (async () => {
343
+ for (const candidate of settingsCandidates) {
344
+ if (await pathExists(candidate)) {
345
+ return candidate;
346
+ }
347
+ }
348
+
349
+ return null;
350
+ })(),
351
+ (async () => {
352
+ for (const candidate of accountCandidates) {
353
+ if (await pathExists(candidate)) {
354
+ return candidate;
355
+ }
356
+ }
357
+
358
+ return null;
359
+ })(),
360
+ (async () => {
361
+ for (const candidate of credentialsCandidates) {
362
+ if (await pathExists(candidate)) {
363
+ return candidate;
364
+ }
365
+ }
366
+
367
+ return null;
368
+ })()
369
+ ]);
370
+
371
+ return {
372
+ isInstalled: Boolean(executablePath || existingSettingsPath || existingAccountPath || existingCredentialsPath),
373
+ executablePath,
374
+ existingSettingsPath,
375
+ existingAccountPath,
376
+ existingCredentialsPath,
377
+ settingsCandidates,
378
+ accountCandidates,
379
+ credentialsCandidates
380
+ };
381
+ }
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { resolveClaudePaths } from './app-paths.js';
3
+ import { detectClaudeCodeInstallation, resolveClaudePaths } from './app-paths.js';
4
4
  import { readManagedProviderTokenSecret, readManagedTokenSecret } from './secrets.js';
5
5
 
6
6
  function isObject(value) {
@@ -240,6 +240,14 @@ export function buildClaudeSettingsForProfile({
240
240
  }
241
241
 
242
242
  export async function activateClaudeProfile({ profile, gatewayBaseUrl = 'http://127.0.0.1:4310/anthropic' }) {
243
+ const installation = await detectClaudeCodeInstallation();
244
+
245
+ if (!installation.isInstalled) {
246
+ throw new Error(
247
+ 'Claude Code no parece estar instalado en esta maquina. Instala o ejecuta Claude Code primero y luego vuelve a activar la conexion.'
248
+ );
249
+ }
250
+
243
251
  const {
244
252
  claudeSettingsPath,
245
253
  claudeAccountPath,
package/src/wizard.js CHANGED
@@ -433,7 +433,22 @@ async function activateClaudeFromSavedProfile() {
433
433
  return profile;
434
434
  }
435
435
 
436
- const result = await activateClaudeProfile({ profile });
436
+ let result;
437
+
438
+ try {
439
+ result = await activateClaudeProfile({ profile });
440
+ } catch (error) {
441
+ renderInfoScreen({
442
+ title: 'No se pudo activar Claude',
443
+ subtitle: 'Claude Connect no pudo aplicar la conexion en Claude Code.',
444
+ lines: [
445
+ colorize(error instanceof Error ? error.message : String(error), colors.warning)
446
+ ],
447
+ footer: 'Presiona una tecla para volver'
448
+ });
449
+ return await waitForAnyKey();
450
+ }
451
+
437
452
  const gateway = result.connectionMode === 'gateway'
438
453
  ? await restartGatewayInBackground()
439
454
  : await stopGateway();