English | Espanol
Plugin de nivel produccion para OpenCode con el proveedor Command Code.
Hace seis cosas:
- Descubrimiento dinamico de modelos - registra cada modelo de Command Code que el transporte compatible con OpenAI realmente puede servir, y etiqueta los tiers gratuitos o promocionales.
- Resolucion de credenciales - encuentra tu API key automaticamente, en cuatro almacenes distintos, y la inyecta solo para el origen real de Command Code.
- Estabilidad de sesion - repara los payloads que Command Code rechaza con HTTP 400, y aborta los streams SSE que se caen en silencio antes de que congelen la interfaz.
- Telemetria de cuota - herramienta
commandcode_quotay comando/commandcode-quotapara balance y ventanas de rate limit. - Credenciales resilientes - rota un pool de keys con cooldowns por key, para que una cuenta muerta no corte el turno.
- Fallos accionables - opt-in de zero data retention (
CMD_ZDR=1) y mensajes de error que dicen que hacer en lugar de repetir el texto del proveedor.
Note
Estado: desarrollo privado. El plugin esta verificado contra OpenCode v2.2.0 y el CLI de Command Code 1.88.0 sobre Bun 1.4.0. Todavia no esta publicado en ningun registro; se instala desde este repositorio.
Command Code expone alrededor de 90 modelos y rota los tiers gratuitos con
frecuencia, por lo que una lista escrita a mano en opencode.json queda obsoleta
sola. Al arrancar, el plugin consulta /provider/v1/models y registra el catalogo
mediante el editor de modelos de OpenCode v2:
- Filtrado por transporte. Solo se registran los modelos que anuncian
/chat/completions. La lineaclaude-*es de forma Anthropic (solo/messages) y respondeHTTP 400 unsupported_modelen el endpoint compatible con OpenAI, por eso se omite en lugar de ofrecerse como opcion rota. - Etiqueta automatica de promociones. Los tiers gratuitos se renombran con el
sufijo
[FREE PROMO]para que sean evidentes en el selector/model. La deteccion cubre ids:free,-free,_free,.freey las palabrasfree/promo/trialen el nombre visible. - Ventana de contexto real por modelo, y los modelos multimodales declaran entrada
image. - Funciona sin red. El catalogo se cachea en disco (TTL 6h). Si Command Code esta caido o no hay red, OpenCode arranca igual con el ultimo catalogo bueno en lugar de quedarse con el proveedor vacio.
Gana la primera coincidencia; todas las rutas son relativas al home (sin rutas de usuario hardcodeadas):
| Orden | Origen |
|---|---|
| 1 | Variable de entorno COMMAND_CODE_API_KEY |
| 2 | Override explicito de archivo COMMANDCODE_KEY_FILE |
| 3 | $XDG_CONFIG_HOME/commandcode/key.txt (por defecto ~/.config/commandcode/key.txt) |
| 4 | /etc/environment -> COMMAND_CODE_API_KEY |
| 5 | ~/.commandcode/auth.json (almacen del CLI oficial) |
| 6 | ~/.commandcode/credentials.json, luego ~/.commandcode/config.json |
La key se inyecta como Authorization: Bearer <key> solo cuando el hostname
parseado de la URL es exactamente api.commandcode.ai (o un host autorizado de
forma explicita con COMMANDCODE_ALLOWED_HOSTS). Nunca se usa coincidencia por
substring para credenciales: filtraria la key a hosts parecidos como
api.commandcode.ai.evil.com o a cualquier servidor que solo lleve
/provider/v1/chat/completions en su URL. En modo key unica una cabecera authorization existente
jamas se sobrescribe; en modo rotacion el plugin es dueno de ella (ver abajo).
Se pueden agrupar varias keys de Command Code para que una cuenta muerta no corte el turno. Dos o mas keys activan la rotacion; con una sola se mantiene el camino de key unica sin cambios.
# inline
export COMMANDCODE_API_KEYS="key_one,key_two"
# o archivo, una por linea (`label=key` o key pelada, comentarios con `#`)
export COMMANDCODE_KEYS_FILE="$HOME/.config/commandcode/keys.txt"Los cooldowns se registran por key segun la respuesta que culpa a la credencial:
402 (creditos insuficientes) y 401/403 bloquean 24h, 429 respeta
Retry-After hasta 6h (5m por defecto), y 5xx nunca culpa a la key. Si todas
estan en cooldown gana la que se recupera antes. El estado guarda solo etiquetas
(<state>/commandcode-keys-state.json, modo 0600) y cada cooldown se registra en
<state>/commandcode-rotation.log.
| <state>/commandcode-errors.log | Fallos decorados: status, codigo upstream, mensaje y la pista mostrada al usuario |
En modo rotacion el plugin es dueno de la cabecera Authorization para
api.commandcode.ai, porque la key que inyecto el host puede ser justo la que
devolvio 402. En modo key unica se mantiene la regla anterior: inyectar solo si
el request no trae cabecera.
Cuatro hooks independientes, todos fail-open (un error del plugin nunca puede romper un request):
- Higiene de conexion - OpenCode corre sobre Bun, que habla HTTP/1.1 con
keep-alive hacia Cloudflare. Cloudflare cierra sockets inactivos bajo carga y Bun
expone la reutilizacion muerta como
ECONNRESET. El hook aplicaConnection: closein-place en cada request a Command Code (verificado: Bun reenvia la cabecera y Cloudflare respondeconnection: close). Es un arreglo a nivel de request, no una garantia total; ver TROUBLESHOOTING. - Sanitizador de payload - repara el historial de mensajes que dispara
HTTP 400 invalid request error: respuestas de herramienta huerfanas o fuera de orden, llamadas desatendidas,tool_call_idduplicados o faltantes,function.argumentsno-string, contenido de herramientanullo vacio. - Watchdog de inactividad SSE - el primer chunk abre una ventana de 60s; si el stream queda mudo despues sin cerrarse, se aborta y se registra en lugar de congelar el turno. El tiempo previo al primer chunk queda a cargo del timeout propio de OpenCode, porque el modelo puede estar pensando legitimamente.
- Registro de reintentos - persiste los reintentos de transporte solo de Command Code, para poder diagnosticar una caida upstream a posteriori.
Command Code expone ZDR como cabecera de peticion (x-cmd-zdr: 1), el mismo
opt-in que su CLI lee de CMD_ZDR=1. El plugin lo replica:
CMD_ZDR=1- toda peticion aapi.commandcode.aillevax-cmd-zdr: 1.- En cualquier otro caso - se elimina cualquier
x-cmd-zdrenviada por el cliente, para que una sesion sin opt-in no pueda emitir la cabecera por error.
La cabecera solo se aplica en el host estricto de Command Code, nunca en un host
parecido que solo comparta /provider/v1/... en la ruta.
- Herramienta
commandcode_quotay comando/commandcode-quota: plan, estado, credito mensual restante, ventanas de rate limit de 5h y semanal con temporizador de reset, mas los totales del ciclo (peticiones, tokens, gasto).
Los fallos de Command Code conservan su status HTTP y su envelope JSON, pero el mensaje gana una pista de resolucion:
| Fallo | Pista agregada |
|---|---|
402 creditos insuficientes |
agrega creditos u otra cuenta a COMMANDCODE_API_KEYS |
MODEL_NOT_IN_PLAN |
elige otro modelo o sube de plan |
401 / 403 |
reemplaza la key o rota el pool |
429 / ventana del plan |
espera el reset o rota a otra key |
unsupported_model |
el modelo es Anthropic-shaped y solo se sirve en /messages |
Solo se tocan cuerpos JSON de error, el status nunca se reescribe (la clasificacion
de reintentos de OpenCode depende de el) y cada decoracion se registra en
<state>/commandcode-errors.log.
- OpenCode v2.0+ (el export
server()de v1 se mantiene como shim de compatibilidad). - Runtime Bun (el plugin se carga desde fuente; sin build y sin
npm install). - Una API key de Command Code en cualquiera de los almacenes listados arriba.
OpenCode clona, cachea y ejecuta el plugin desde el repositorio. Sin publicacion en npm y sin build: el codigo fuente es TypeScript ejecutado por Bun.
git clone git@github.com:JoshRob297/opencode-commandcode.git{
"plugins": [
"/ruta/absoluta/a/opencode-commandcode"
]
}OpenCode tambien auto-descubre todo directorio dentro de ~/.config/opencode/plugins/,
asi que un clon ahi se carga sin tocar opencode.json. La remocion no es simetrica:
borrar la entrada del config o renombrar el directorio no lo descarga; el directorio
tiene que salir de plugins/ por completo. Ver
TROUBLESHOOTING.
Reinicia OpenCode. En el siguiente arranque se descubre el catalogo de modelos, se registran la herramienta y el comando de barra, y se enganchan los hooks.
Important
No ejecutes este plugin junto a un segundo plugin que registre
commandcode_quota. Los ids de herramienta duplicados y los hooks http.request
en competencia provocan doble sanitizado de requests. Elimina la entrada del plugin
viejo durante el cutover.
El plugin no gestiona la definicion del proveedor. opencode.json sigue necesitando
el bloque del provider (la lista de modelos dentro es opcional; el descubrimiento los
agrega en runtime):
{
"providers": {
"commandcode": {
"name": "Command Code",
"package": "@opencode/ai/providers/openai-compatible",
"settings": {
"baseURL": "https://api.commandcode.ai/provider/v1",
"apiKey": "{file:~/.config/commandcode/key.txt}"
}
}
}
}El orden del selector lo maneja el plugin, no el catalogo:
- Primero los promo / free, con el badge
Freedel runtime. El plugin escribe una entrada decostcon input en cero para esos modelos, lo que otorga el badge y ademas reporta su costo real como cero. - Agrupados por familia (Claude, GPT, Gemini, DeepSeek, Kimi, GLM, MiniMax, Qwen, ...).
- La version mas nueva primero dentro de cada familia (
gpt-6.1-solantes quegpt-6-solantes quegpt-5.6-sol), derivado de la version en el id del modelo.
Se materializa en el time.released de cada modelo, asi que sobrevive a los refrescos del
catalogo y los modelos nuevos se ubican solos sin editar el plugin.
models.dev publica ademas su propio provider commandcode, que OpenCode mergea con el
configurado. Ese merge trae los claude-* de forma Anthropic, que responden
400 unsupported_model en el transporte compatible con OpenAI, por eso el plugin desactiva
todo modelo del provider que no forme parte de su catalogo curado.
Cada ajuste es una variable de entorno con default seguro. Referencia completa: docs/es/configuration.md.
| Variable | Default | Proposito |
|---|---|---|
COMMAND_CODE_API_KEY |
- | Resolucion de la key, maxima prioridad |
COMMANDCODE_KEY_FILE |
- | Ruta absoluta a un archivo con la key |
COMMANDCODE_API_KEYS |
- | Pool de keys; dos o mas activan rotacion |
COMMANDCODE_KEYS_FILE |
- | Archivo pool (label=key o key pelada por linea) |
CMD_ZDR |
- | 1 para zero data retention (x-cmd-zdr: 1) |
COMMANDCODE_ALLOWED_HOSTS |
- | Hosts extra separados por coma autorizados a recibir la key |
CC_NO_CONNECTION_CLOSE |
0 |
Poner en 1 para dejar de inyectar Connection: close |
COMMANDCODE_STATE_DIR |
$XDG_STATE_HOME/opencode |
Donde viven los logs y la cache de modelos |
COMMANDCODE_LIVE_TESTS |
0 |
Poner en 1 para correr la suite de tests con red |
COMMANDCODE_OUTPUT_TOKENS |
32768 |
Limite de tokens de salida declarado por modelo (OpenCode lo envia como max_completion_tokens). Subilo si un modelo soporta mas |
Kill switch en runtime para la higiene de conexion: crea un archivo centinela llamado
.disable-connection-close dentro del directorio del plugin.
| Archivo | Contenido |
|---|---|
<state>/commandcode-sanitizer.log |
Cada reparacion de payload en vuelo, con las correcciones aplicadas |
<state>/commandcode-watchdog.log |
Streams SSE abortados por inactividad |
<state>/commandcode-retries.log |
Reintentos de transporte de Command Code: sesion, agente, proveedor, modelo, tipo de error, status HTTP, numero de intento, delay del backoff y mensaje |
<state>/commandcode-rotation.log |
Cooldowns de keys: etiqueta, status HTTP, motivo y hora de reset |
<state>/commandcode-keys-state.json |
Estado de cooldown por etiqueta (solo etiquetas, 0600) |
<state>/commandcode-models-cache.json |
Ultimo catalogo de modelos bueno (TTL 6h) |
<state> por defecto es ~/.local/state/opencode.
El log de reintentos es un artefacto forense, no una metrica de salud: OpenCode ya
reintenta fallos de transporte transitorios de forma nativa (10 intentos, backoff de
2s a 10s), asi que una rafaga de entradas ECONNRESET normalmente significa una
caida upstream que se recupero de forma transparente. Ver
TROUBLESHOOTING.
bun install
bun test # suite offline, sin red, sin consumir cuota
bun run test:live # suite con red, consume una cantidad insignificante de cuota
bun run typecheck # tsc estricto, sin emitEstructura:
index.ts entrypoint del plugin (setup v2 + server v1)
src/auth/resolver.ts resolucion multi-fuente de credenciales
src/client/api.ts cliente HTTP, payloads de cuota, cache del catalogo
src/hooks/resilience.ts helpers puros: guardas de URL, sanitizador, watchdog, clasificadores
src/tools/quota.ts formateo del reporte de cuota
src/index.ts registro de hooks, catalogo, herramienta y comando
tests/ suites de bun testReglas de diseno que no se deben romper:
- Fail-open en todo: un error del plugin nunca debe fallar un request de modelo.
- Las credenciales solo se adjuntan con coincidencia exacta de host.
- La exactitud del catalogo vale mas que su tamano: no registres modelos que el transporte no puede servir.
- Sin rutas de usuario hardcodeadas.
Arquitectura y flujo de datos: docs/es/architecture.md.
MIT - ver LICENSE.