Skip to content

feat(wsl): sesiones de proveedores por el adaptador — issue #91 - #239

Merged
egdev6 merged 66 commits into
egdev6:mainfrom
sosan:wsl-sessions
Nov 6, 2026
Merged

egdev6 merged 66 commits into
egdev6:mainfrom
sosan:wsl-sessions

Conversation

@sosan

@sosan sosan commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

PR encadenada sobre #220 (ya en main). Implementa la resolución de sesiones de los proveedores por entorno (issue #91): en host se mantiene el comportamiento de siempre y en WSL cada CLI lee su sesión dentro de la distro, por el adaptador de #90.

Alcance

  • SessionResolver (wsl/session.rs): decide dónde está la sesión según el entorno y cómo se lee, sin que los proveedores conozcan WSL.
    • Explícita (credentials_path) > ruta por defecto del entorno.
    • Sin credenciales en el entorno elegido no se cae a una cuenta de otro entorno (host ↔ WSL, ni entre distros).
    • read_async corre la lectura en hilo propio (spawn_blocking): la espera a wsl.exe no bloquea el executor.
  • Integración de los cuatro proveedores CLI (claude, codex, gemini, grok) y de las cuentas adicionales: la carpeta de cada cuenta se compone sobre el home del entorno elegido. Mapeo de errores: Absent → NeedsLogin, fallo del subsistema → Error con el detalle de WSL.
  • Configuración: session_env (host/wsl) y wsl_distro en AppConfig; wsl sin distro se trata como host (la resolución nunca inventa una distro). credentials_file_name como fuente única de la ruta relativa por proveedor.
  • Cableado (lib.rs): session_resolver (async) construye el resolvedor del entorno; fetch_usage, check_provider y session_stamp lo usan. Si el entorno configurado no se puede resolver (WSL sin instalar, distro ausente), el orbe muestra un error explícito de WSL.
  • Caché del home por distro (WslHomeCache en AppState): resolve_home lanza dos procesos wsl.exe; con session_stamp cada 15 s, el home se resuelve una vez por distro y se invalida solo al cambiar la distro configurada.
  • Rutas POSIX: SessionResolver::default_path compone con join_guest_path (siempre /), no con Path::join, que en Windows inserta \ en una ruta que viaja literal al cat de la distro. Tests del argumento exacto en cualquier plataforma.
  • Sello de sesiones en WSL: session_stamp lee el contenido dentro de la distro (misma fuente de rutas que los fetch) y suma su huella, de modo que un login dentro de la distro refresca el widget aunque el host no tenga mtime accesible.

Tests

  • Unit tests del crate en CI (Windows, Linux y macOS): incluye los nuevos de wsl/session.rs (host/WSL/read_async/POSIX), session_stamp en WSL, y config::session_env.
  • Harness aislado local: 164/164 (módulos reales wsl, config, model, cli_process y los cuatro proveedores con el mod.rs real).
  • cargo fmt --check limpio, inventario i18n regenerado (--check OK).

Evidencias

  • CI (Windows) sobre 47446da: run 41741077834 — success.
  • W3 lab sobre 47446da: run 41741073716 — GAP1, GAP1B, GAP2, GAP3 PASS.
  • Linux y macOS se validan automáticamente al abrir esta PR.

Nota (follow-up conocido)

En la señalización de procesos queda una ventana mínima entre el grep de identidad y el kill dentro del mismo shell; cerrarla del todo requeriría pidfd_open/pidfd_send_signal, que el shell no expone de forma portable. El diseño es fail-closed: si el PID cambió de dueño, el grep falla y no se señala nada (cubierto por regresión determinista en #220).

Closes #91

🤖 Generated with Claude Code

sosan and others added 30 commits October 24, 2026 21:59
El workflow w3-evidence.yml vive en w2-fase-a; GitHub solo ofrece para
dispatch los workflows presentes en la rama por defecto del repo. Se
añade aquí (mismo contenido, sin cambios) para que aparezca en el
desplegable de "Run workflow" del fork y se pueda lanzar con
ref=w2-fase-a.

Co-Authored-By: Claude Code <noreply@anthropic.com>
El run falló en el primer paso: `windows-latest` es ahora la imagen
windows-2025-vs2026, sin WSL instalado. Las imágenes con WSL
preinstalado son windows-2022 y windows-2019 (el lab W0 corrió en la que
entonces era latest). Se fija windows-2022 y se añade una comprobación
explícita de WSL al inicio, para que su ausencia se reporte con un
mensaje claro en vez de un exit code opaco.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: [CC] <noreply@anthropic.com>
…ev6#90

Trasplanta a main el contrato `WslAdapter` y su implementación real contra
`wsl.exe`, junto con el lab W3 que lo acredita con WSL real:

- src-tauri/src/wsl/: Fase A (mod.rs contrato + mock determinista,
  errors.rs) y Fase B (real.rs RealWslAdapter, process.rs lanzador con
  cancelación acotada nivel 1). Solo depende de cli_process + std.
- lib.rs: expone `pub mod wsl`.
- src-tauri/examples/wsl-wslenv-probe.rs: harness que mide WSLENV por el
  lanzamiento real del adaptador (run_command -> env_with -> ProcessRunner
  con env_clear().envs()); no es un binario del producto.
- .github/workflows/w3-evidence.yml + scripts/wsl-f14-probe.rs: el lab de
  evidencia — Gap 1 (pidfile endurecido, kill acotado, testigo ajeno),
  Gap 1b (timeout del host), Gap 2 (F14 doble frontera por la vía real)
  y Gap 3 (WSLENV por el adaptador real, allowlist x ramas + no-duplicar).

El lab ejercita el código real y en WSL real destapó y corrigió un bug de
shell_strategy (exec no ejecuta builtins -> SCRIPT_COMMAND_V). Incluye el
Caso I estricto (debe escalar a KILL) y el contrato nonpath=ausente.

Co-Authored-By: Claude Code <noreply@anthropic.com>
sosan and others added 6 commits November 3, 2026 17:05
# Conflicts:
#	docs/i18n-inventory.md
…6#90)

Bloqueo de la revisión del mantenedor (2026-11-03): la identidad se
comprobaba antes del TERM, pero durante la espera solo se consultaba
`kill -0` y el KILL final se enviaba sin volver a comprobar. Si el proceso
original moría y su PID se reutilizaba entre sondas, el nuevo dueño
recibía el KILL.

La comprobación y el envío van ahora en UNA sola invocación
(`SCRIPT_SIGNAL_IF_IDENTITY`: `tr … | grep -qxF … && kill -<señal> <pid>`),
también para la escalada: no hay proceso de verificación separado que deje
ventana entre verificar y señalizar, y si el PID fue reutilizado el grep
falla y no se envía nada (fail-closed).

Regresión determinista añadida: identidad correcta → TERM → el original
muere y el PID se reutiliza entre sondas (todas las `kill -0` ven vivo) →
la escalada re-verifica y el proceso nuevo NO recibe KILL.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
# Conflicts:
#	docs/i18n-inventory.md
# Conflicts:
#	docs/i18n-inventory.md
#	src-tauri/src/wsl/mod.rs
#	src-tauri/src/wsl/process.rs
#	src-tauri/src/wsl/real.rs
@egdev6

egdev6 commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Gracias por la PR, @sosan. El diseño está bien pensado y se nota el trabajo: separar el resolvedor de los proveedores, no caer a otro entorno y sacar la lectura del executor son decisiones correctas. Los cinco checks pasan. Aun así, antes de integrar hay dos defectos de rutas que conviene resolver.

1. Las cuentas adicionales quedan ilegibles en WSL. En src-tauri/src/providers/mod.rs:271 y :513 la ruta se compone con a.session_dir(Path::new("")).join(credentials_file_name(..)). En Windows, Path::join inserta \, y join_guest_path solo normaliza la frontera base/relativa, no el interior. El invitado recibe algo como /home/u/.quorb\accounts\claude-1/.credentials.json, el cat falla y la cuenta queda en «Sin sesión» de forma permanente, sin ningún error visible. Hay un caso relacionado: si config_dir es una ruta absoluta (C:\… o /mnt/c/…), join_guest_path le quita la / inicial y la re-enraiza bajo el home, ignorando en silencio la carpeta que eligió el usuario. La composición debería usar / también para esa ruta y rechazar explícitamente lo que no sea POSIX, en lugar de producir una ruta inválida.

2. La ruta explícita no se valida como ruta del invitado. wsl/session.rs:119 devuelve PathBuf::from(explicit) sin pasar por la validación que ya existe en wsl::session_path. El problema es concreto porque Ajustes sigue indicando pegar la ruta UNC (src/Settings.tsx:1891: \\wsl.localhost\Ubuntu\home\…). Ese valor viaja literal al cat, devuelve «No such file» y se presenta como «Sin sesión», cuando en realidad es un error de configuración. Lo correcto es validar con session_path y mapear InvalidArgument a un error explícito.

Los dos son solo reproducibles en Windows en runtime, y por eso los tests de Linux y macOS no los ven. El test default_path_produces_posix_arguments_for_the_guest compara el PathBuf compuesto y nunca mira el argumento real que recibe el invitado, así que no cubre ninguno de los dos. Harían falta regresiones del WslCall exacto para una cuenta adicional en fetch_account y en wsl_session_stamp, y para la ruta explícita con forma de host.

Puntos menores, sin bloquear:

  • session_env = wsl con distro vacía se trata como host (config.rs:615), que es justo el cruce de entornos que el módulo dice evitar. Además sessionEnv y wslDistro no existen en el frontend, así que la función no se puede activar desde Ajustes: solo editando el JSON.
  • wsl_session_stamp lanza un wsl.exe cat por proveedor y cuenta, en serie, cada 15 segundos, y descarta los errores: la caché resolvió el home, pero no este costo. Un fallo de distro y «no hay sesiones» acaban produciendo la misma huella.
  • session_stamp devuelve 0 ante un fallo de WSL (lib.rs:787), con el comentario de que es intencional; conviene confirmar que es el comportamiento deseado.

Lo que verifiqué correcto: con session_env = host el comportamiento es el mismo de antes, incluido el mapeo de errores y el fallback de Keychain; no hay ninguna vía que lea credenciales del host cuando el entorno es WSL; resolve_home y las lecturas van por spawn_blocking y no se retiene ningún lock a través de un await.

Esta revisión fue estática, y el contraste de evidencia fue la CI del head. No ejecuté el código de la PR ni reproduje los dos defectos en una distro real.

sosan and others added 6 commits November 4, 2026 21:06
…cita (egdev6#91)

Revisión del mantenedor (2026-11-04), dos defectos de rutas solo
reproducibles en Windows:

1. Cuentas adicionales ilegibles en WSL. La ruta se componía con
   `PathBuf::join`, que en Windows inserta `\` en el interior, y
   `join_guest_path` solo normalizaba la frontera: el guest recibía
   `/home/u/.quorb\accounts\…` y la cuenta quedaba «Sin sesión» sin error
   visible. Ahora `account_session_path` compone con POSIX en todo el
   recorrido; un `config_dir` absoluto del guest se usa tal cual (sin
   re-enraizar) y lo que no sea POSIX (separadores de Windows o rutas
   relativas) se rechaza con un error de configuración accionable, que en
   `fetch_account` se convierte en el report de la cuenta sin tocar la red.
   Regresión del WslCall exacto en `wsl_session_stamp`.

2. La ruta explícita no se validaba como ruta del guest. `resolve` devolvía
   `PathBuf::from(explicit)` tal cual: la UNC que sugiere Ajustes
   (`\\wsl.localhost\…`) viajaba literal al `cat`, fallaba como «no existe»
   y se mostraba «Sin sesión» ocultando un error de configuración. `resolve`
   devuelve ahora `Result` y en WSL valida la ruta (POSIX absoluta del
   guest); los proveedores mapean `Invalid` a un error explícito y el sello
   omite la ruta inválida.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…gdev6#91)

El CI de Windows confirmó el defecto de la revisión (2026-11-04): el
`Path::join` interno inserta `\` también entre componentes
(`.quorb\.credentials.json`), no solo en la frontera. Los proveedores
componían sus rutas por defecto con `Path::new(".claude").join(...)`.

Nuevo `guest_relative` (una sola fuente): compone la relativa del guest con
`/` en cualquier plataforma, sin `Path::join`. Se usa en los cuatro
proveedores, en la composición de cuentas adicionales (`fetch_account` y
`wsl_session_stamp`) y en los tests. Regresión que fija el string POSIX
exacto y el `WslCall` de la cuenta adicional.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Review rust (2026-11-04):

- HIGH: `account_session_path` cortaba también las cuentas de proveedores
  SIN login por CLI (openrouter, zai, nan, copilot…) en modo WSL, porque el
  lookup del fichero se evaluaba antes del match por proveedor. Ahora los
  proveedores sin sesión CLI devuelven `Ok(None)` y su fetch normal sigue
  usando su token.
- MEDIUM (documentado): sin `config_dir`, dos cuentas del mismo proveedor
  comparten `~/.quorb/<fichero>` (igual que en host); el aislamiento real
  lo da el config_dir de Ajustes. Test que fija el layout y el choque.
- Menores: `trim_end_matches('/')` en config_dir; `guest_relative` solo en
  tests (sin warning en el build no-test).

Co-Authored-By: Claude Code <noreply@anthropic.com>
…ogin CLI"

Co-Authored-By: Claude Code <noreply@anthropic.com>
@sosan

sosan commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Gracias por la revisión, @egdev6 — los dos defectos de rutas están corregidos y, además, tu hallazgo se reprodujo en la CI: el primer arreglo falló en Windows con left: "/home/user/.quorb\\.credentials.json", confirmando que el \ se colaba también en el INTERIOR de la ruta (no solo en la frontera que normaliza join_guest_path).

1. Cuentas adicionales en WSL.

  • Nueva fuente única guest_relative(parts) (wsl/mod.rs): compone la relativa del guest con / sin Path::join (en Windows insertaría \ entre componentes). Se usa en los cuatro proveedores, en la composición de cuentas adicionales y en los tests.
  • Nueva account_session_path(a, resolver) (providers/mod.rs), que sustituye al session_dir(...).join(...):
    • config_dir absoluto del guest (/mnt/c/…): se usa tal cual, sin re-enraizar bajo el home.
    • config_dir con separadores de Windows (\) o relativo: se rechaza explícitamente con mensaje accionable; en fetch_account se convierte en el Status::Error de la cuenta sin tocar el adaptador ni la red, y en wsl_session_stamp la ruta se omite.
    • Sin config_dir: ~/.quorb/<fichero> compuesta bajo el home del entorno, con POSIX.
  • Regresión del WslCall exacto del invitado: wsl_session_stamp_reads_extra_accounts_with_guest_posix_paths exige /home/user/.quorb/.credentials.json y que ninguna ruta del guest lleve separadores del host.

2. La ruta explícita se valida como ruta del invitado.

SessionResolver::resolve devuelve ahora Result<PathBuf, SessionError>: en WSL valida (guest_path_problem) y rechaza \ — incluida la UNC \\wsl.localhost\… que sugiere Ajustes — y las rutas relativas, con SessionError::Invalid y mensaje accionable («Pega la ruta del guest (…) en lugar de la ruta UNC»). Los cuatro proveedores lo mapean a Status::Error con el mensaje: se ve como error de configuración, no como «Sin sesión». En host la validación no se aplica (el fallback del Keychain de macOS queda intacto).

Review de la skill (adicional a la tuya), ya absorbida:

  • HIGH detectado y corregido: account_session_path truncaba también las cuentas de proveedores sin login CLI (openrouter, zai, nan, copilot…) en WSL; ahora devuelven Ok(None) y su fetch normal sigue usando su token (shellless_providers_are_not_truncated_in_wsl).
  • MEDIUM documentado con test: sin config_dir, dos cuentas del mismo proveedor comparten ~/.quorb/<fichero> — igual que en host; el aislamiento real lo da el config_dir que crea Ajustes. Test que fija el layout (default_extra_account_path_is_shared_without_config_dir).

Regresiones (harness 170/170 + los cinco checks de la PR en verde, Windows incluido): guest_relative_uses_posix_separators_on_every_platform, explicit_host_style_paths_are_rejected_in_wsl_mode, wsl_session_stamp_reads_extra_accounts_with_guest_posix_paths, account_path_is_posix_and_rejects_host_shaped_config_dirs.

Puntos menores, decididos:

  • session_env = wsl con distro vacía → sigue siendo host a propósito: la resolución nunca inventa una distro (lo documento; si prefieres un estado «sin configurar», lo cambio).
  • sessionEnv/wslDistro en Ajustes: sigue pendiente (UI, alcance aparte); hoy el modo se activa editando el JSON.
  • Coste del sello: el caché del home ya está; queda un cat por proveedor/cuenta en serie cada 15 s (pendiente de paralelización, anotado).
  • session_stamp → 0 ante fallo de WSL: intencional (sin sello no se refresca y el fallo se ve en el orbe); confirmado aquí.

Head actual: a3e02c8, CI del propio head en verde.

@egdev6

egdev6 commented Nov 5, 2026

Copy link
Copy Markdown
Owner

Gracias por los cambios, @sosan. Los dos defectos de rutas quedaron bien resueltos: resolve valida la ruta explícita en WSL y la UNC ya se muestra como error de configuración, guest_relative evita el \ interior en todas las plataformas y el rechazo de config_dir con forma de host es explícito. También es correcto el caso de los proveedores sin login CLI.

Queda un bloqueo, introducido por el arreglo: la carpeta por defecto de las cuentas adicionales en WSL no es la misma que en host.

  • Host (Account::session_dir, config.rs:259): home_root(home)/accounts/<provider>-<suffix>/<fichero>.
  • WSL (account_session_path): <home>/.quorb/<fichero>.

Hay dos consecuencias:

  1. Dos cuentas del mismo proveedor sin config_dir leen el mismo ~/.quorb/.credentials.json, mientras que en host cada una tiene su subcarpeta. Esto rompe el aislamiento que declara wsl/session.rs («resolver una no pisa los ficheros de otra»). El test default_extra_account_path_is_shared_without_config_dir fija justamente ese comportamiento, así que conviene invertirlo en lugar de conservarlo.
  2. .quorb está fijo en el código, mientras que host usa channel::current().home_dir, de modo que los canales con otro directorio divergen.

Propuesta: que WSL componga la misma ruta que host (home_dir del canal + accounts/<provider>-<suffix> + fichero) con guest_relative, y un test que verifique que dos cuentas obtienen rutas distintas.

Menores, sin bloquear:

  • La regla «absoluta del guest y sin \» ahora existe tres veces (wsl::session_path, guest_path_problem y account_session_path). Una sola fuente evitaría que diverjan.
  • wsl_session_stamp repite a mano las rutas de los proveedores principales (".claude/.credentials.json", etc.) en lugar de tomarlas de la misma fuente que los fetch.
  • fetch_account en WSL sigue sin una regresión directa del WslCall. La justificación del cliente reqwest es razonable, pero la cobertura es indirecta.

Las decisiones sobre los puntos menores anteriores (distro vacía tratada como host, UI de Ajustes pendiente, session_stamp en 0) me parecen bien como seguimiento.

Esta revisión fue estática sobre a3e02c8 y su CI; no ejecuté el código en una distro real.

@egdev6 egdev6 mentioned this pull request Nov 5, 2026
sosan and others added 3 commits November 6, 2026 00:58
…v6#91)

Revisión del mantenedor (2026-11-06):

- BLOQUEANTE resuelto: la carpeta por defecto de las cuentas adicionales
  en WSL es ahora la MISMA que en host — home_dir del canal +
  `accounts/<provider>-<suffix>` + fichero, con POSIX del guest. Dos
  cuentas del mismo proveedor sin config_dir ya no comparten
  `~/.quorb/<fichero>` (el aislamiento que declara wsl/session.rs) y los
  canales con otro home_dir no divergen. El test que fijaba el
  comportamiento compartido se invierte (`default_extra_accounts_are_
  isolated_without_config_dir`).
- Menor 1: la regla «absoluta del guest y sin \» vive en UNA sola fuente
  (`wsl::guest_path_problem`), usada por `SessionResolver::resolve`,
  `wsl::session_path` y `account_session_path`.
- Menor 2: las rutas de los proveedores principales salen de una única
  fuente (`provider_session_relative`) usada por los cuatro fetch y por
  `wsl_session_stamp` (ya no se repiten a mano).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…egdev6#91)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@egdev6 egdev6 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gracias, @sosan. Revisé el head 44d7a02 y el bloqueo de la ronda anterior está resuelto: las cuentas adicionales en WSL componen la misma ruta que host (home_dir del canal + accounts/<provider>-<suffix>), y el test ahora exige rutas distintas para dos cuentas. También quedaron en una sola fuente la regla POSIX (guest_path_problem) y las rutas de los proveedores (provider_session_relative).

Apruebo la PR. Antes de integrarla faltan dos cosas:

  1. Conflicto con main en docs/i18n-inventory.md. Es generado: basta con mergear main y regenerarlo con node scripts/i18n-inventory.mjs.
  2. CI sobre el head. Con el conflicto, los workflows no corrieron sobre los cambios de hoy; los últimos checks son de a3e02c8. Integro cuando los cinco estén en verde, en especial Windows, que es donde aparecían los defectos de rutas.

Los puntos menores pendientes (regresión directa del WslCall en fetch_account, UI de sessionEnv/wslDistro, costo del sello, distro vacía y el expect de provider_session_relative) quedan como seguimiento en #243.

Revisión estática sobre el diff y el código del head; no ejecuté la PR en una distro real.

egdev6
egdev6 previously approved these changes Nov 6, 2026
# Conflicts:
#	docs/i18n-inventory.md
@egdev6
egdev6 merged commit b82cf78 into egdev6:main Nov 6, 2026
5 checks passed
@sosan

sosan commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

Gracias por la revisión, @egdev6 — los dos defectos de rutas están corregidos y, además, tu hallazgo se reprodujo en la CI: el primer arreglo falló en Windows con left: "/home/user/.quorb\\.credentials.json", confirmando que el \ se colaba también en el INTERIOR de la ruta (no solo en la frontera que normaliza join_guest_path).

1. Cuentas adicionales en WSL.

  • Nueva fuente única guest_relative(parts) (wsl/mod.rs): compone la relativa del guest con / sin Path::join (en Windows insertaría \ entre componentes). Se usa en los cuatro proveedores, en la composición de cuentas adicionales y en los tests.
  • Nueva account_session_path(a, resolver) (providers/mod.rs), que sustituye al session_dir(...).join(...):
    • config_dir absoluto del guest (/mnt/c/…): se usa tal cual, sin re-enraizar bajo el home.
    • config_dir con separadores de Windows (\) o relativo: se rechaza explícitamente con mensaje accionable; en fetch_account se convierte en el Status::Error de la cuenta sin tocar el adaptador ni la red, y en wsl_session_stamp la ruta se omite.
    • Sin config_dir: ~/.quorb/<fichero> compuesta bajo el home del entorno, con POSIX.
  • Regresión del WslCall exacto del invitado: wsl_session_stamp_reads_extra_accounts_with_guest_posix_paths exige /home/user/.quorb/.credentials.json y que ninguna ruta del guest lleve separadores del host.

2. La ruta explícita se valida como ruta del invitado.

SessionResolver::resolve devuelve ahora Result<PathBuf, SessionError>: en WSL valida (guest_path_problem) y rechaza \ — incluida la UNC \\wsl.localhost\… que sugiere Ajustes — y las rutas relativas, con SessionError::Invalid y mensaje accionable («Pega la ruta del guest (…) en lugar de la ruta UNC»). Los cuatro proveedores lo mapean a Status::Error con el mensaje: se ve como error de configuración, no como «Sin sesión». En host la validación no se aplica (el fallback del Keychain de macOS queda intacto).

Review de la skill (adicional a la tuya), ya absorbida:

  • HIGH detectado y corregido: account_session_path truncaba también las cuentas de proveedores sin login CLI (openrouter, zai, nan, copilot…) en WSL; ahora devuelven Ok(None) y su fetch normal sigue usando su token (shellless_providers_are_not_truncated_in_wsl).
  • MEDIUM documentado con test: sin config_dir, dos cuentas del mismo proveedor comparten ~/.quorb/<fichero> — igual que en host; el aislamiento real lo da el config_dir que crea Ajustes. Test que fija el layout (default_extra_account_path_is_shared_without_config_dir).

Regresiones (harness 170/170 + los cinco checks de la PR en verde, Windows incluido): guest_relative_uses_posix_separators_on_every_platform, explicit_host_style_paths_are_rejected_in_wsl_mode, wsl_session_stamp_reads_extra_accounts_with_guest_posix_paths, account_path_is_posix_and_rejects_host_shaped_config_dirs.

Puntos menores, decididos:

  • session_env = wsl con distro vacía → sigue siendo host a propósito: la resolución nunca inventa una distro (lo documento; si prefieres un estado «sin configurar», lo cambio).
  • sessionEnv/wslDistro en Ajustes: sigue pendiente (UI, alcance aparte); hoy el modo se activa editando el JSON.
  • Coste del sello: el caché del home ya está; queda un cat por proveedor/cuenta en serie cada 15 s (pendiente de paralelización, anotado).
  • session_stamp → 0 ante fallo de WSL: intencional (sin sello no se refresca y el fallo se ve en el orbe); confirmado aquí.

Head actual: 7c9227b (incluye el merge de main con el inventario regenerado), CI del propio head en verde (cinco checks SUCCESS).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: resolver credenciales y sesiones CLI desde WSL

2 participants