Skip to content

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

Open
sosan wants to merge 61 commits into
egdev6:mainfrom
sosan:wsl-sessions
Open

sosan wants to merge 61 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 19 commits October 31, 2026 18:30
… i18n) en feat/wsl-adapter

El main avanzó con la corrección de colocación X11 (egdev6#221), la notarización
resumible de macOS (egdev6#219) y el proveedor Antigravity (egdev6#207). Se integra
sobre la rama del adaptador WSL; docs/i18n-inventory.md se regenera desde
el árbol fusionado.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Revisión del mantenedor (2026-11-02), dos puntos:

1. Identidad del proceso. El pidfile guardaba solo el número de PID; entre
   leerlo y el primer kill, un PID reutilizado por otro proceso de la
   distro habría recibido TERM/KILL ajeno. Ahora la instancia publica un
   TOKEN (sufijo aleatorio del directorio) en `token` y el proceso final lo
   hereda como `QUORB_WSL_TOKEN` vía `exec`. Antes de señalar se comprueba
   que `/proc/<pid>/environ` contiene el token (fail-closed: identidad no
   verificada -> no se señala; recuperación explícita del nivel 2). Con
   regresión: identidad cambiada sin que ninguna sonda observe el PID
   ausente -> no TERM, no KILL.

2. Rutas con semántica del host. `session_path` componía con `Path::join`,
   que en Windows introduce `\` en una ruta que viaja literal al `cat` de
   la distro (Linux): allí `\` no separa nada y se buscaría otro fichero.
   Ahora se compone con separador `/` (join_guest_path) y se rechazan con
   `InvalidArgument` las rutas explícitas o relativas que lleven `\`.
   Tests que fijan el argumento exacto en cualquier plataforma.

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

El puente del camino 1: un resolvedor que sabe DÓNDE vive la sesión de un
proveedor según el entorno (host Windows/macOS vs distro WSL) y CÓMO se
lee — con `read_file` del adaptador (`wsl cat` con cancelación acotada)
en WSL, o del disco local en host. Los proveedores no conocen WSL.

Contrato de sesiones (egdev6#91):
- Ruta explícita con prioridad; una inválida es error sin fallback.
- Sin credenciales en el entorno elegido, nunca se usa una cuenta de
  otro entorno silenciosamente.
- Errores tipados: Absent (necesita login), Unreadable, Invalid y Wsl
  (el fallo es del subsistema o la distro, no de la sesión) — la UI
  distingue "login" de "WSL roto".
- No se copian tokens: cada entorno lee en su casa.

Tests deterministas con MockWslAdapter: lectura en host y en distro,
ausente en cada entorno, y fallo del subsistema propagado como WslError.
80 tests del módulo wsl pasan.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Cablea el SessionResolver en la lectura de uso: en host se mantiene el
comportamiento de siempre (disco local, incluido el respaldo del Keychain
de macOS); en WSL la sesión de cada CLI (Claude, Codex, Gemini, Grok) se
resuelve y se lee DENTRO de la distro, con el home de su default user
resuelto por el adaptador (F7). El fichero de sesión por proveedor tiene
una sola fuente (credentials_file_name) para que host y WSL compongan la
misma ruta relativa.

Si el entorno configurado no se puede resolver (WSL sin instalar, distro
ausente), el orbe muestra un error explícito de WSL: nunca se cae a una
cuenta de otro entorno.

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

Revisión PR egdev6#220 (HIGH): el adaptador es síncrono y las lecturas de sesión
esperan a wsl.exe (hasta 20 s cada una); hacerlas en el executor async
congelaba el widget con varias cuentas. El resolvedor pasa a `Arc<dyn
WslAdapter>` y expone `read_async` (spawn_blocking); resolve_home también
va en hilo propio. El crate ya usaba este patrón (claude.rs, antigravity.rs).

El sello de sesiones (session_stamp) también observa el entorno: en WSL se
lee el contenido de los ficheros dentro de la distro (misma fuente que los
fetch: credentials_path > home del entorno) y se suma su huella, de modo
que un login dentro de la distro refresca el widget aunque el host no tenga
mtime accesible.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Dos errores que solo aparecieron al compilar el crate completo en CI (el
harness aislado no incluye lib.rs, que necesita Tauri):

- E0599: `resolve_home` es un método del trait WslAdapter, no inherente; con
  el adaptador en `Arc` el trait tenía que estar en scope para el auto-deref.
- E0277: al pasar `session_stamp` a async, Tauri exige que los commands async
  con `State` devuelvan Result (el futuro vive más que el borrow).

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
- wsl/real.rs y wsl/process.rs: doc comments sin línea vacía / sin
  indentación de lista (empty_line_after_doc_comments, doc_lazy_continuation).
- config.rs: el test de session_env inicializa el struct en vez de
  reasignar campos (field_reassign_with_default).

El resto de lints que reporta clippy son preexistentes de main y el CI del
proyecto no ejecuta clippy.

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

Revisión del mantenedor (2026-11-02) aplicada a la PR de egdev6#91:

- `SessionResolver::default_path` componía con `Path::join`, que en Windows
  introduce `\` en una ruta que viaja literal al `cat` de la distro (Linux,
  donde `\` no separa nada). Ahora usa `join_guest_path` (semántica POSIX)
  con test del argumento exacto en cualquier plataforma.

- `session_stamp` se consulta cada 15 s y cada consulta resolvía el home de
  la distro con dos procesos wsl.exe (`resolve_home`), manteniendo la VM de
  WSL potencialmente siempre activa. Nuevo `WslHomeCache` en AppState: el
  home se resuelve una vez por distro y se invalida solo al cambiar la
  distro configurada.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
# 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

This branch has not been deployed

No deployments
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