atuq §6.5.bis: la extensión foco — muestra el foco del sistema y no tiene con qué apagarlo
La mitad del navegador del §6.5, deliberadamente asimétrica: sólo lee. Pregunta `focus.state` (nuevo en puriy-costura, commit 38815b5f3) y pinta tres estados — `foco`, nada, y `?` cuando nadie escribió el estado. Ese tercero es el que importa: si «no sé» se redondeara a «apagado», la insignia afirmaría que no hay foco sin haberlo mirado. No hay verbo para apagarlo, y es la propiedad y no un pendiente: si el navegador pudiera levantar el foco, valdría lo que vale un bloqueador de extensión. En tawasuyu hay un test que lo fija; acá el guardián mide el EFECTO — tras una sesión entera, el fichero de estado quedó igual. `scripts/test-atuq-foco.py` corre los tres estados sobre el path de PRODUCCIÓN (`/etc/takana/focus`), no la escotilla de pruebas: una escotilla mide el código, no el contrato con la imagen. Verde sobre el artefacto vigente (atuq 5d1afc50, puriy-costura 0de6b4ca), y verificado rompiéndolo — con una extensión parcheada que pinta «foco» siempre (en una COPIA del artefacto, vía ATUQ_DIR), falla nombrando la insignia.
This commit is contained in:
@@ -491,7 +491,7 @@ La columna «quién más lo tiene» es lo que evita que nos contemos un cuento.
|
||||
| 6.2 | **Descargas direccionadas por contenido** | nadie | store CAS de takana, `tejido` | bajo |
|
||||
| 6.3 | **Archivo personal + RAG local** | Rewind/Recall (nube, Windows); SingleFile (guarda, no busca) | `khipu`, `rag-motor`, `willay-rag` | medio |
|
||||
| 6.4 | **Historial y perfil sobre `qullqa`** | nadie | `qullqa-core`, `qullqa-pozo` | medio |
|
||||
| 6.5 | **Foco por `cortafuegos`, no por extensión** | nadie (nadie es dueño del navegador *y* del sistema) | `cortafuegos`, `pacha` | bajo |
|
||||
| 6.5 | **Foco por `cortafuegos`, no por extensión** ✅ **cadena y navegador; falta quién lo aplica** | nadie (nadie es dueño del navegador *y* del sistema) | `cortafuegos`, `nftables` (nuevo en el corpus) | bajo |
|
||||
| 6.6 | **Medios por fuera del navegador** | extensiones sueltas; de fábrica no | `foreign-ytdlp`, `-platform`, `-dlna` | bajo |
|
||||
| 6.7 | **IA local en la barra lateral** | Chrome/Edge son nube; Zen no tiene | `rimay`, `iniy` | bajo |
|
||||
| 6.8 | **Proxy por contenedor** ✅ **v0.5** | nadie de fábrica | — (API de Firefox) | bajo |
|
||||
@@ -761,6 +761,35 @@ navegador:
|
||||
Mientras eso no exista, lo que `atuq` puede tener —y tiene, §6.5.bis— es la mitad honesta: **ver** el
|
||||
estado del foco y **no poder apagarlo**.
|
||||
|
||||
#### 6.5.bis La mitad del navegador: `focus.state`, y la asimetría a propósito (2026-09-10)
|
||||
|
||||
La extensión `foco` **sólo lee**. Pregunta `focus.state` al host cada minuto —el estado lo cambia root
|
||||
por fuera, no hay evento al que suscribirse— y pinta tres cosas: `foco`, nada, o **`?`**. El tercer
|
||||
caso es el que importa: el host contesta `unknown` cuando nadie escribió el estado o cuando el
|
||||
contenido no se entiende, y **eso no se redondea a `off`**; una insignia que dijera «sin foco» sin
|
||||
haber mirado afirmaría un hecho que no tiene.
|
||||
|
||||
**Y no hay verbo para apagarlo.** `focus.stop` cae en «verbo desconocido», igual que `focus.off` y
|
||||
`focus.start`. No es una omisión pendiente: es la propiedad entera. Si el navegador pudiera levantar
|
||||
el foco, esto valdría exactamente lo que vale un bloqueador de extensión —dos clics— que es el
|
||||
problema que el §6.5 venía a resolver. Encenderlo tampoco está: aplicar un reglaset pide root y este
|
||||
proceso corre como el usuario. Hay un test en tawasuyu que lo fija por nombre, y se puso rojo a
|
||||
propósito agregando un `focus.stop` «por comodidad».
|
||||
|
||||
El estado vive en `/etc/takana/focus` (lo escribe root junto con el reglaset). `scripts/test-atuq-foco.py`
|
||||
corre **el path de producción, no la escotilla de pruebas** —una escotilla mide el código, no el
|
||||
contrato con la imagen— en tres sesiones del navegador, y en cada una comprueba tres cosas: qué dijo
|
||||
la extensión, qué pintó, y que el fichero **quedó igual** después de la sesión. Eso último es la
|
||||
promesa del §6.5 medida del lado del navegador. Verificado rompiéndolo: con una extensión parcheada
|
||||
que pinta «foco» pase lo que pase (dentro de una COPIA del artefacto, vía `ATUQ_DIR`), el guardián
|
||||
falla nombrando la insignia.
|
||||
|
||||
```
|
||||
-- fichero de foco: on → ESTADO on · INSIGNIA "foco" · quedó on
|
||||
-- fichero de foco: off → ESTADO off · INSIGNIA "" · quedó off
|
||||
-- fichero de foco: (no existe) → ESTADO unknown · INSIGNIA "?" · siguió ausente
|
||||
```
|
||||
|
||||
### 6.6 Medios fuera del navegador — HECHO (2026-09-10)
|
||||
|
||||
Una navegación de PRIMER NIVEL a un medio (`Content-Type: video/*` o `audio/*`) se cancela y la URL se
|
||||
|
||||
+27
-1
@@ -29,6 +29,7 @@ el vigente (por eso el ciclo es: editar todo → resellar una vez → medir), y
|
||||
| `extensions/archivo/` | `<appdir>/distribution/extensions/archivo@atuq.tawasuyu.xpi` | **Archivo personal** (SDD 26 §6.3). |
|
||||
| `extensions/medios/` | `<appdir>/distribution/extensions/medios@atuq.tawasuyu.xpi` | **Medios fuera del navegador** (SDD 26 §6.6). |
|
||||
| `extensions/torrent/` | `<appdir>/distribution/extensions/torrent@atuq.tawasuyu.xpi` | **Torrent con identidad** (SDD 26 §6.9). |
|
||||
| `extensions/foco/` | `<appdir>/distribution/extensions/foco@atuq.tawasuyu.xpi` | **Modo foco: lo muestra, no lo apaga** (SDD 26 §6.5). |
|
||||
| `native-messaging/*.json` | `/usr/lib/mozilla/native-messaging-hosts/` | El manifiesto del host nativo. **Fuera del appdir** — ver abajo. |
|
||||
| `bin/puriy-costura-host` | `<appdir>/puriy-costura-host` | El lanzador del host: le pone el `--state` que el manifiesto no puede. |
|
||||
|
||||
@@ -240,6 +241,32 @@ sin pestaña es el único resultado inaceptable, y el control negativo del guard
|
||||
⚠ El binario **no viene del mensaje**: es una constante del host. Lo único que viaja es la URL, sólo
|
||||
`http(s)`, y detrás de un `--`.
|
||||
|
||||
## `foco` — el navegador MUESTRA el foco, y no tiene con qué apagarlo (§6.5)
|
||||
|
||||
El foco no es un bloqueador de sitios: es una regla `nft` de **egress por cgroup** que aplicó root, y
|
||||
mientras está puesta el navegador **no sale a la red** — con todo lo local andando (el archivo del
|
||||
§6.3 se lee, el CAS del §6.2 está). Medido en una máquina con root: el proceso del cgroup en foco no
|
||||
abre la conexión y el del cgroup de al lado sí (`scripts/test-foco-egress.sh`).
|
||||
|
||||
Esta extensión es la mitad del navegador, y es **asimétrica a propósito: sólo lee**. Pregunta
|
||||
`focus.state` al host cada minuto —el estado lo cambia root por fuera, así que no hay evento al que
|
||||
suscribirse— y pinta tres cosas y ninguna más: `foco` si está puesto, nada si no, y **`?` si nadie
|
||||
escribió el estado**. Ese tercer caso es el que importa: si «no sé» se redondeara a «apagado», la
|
||||
insignia afirmaría que no hay foco sin haberlo mirado.
|
||||
|
||||
**No hay verbo para apagarlo, y eso es la propiedad y no un pendiente.** Si desde el navegador se
|
||||
pudiera levantar, el modo foco valdría lo que vale un bloqueador de extensión: dos clics. En tawasuyu
|
||||
hay un test que lo fija (`focus.stop` → «verbo desconocido»), y acá el guardián mide el efecto:
|
||||
después de una sesión entera del navegador, el fichero de estado quedó **igual**.
|
||||
|
||||
`scripts/test-atuq-foco.py` corre los tres estados y comprueba, por corrida, qué dijo la extensión,
|
||||
qué pintó y que el estado no cambió. Verificado rompiéndolo: con una extensión que pinta «foco» pase
|
||||
lo que pase, el guardián falla nombrando la insignia.
|
||||
|
||||
Lo que todavía NO existe —y no se inventó de paso— es quién pone a `atuq` en un cgroup y quién aplica
|
||||
la política: lo primero pide delegación de un subárbol a la sesión (decisión de arje, SDD 10) y lo
|
||||
segundo pide root. Ver el §6.5 del SDD 26.
|
||||
|
||||
## `torrent` — el magnet lo toma la suite (§6.9)
|
||||
|
||||
La extensión registra un handler para `magnet:` (`protocol_handlers`, que existe en nuestro build),
|
||||
@@ -324,7 +351,6 @@ pestañas están fijadas borra el wrapper y devuelve `null`. `--negative-control
|
||||
necesita el daemon de embeddings y un backend LLM en la imagen. `willay-rag` es el molde.
|
||||
- **La IA local en la barra lateral (6.7)**, que está bloqueada por algo medido: el corpus no tiene
|
||||
ninguna receta de LLM ni de embeddings. Es también la que traería el motor que le falta al archivo.
|
||||
- **El foco por `cortafuegos` (6.5)**, el último del §6 que no depende de un modelo ni de un daemon.
|
||||
- **Que una descarga se pueda COMPARTIR con `tejido`**: hoy el objeto está en el CAS de descargas y
|
||||
tejido sirve el del sistema. Falta que el índice de descargas sea una raíz que el GC respete, y ahí
|
||||
las dos raíces pueden volver a ser una.
|
||||
|
||||
@@ -55,6 +55,10 @@
|
||||
"torrent@atuq.tawasuyu": {
|
||||
"installation_mode": "normal_installed",
|
||||
"install_url": "file:///usr/lib/atuq/distribution/extensions/torrent@atuq.tawasuyu.xpi"
|
||||
},
|
||||
"foco@atuq.tawasuyu": {
|
||||
"installation_mode": "normal_installed",
|
||||
"install_url": "file:///usr/lib/atuq/distribution/extensions/foco@atuq.tawasuyu.xpi"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
"use strict";
|
||||
// foco — el navegador MUESTRA el modo foco; no lo decide y no lo puede apagar (SDD 26 §6.5), v1.
|
||||
//
|
||||
// ── QUÉ ES EL FOCO, Y POR QUÉ NO ES UN BLOQUEADOR DE SITIOS ───────────────────
|
||||
// El foco lo sostiene el cortafuegos del sistema: una regla `nft` de egress por
|
||||
// cgroup que aplicó root. Cuando está puesto, el navegador NO SALE A LA RED — y
|
||||
// todo lo local sigue: el archivo personal (§6.3) se lee, el CAS (§6.2) está ahí.
|
||||
// No es una lista de dominios: una lista se esquiva con un espejo, y un cgroup sin
|
||||
// egress no.
|
||||
//
|
||||
// ── POR QUÉ ESTA EXTENSIÓN SÓLO MIRA ──────────────────────────────────────────
|
||||
// Si desde acá se pudiera levantar el foco, valdría exactamente lo que vale un
|
||||
// bloqueador de extensión: lo apaga en dos clics la misma persona que no quería
|
||||
// distraerse. El host no expone ningún verbo para apagarlo —`focus.stop` contesta
|
||||
// «verbo desconocido», y hay un test que lo fija—, así que esta extensión no tiene
|
||||
// con qué, y eso es la propiedad. Encenderlo tampoco: aplicar un reglaset pide root.
|
||||
//
|
||||
// ── Y POR QUÉ «NO SÉ» ES UNA RESPUESTA ────────────────────────────────────────
|
||||
// Si nadie escribió el estado, el host contesta `unknown` y acá se muestra tal cual.
|
||||
// Pintar «sin foco» cuando no se miró sería afirmar un hecho que no se tiene.
|
||||
|
||||
const HOST = "puriy_costura";
|
||||
// Cada cuánto se vuelve a preguntar. El estado lo cambia root por fuera del
|
||||
// navegador, así que no hay evento al que suscribirse: se sondea, y despacio.
|
||||
const CADA_MS = 60000;
|
||||
|
||||
let puerto = null;
|
||||
let siguiente = 1;
|
||||
|
||||
function di(s) {
|
||||
try { dump("FOCO " + s + "\n"); } catch (e) {}
|
||||
}
|
||||
|
||||
// La insignia es TODA la interfaz: tres estados, tres textos, y ninguno inventado.
|
||||
function pintar(estado, fichero) {
|
||||
const texto = estado === "on" ? "foco" : estado === "unknown" ? "?" : "";
|
||||
const titulo =
|
||||
estado === "on"
|
||||
? "Modo foco: el sistema tiene al navegador sin salida a la red"
|
||||
: estado === "off"
|
||||
? "Modo foco apagado"
|
||||
: "Modo foco: no se sabe — nadie escribió el estado en " + (fichero || "el sistema");
|
||||
try {
|
||||
browser.browserAction.setBadgeText({ text: texto });
|
||||
browser.browserAction.setTitle({ title: titulo });
|
||||
} catch (e) {
|
||||
di("INSIGNIA falló " + e);
|
||||
}
|
||||
di('INSIGNIA texto="' + texto + '"');
|
||||
}
|
||||
|
||||
function conectar() {
|
||||
try {
|
||||
puerto = browser.runtime.connectNative(HOST);
|
||||
} catch (e) {
|
||||
di("SIN HOST " + e);
|
||||
return;
|
||||
}
|
||||
di("CONECTADO " + HOST);
|
||||
puerto.onMessage.addListener((r) => {
|
||||
if (!r || r.verb !== "focus.state") return;
|
||||
if (!r.ok) {
|
||||
di("ERROR " + (r.error || "sin causa"));
|
||||
return;
|
||||
}
|
||||
di("ESTADO " + r.focus + " fichero=" + (r.focus_file || "?"));
|
||||
pintar(r.focus, r.focus_file);
|
||||
});
|
||||
puerto.onDisconnect.addListener(() => {
|
||||
const e = puerto && puerto.error ? puerto.error.message : "sin causa";
|
||||
di("DESCONECTADO " + e);
|
||||
puerto = null;
|
||||
});
|
||||
}
|
||||
|
||||
function preguntar() {
|
||||
if (!puerto) conectar();
|
||||
if (!puerto) return;
|
||||
try {
|
||||
puerto.postMessage({ id: siguiente++, verb: "focus.state" });
|
||||
} catch (e) {
|
||||
di("ENVÍO falló " + e);
|
||||
puerto = null;
|
||||
}
|
||||
}
|
||||
|
||||
di("LISTA v1 muestra el foco del sistema; no lo apaga (no tiene con qué)");
|
||||
preguntar();
|
||||
setInterval(preguntar, CADA_MS);
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "atuq — modo foco",
|
||||
"description": "Muestra si el sistema tiene al navegador en modo foco. No lo apaga: no puede (SDD 26 §6.5).",
|
||||
"version": "1.0",
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "foco@atuq.tawasuyu",
|
||||
"strict_min_version": "115.0"
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"nativeMessaging"
|
||||
],
|
||||
"background": {
|
||||
"scripts": ["fondo.js"]
|
||||
},
|
||||
"browser_action": {
|
||||
"default_title": "Modo foco",
|
||||
"default_icon": "atuq128.png"
|
||||
},
|
||||
"icons": {
|
||||
"128": "atuq128.png"
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
"descargas@atuq.tawasuyu",
|
||||
"archivo@atuq.tawasuyu",
|
||||
"medios@atuq.tawasuyu",
|
||||
"torrent@atuq.tawasuyu"
|
||||
"torrent@atuq.tawasuyu",
|
||||
"foco@atuq.tawasuyu"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[source]
|
||||
repo = "gitea@git.tawasuyu.net:tawasuyu/tawasuyu.git"
|
||||
commit = "066b3761f7a7a18415b0a59cb671355953bb48a1"
|
||||
commit = "38815b5f3625d7d61dd4005195e3f822d11fc380"
|
||||
# ⚠ SIN ESTO EL BUILD MUERE EN UN SITIO QUE NO NOMBRA LA CAUSA — medido acá el 2026-09-10.
|
||||
# tawasuyu COMMITEA su propio `vendor/`: adentro tiene un `smithay` parcheado a mano (tearing/flip
|
||||
# async para el compositor), y lo enchufa con `[patch.crates-io] smithay = { path = "vendor/smithay" }`.
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python3
|
||||
"""¿El navegador MUESTRA el modo foco del sistema, y lo deja como estaba? (SDD 26 §6.5)
|
||||
|
||||
python3 scripts/test-atuq-foco.py
|
||||
|
||||
── QUÉ SE EJERCITA ───────────────────────────────────────────────────────────────────────────
|
||||
Tres corridas, una por estado del fichero que escribe root (`/etc/takana/focus`): `on`, `off` y
|
||||
**ausente**. En cada una se mide lo que la extensión DICE y lo que pinta en su insignia, leído del
|
||||
`dump` del navegador — no lo que el host contestó, que es la mitad que ya cubren los tests de
|
||||
`puriy-costura`.
|
||||
|
||||
── Y LA PROMESA DEL §6.5, MEDIDA DEL LADO DEL NAVEGADOR ──────────────────────────────────────
|
||||
Después de cada corrida se vuelve a leer el fichero DENTRO de la jaula: tiene que estar **igual**.
|
||||
El foco lo sostiene un reglaset `nft` que aplicó root, y la única forma de que eso valga algo es que
|
||||
el navegador no pueda deshacerlo. Que no exista un verbo para apagarlo está fijado en tawasuyu
|
||||
(`no_hay_verbo_para_apagar_el_foco_y_eso_es_la_propiedad`); acá se comprueba el efecto: el estado
|
||||
sobrevive intacto a una sesión entera del navegador.
|
||||
|
||||
`unknown` es el caso que más importa de los tres: si el host redondeara «no sé» a «apagado», la
|
||||
insignia diría «sin foco» sin que nadie lo haya mirado.
|
||||
|
||||
REQUISITOS: el rootfs mínimo de `atuq-nested.sh`, y `atuq` + `puriy-costura` sellados.
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
RFS = os.environ.get("RFS", "/mnt/cosecha/escritorios/atuq-rootfs")
|
||||
ALPINE = os.path.join(ROOT, ".dev-fs/alpine")
|
||||
HAMMER = os.path.join(ROOT, "target/release/takana")
|
||||
STORE = os.environ.get("HAMMER_STORE", os.path.join(ROOT, "store"))
|
||||
# El path de producción, el mismo que la constante del host. Se mide ÉSTE y no la escotilla de
|
||||
# pruebas: una escotilla mide el código, no el contrato con la imagen.
|
||||
FOCO = "/etc/takana/focus"
|
||||
|
||||
|
||||
def fatal(msg):
|
||||
print(f"✗ {msg}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def artefacto(receta, nombre, escotilla=None):
|
||||
forzado = os.environ.get(escotilla) if escotilla else None
|
||||
if forzado:
|
||||
if not os.path.isdir(forzado):
|
||||
fatal(f"{escotilla}={forzado} no existe")
|
||||
print(f"⚠ {escotilla}: se mide {os.path.basename(forzado)}, que NO es necesariamente el vigente")
|
||||
return forzado
|
||||
r = subprocess.run([HAMMER, "--store", STORE, "hash", f"recipes/{receta}.toml"],
|
||||
cwd=ROOT, capture_output=True, text=True)
|
||||
if r.returncode != 0:
|
||||
fatal(f"no pude hashear {receta}: {r.stderr.strip()}")
|
||||
h = r.stdout.strip().splitlines()[-1].removeprefix("b3:")
|
||||
d = os.path.join(STORE, f"{h}-{nombre}")
|
||||
if not os.path.isdir(d):
|
||||
fatal(f"{nombre} b3:{h[:8]} no está sellado — construilo antes")
|
||||
if not os.listdir(d):
|
||||
fatal(f"{nombre} b3:{h[:8]} está VACÍO: eso no es un artefacto, es un nombre")
|
||||
return d
|
||||
|
||||
|
||||
def correr(atuq, costura, salida, estado):
|
||||
"""Una sesión del navegador con el fichero de foco en `estado` (None = no existe)."""
|
||||
poner = "rm -f " + FOCO if estado is None else (
|
||||
f"mkdir -p {os.path.dirname(FOCO)} && printf '%s\\n' '{estado}' > {FOCO}")
|
||||
guion = f"""
|
||||
mkdir -p /root /salida/hogar
|
||||
[ -s /etc/machine-id ] || tr -d - < /proc/sys/kernel/random/uuid > /etc/machine-id
|
||||
export HOME=/salida/hogar
|
||||
{poner}
|
||||
timeout 45 /usr/bin/atuq --profile /salida/perfil --no-remote --headless 'about:blank' \
|
||||
> /salida/consola.log 2>&1
|
||||
# El estado, RELEÍDO después de que el navegador terminó: es la prueba de que no lo tocó.
|
||||
if [ -f {FOCO} ]; then cp {FOCO} /salida/focus-final; else echo AUSENTE > /salida/focus-final; fi
|
||||
exit 0
|
||||
"""
|
||||
subprocess.run([
|
||||
"bwrap",
|
||||
"--overlay-src", ALPINE, "--overlay-src", RFS,
|
||||
"--overlay-src", atuq, "--overlay-src", costura,
|
||||
"--tmp-overlay", "/",
|
||||
"--dev-bind", "/dev", "/dev", "--proc", "/proc", "--bind", "/sys", "/sys",
|
||||
"--tmpfs", "/tmp", "--tmpfs", "/run", "--unshare-pid",
|
||||
"--bind", salida, "/salida",
|
||||
"--uid", "0", "--gid", "0",
|
||||
"--setenv", "PATH", "/usr/bin:/bin:/usr/sbin:/sbin",
|
||||
"--setenv", "XDG_RUNTIME_DIR", "/run",
|
||||
"--setenv", "XDG_DATA_DIRS", "/usr/share", "--setenv", "MOZ_HEADLESS", "1",
|
||||
"--setenv", "MOZ_DISABLE_CONTENT_SANDBOX", "1",
|
||||
"--setenv", "MOZ_DISABLE_GMP_SANDBOX", "1",
|
||||
"--setenv", "MOZ_DISABLE_RDD_SANDBOX", "1",
|
||||
"--setenv", "MOZ_DISABLE_SOCKET_PROCESS_SANDBOX", "1",
|
||||
"--setenv", "MOZ_DISABLE_UTILITY_SANDBOX", "1",
|
||||
"sh", "-c", guion,
|
||||
], check=False)
|
||||
log = open(os.path.join(salida, "consola.log"), errors="replace").read()
|
||||
lineas = [l[len("FOCO "):] for l in log.splitlines() if l.startswith("FOCO ")]
|
||||
final = ""
|
||||
try:
|
||||
final = open(os.path.join(salida, "focus-final"), errors="replace").read().strip()
|
||||
except OSError:
|
||||
pass
|
||||
return lineas, final, log
|
||||
|
||||
|
||||
def main():
|
||||
atuq = artefacto("atuq", "atuq", "ATUQ_DIR")
|
||||
costura = artefacto("puriy-costura", "puriy-costura")
|
||||
print(f"artefactos: {os.path.basename(atuq)}\n {os.path.basename(costura)}")
|
||||
if not os.path.isdir(RFS):
|
||||
fatal(f"no está el rootfs en {RFS} — corré antes `scripts/atuq-nested.sh`")
|
||||
|
||||
# estado puesto → (lo que la extensión tiene que decir, la insignia, el fichero tras la sesión)
|
||||
casos = [
|
||||
("on", "on", "foco", "on"),
|
||||
("off", "off", "", "off"),
|
||||
(None, "unknown", "?", "AUSENTE"),
|
||||
]
|
||||
tmp = tempfile.mkdtemp(prefix="atuq-foco-")
|
||||
try:
|
||||
for puesto, espera, insignia, tras in casos:
|
||||
salida = os.path.join(tmp, f"caso-{puesto or 'ausente'}")
|
||||
os.makedirs(os.path.join(salida, "perfil"))
|
||||
with open(os.path.join(salida, "perfil", "user.js"), "w") as fh:
|
||||
fh.write('user_pref("browser.dom.window.dump.enabled", true);\n')
|
||||
fh.write('user_pref("browser.shell.checkDefaultBrowser", false);\n')
|
||||
|
||||
print(f"\n-- fichero de foco: {puesto if puesto else '(no existe)'}")
|
||||
lineas, final, log = correr(atuq, costura, salida, puesto)
|
||||
for l in lineas:
|
||||
print(" " + l)
|
||||
if not lineas:
|
||||
destino = os.path.join(ROOT, "work", f"atuq-foco-{puesto or 'ausente'}.log")
|
||||
os.makedirs(os.path.dirname(destino), exist_ok=True)
|
||||
shutil.copy(os.path.join(salida, "consola.log"), destino)
|
||||
fatal(f"la extensión no dijo nada — log en {destino}")
|
||||
|
||||
estados = [l for l in lineas if l.startswith("ESTADO ")]
|
||||
if not estados:
|
||||
fatal(f"la extensión no informó ningún estado: {lineas}")
|
||||
if not estados[-1].startswith(f"ESTADO {espera} "):
|
||||
fatal(f"con el fichero en {puesto!r} dijo «{estados[-1]}» y tenía que decir «{espera}»")
|
||||
if f'INSIGNIA texto="{insignia}"' not in lineas:
|
||||
fatal(f"la insignia no quedó en «{insignia}»: {[l for l in lineas if 'INSIGNIA' in l]}")
|
||||
# Y la promesa: el navegador no cambió el estado.
|
||||
if final != tras:
|
||||
fatal(f"el fichero del foco quedó en {final!r} y tenía que quedar en {tras!r}"
|
||||
" — el navegador NO debería poder tocarlo")
|
||||
print(f" ✓ dijo {espera}, pintó «{insignia}», y dejó el estado igual ({final})")
|
||||
|
||||
print("\n✓ los tres estados llegan al navegador tal como los dejó el sistema")
|
||||
print("✓ «no sé» es una respuesta: sin fichero, la insignia dice «?» y no «sin foco»")
|
||||
print("✓ y tras una sesión entera el estado del foco quedó intacto: el navegador no lo apaga")
|
||||
return 0
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user