0.3.1 · · Release en GitHub
Kernl 0.3.1
Actualizar desde 0.3.0 o anterior
- Instalá esta versión a mano una vez. Los arreglos de abajo están en el
actualizador, y una copia instalada se actualiza con el actualizador que ya
tiene, que es justamente el que no puede. En Windows corré el MSI nuevo (o
descomprimilo encima de la copia portable), en macOS abrí el dmg nuevo, en
Linux volvé a correr
install.sh. Todas las actualizaciones siguientes se hacen desde el botón "Update now".
Agregado
- Reiniciá Kernl desde el dashboard (Settings → About). Lo relanza de la
misma forma en que se inició la instalación (start.bat, la .app, la unidad
de systemd, la política de reinicio del contenedor), así que una extensión
actualizada toma efecto sin cerrar una ventana de consola ni andar buscando
un acceso directo.
POST /api/update/restarthace lo mismo.
Corregido
Todas las páginas de extensiones salían en blanco en Windows.
/ext-assetsverificaba la contención constartsWith(dir + "/"), que nunca coincide con una ruta con barras invertidas, así que Cinema y todas las demás páginas de extensiones respondían 404. Ahora todas las jaulas de rutas del kernel, las extensiones y el dashboard comparan a través derelative(), sin distinguir mayúsculas en Windows, y el smoke test del release de Windows pide una página de extensión.Los subtítulos offline fallaban en una PC sin el runtime de Visual C++. whisper-cli y onnxruntime importan MSVCP140, VCRUNTIME140 y VCOMP140; los paquetes de Windows los llevan al lado de los binarios, y el release falla si faltan.
Cinema en Windows: los videos convertidos se reproducían en negro (
/api/cinema/media/convert/filerespondía 401), whisper abortaba con un nombre de perfil con tildes, una descarga de modelo interrumpida dejaba un modelo truncado al que se le confiaba para siempre, y una extracción fallida dejaba su WAV en la carpeta temporal.Los paquetes de extensiones no se resolvían para usuarios comunes de Windows. El link del payload era un symlink de directorio, que necesita permisos de administrador; ahora es una junction.
Claude Code en Windows. El CLI se encuentra sin
which; las tools del kernel llegan al chat y a los agentes por HTTP con el token de la API en vez de un socket Unix bajo/tmp; las reglas de permisos de los agentes usan la forma absoluta//path(una sola barra inicial ancla en el origen de la configuración y nunca coincidía en ninguna plataforma), y los agentes Docker usan el bridge stdio que realmente se distribuye.Tools y rutas solo POSIX en Windows: exec y búsqueda de repos, instalaciones de plugins desde zip, el workspace evolver, las copias remotas del filesystem-commander y su guarda que impide borrar una raíz, los escaneos de seguridad fuera de la carpeta de datos, el mensaje del certificado de IRC, el bloqueo de
data/dekernel_code_read,HOME, el frontmatter con CRLF,tartomado del PATH, y una ventana de consola cerrada que se salteaba el apagado limpio.El dashboard rechazaba rutas de Windows en Filesystem, Workspace, el registro de repos, Files y la página de licencia, y mostraba ⌘K en teclados sin tecla ⌘.
Las actualizaciones de extensiones toman un lock por extensión, actualizan las extensiones incluidas en la carpeta de datos en vez de la carpeta de instalación, sobreviven a una carpeta que Windows mantiene bloqueada, y ya no borran la copia distribuida al desinstalar.
Los agentes en sandbox sobre un stack con volúmenes con nombre escribían en un
/workspacevacío y perdían sus archivos con el contenedor. El workspace se monta desde el volumen de datos, y el panel del agente muestra la carpeta de oficina en la que el agente corre de verdad.macOS nunca podía actualizarse desde la app. El bundle se buscaba dos carpetas arriba de
Contents/Resourcesy después se exigía que terminara en.app/Contents, cosa que nunca pasa, así que cada intento se rechazaba con "could not work out which directory to replace". El chequeo siguiente verificaba la firma de la app ya instalada en vez de la descargada, y rechazaba porque los releases todavía no están firmados. Ahora se verifica el bundle descargado: una firma rota se rechaza, y un build sin firmar se instala sacándole el atributo de cuarentena. Una app que corre traslocada desde Descargas, desde la imagen de disco o desde una carpeta en la que la cuenta no puede escribir lo avisa antes de descargar nada.Las instalaciones por paquete se actualizan desde la app. El .deb o .rpm se descarga, se verifica y se instala con el pedido de administrador del escritorio (pkexec), y después Kernl se reinicia por systemd, o se relanza si se había iniciado a mano. Sin pkexec, el mensaje es el comando de instalación exacto para el archivo ya descargado. Antes sugería
apt upgrade kernl, que no hace nada: ningún repositorio publica Kernl.Una actualización fallida dejaba Kernl sin correr. El kernel sale antes de que el helper cambie nada, y cada falla en el helper (una carpeta que no podía mover, msiexec fallando, un aviso de UAC rechazado) simplemente terminaba. Ahora cualquier salida vuelve a iniciar Kernl, y registra lo que pasó para el próximo arranque.
Una actualización que instalaba una versión que no arranca era definitiva. El helper conserva la copia anterior hasta que la versión nueva responde en
/api/health, y la vuelve a poner si no lo hace en cinco minutos.El dashboard nunca decía si una actualización había funcionado. Mostraba "restarting, this page will reconnect" y dejaba de escuchar; perder contacto a mitad de la descarga también se leía como éxito. Ahora espera a que Kernl vuelva y dice "now on vX", o qué salió mal.
Otras cuentas de Windows no podían actualizar una instalación MSI. La marca del MSI vivía en el HKCU del usuario que instaló, así que cualquier otra cuenta veía una copia descomprimida e intentaba mover Program Files. El instalador escribe una marca en HKLM con la carpeta de instalación, y una copia bajo Program Files nunca se intercambia.
El "éxito, requiere reinicio" (3010) de msiexec contaba como falla.
Instalar un rc y después su build final dejaba dos entradas en Agregar o quitar programas, porque las dos tienen la misma versión de MSI.
El helper de Windows confiaba en el
findy eltarque aparecieran primero en el PATH. El find de GNU de Git for Windows terminaba el loop de espera de inmediato, así que el intercambio corría con el kernel todavía vivo. Las herramientas del sistema se llaman desde System32 con la ruta completa, y los movimientos se reintentan mientras Windows mantiene bloqueado un archivo recién cerrado.Salir para actualizar se salteaba el apagado propio del kernel y dejaba whisper, ffmpeg y otros procesos hijos reteniendo archivos en la carpeta de instalación. Ahora se frenan primero, y el kernel cierra con su apagado normal.
Una copia portable de Linux podía borrar su propia base de datos al actualizarse. Si se iniciaba desde adentro de la carpeta extraída, Kernl guardaba
data/ahí, y la actualización reemplaza esa carpeta. El tarball trae un lanzadorkernlque guarda los datos en~/.local/share/kernl, y el actualizador se niega mientras haya datos dentro de la instalación.Cada actualización exitosa dejaba su descarga tirada, al lado de la instalación o en la carpeta temporal. El próximo arranque la borra.
Docker ofrecía un botón "Update now" que solo podía negarse. Ahora muestra el comando de rebuild.
Una copia portable podía informar la versión de un package.json que no tenía nada que ver. La búsqueda arrancaba tres carpetas arriba, que para un tarball bajo
~/appses la carpeta home.install.sh en macOS instalaba una segunda copia versionada en cada re-ejecución. Ahora instala
Kernl.app, igual que el dmg.La actualización desde la app no podía funcionar en Windows, y lo decía recién después de negarse. Una instalación se reconocía porque el código en ejecución estaba en un directorio
bin/, la forma que producestage-payload.shy que traen el bundle de macOS y el tarball de Linux. Perobuild-zip.shybuild-msi.shcopian los dos el kernel empaquetado a la RAÍZ del paquete, así que una instalación de Windows corre desdeC:\Program Files\Kernly se clasificaba como "not an installed copy". La única plataforma sin un gestor de paquetes detrás era la que nunca podía actualizarse.Una instalación MSI ahora la actualiza el instalador de Windows, no moviendo su directorio. Un intercambio bajo Program Files necesita permisos que este proceso no tiene, y aun donde funciona deja a Agregar o quitar programas anunciando una versión que ya no está en disco. La app descarga el MSI nuevo, lo verifica y se lo pasa a
msiexec /i /qb: un solo aviso de UAC, el progreso del propio instalador, y el registro y el desinstalador sincronizados. Una copia portable se sigue intercambiando, que es lo correcto para un directorio que no es de nadie más.El actualizador pedía nombres de assets que el release no publica. Los armaba a mano:
Kernl-…-windows-x64.zipcontra unkernl-…-windows-x64.zippublicado. GitHub resuelve los nombres de assets sin distinguir mayúsculas, así que la descarga sobrevivía; la búsqueda del checksum, una comparación exacta de strings contra SHA256SUMS, no, y la actualización fallaba después de transferir 311 MB. Ahora el nombre sale de la API de releases, así que lo que se verifica y lo que se descarga son el mismo string por construcción.El helper de Windows no podía dormir, así que nunca intercambiaba nada. Usaba
timeout, que sale de inmediato con "Input redirection is not supported" cuando stdin está redirigido, y el helper se lanza con su stdio ignorado. El loop de espera quemaba sus sesenta intentos en milisegundos y se rendía, y para ese momento el kernel ya había salido: app cerrada, sin actualización y sin relanzar.Un primer movimiento fallido podía anidar la instalación nueva dentro de la vieja. El movimiento que aparta la copia anterior no se verificaba, y
movehacia un directorio existente mueve adentro de él, así que un movimiento denegado dejaba el árbol nuevo dentro de la instalación activa, informaba éxito, y borraba un backup que nunca se había hecho.El relanzamiento apuntaba a un directorio. Correcto para
opensobre un bundle de macOS; en Windows corríastart "" "C:\Program Files\Kernl", que abre el Explorador, y en Linux intentaba ejecutar una carpeta. Windows relanzastart.bat, el punto de entrada documentado que ya usa el acceso directo del menú Inicio, y la copia portable de Linux corre su runtime empaquetado contra su kernel empaquetado.La actualización desde la app en Linux devolvía 404. El tarball portable se construía y se subía como artefacto de CI, pero el release solo adjuntaba
Kernl-*-macos.tar.gz, así que el archivo que descarga el actualizador nunca se publicaba.Dos clics arrancaban dos actualizaciones. Nada protegía el camino de aplicación, así que un segundo clic (o una segunda pestaña, o un reintento después de un timeout) empezaba otra descarga, escribía otro helper y armaba otro timer de salida, con los dos helpers compitiendo por mover el mismo directorio. El segundo encontraba la instalación ya desaparecida.
Una copia portable en otra unidad nunca se podía actualizar. La descarga se preparaba en el directorio temporal del sistema, y
moveen Windows no puede mover un directorio entre volúmenes: con la app en D: y TEMP en C:, el helper esperaba a que el kernel saliera y después fallaba en el único paso que importa. Ahora la preparación se hace al lado de la instalación, lo que en Linux y macOS además convierte una copia entre dispositivos de varios cientos de megas en un rename.Un MSI instalado fuera de Program Files se intercambiaba en vez de actualizarse.
INSTALLDIRlo puede cambiar el usuario, así que la pregunta "¿esta es una instalación de un instalador?" no se puede responder por la ruta. Ahora lee el valor del registro queproduct.wxsya escribe, y solo cae a la ruta cuando no se puede leer para nada.Cada intento fallido dejaba la descarga tirada. Hasta trescientos megas por clic, en el directorio temporal, sin nada que alguna vez lo limpiara.
Verificar el checksum cargaba todo el archivo en memoria. El archivo se leía de una sola vez para hashearlo: un pico de 311 MB encima de la copia que ya estaba en disco. Ahora se hashea por partes.
Nada tenía timeout. Una conexión que se abría y después se quedaba callada dejaba la barra de progreso en "downloading" para siempre, sin botón de cancelar en ningún lado. Ahora una descarga que no recibe nada durante sesenta segundos se abandona y lo avisa, y las llamadas al host de releases se rinden a los quince.
Un momento sin conexión escondía un release durante seis horas. El chequeo cacheaba las fallas con la misma vida de seis horas que las respuestas, así que un solo fetch inestable dejaba la UI callada el resto del día, y una UI callada es justo cuando a nadie se le ocurre apretar "Check for updates". Ahora una falla se guarda un minuto, y un rate limit de GitHub cinco, y se informa como lo que es en vez de como "offline".
El chequeo de actualizaciones se identifica ante GitHub, que les pide eso a los llamadores y permite sesenta requests anónimos por hora por IP.
Agregado
- Un test que compara los nombres de assets que pide el actualizador contra los nombres que publicó un release real. El test anterior comparaba el nombre armado a mano contra sí mismo, lo que pasa por más mal que esté ese nombre, y es la razón por la que todo lo anterior salió publicado.
- Tests para el chequeo de actualizaciones en sí, que no tenía ninguno a pesar de aceptar un fetch inyectable justamente para eso: el orden de versiones incluida la regla de prereleases, y qué le puede hacer una falla a la caché.
Cambiado
docs/ARCHITECTURE-MAP.mdya no existe. Su contenido ahora abredocs/ARCHITECTURE.md, que es adonde siempre iba a ir: los dos archivos describían el mismo sistema a dos profundidades y cada uno empezaba remitiendo al otro. Un link a la ruta vieja ya no va a resolver.- Los manifiestos del dashboard y del escritorio llevan la versión del kernel
en vez de quedarse en 0.1.0 mientras el kernel llegaba a 0.3.0, y
scripts/release.shlos sube junto con él, así que un checkout ya no parece tres proyectos de tres edades distintas.