jorgex-stack 1.9.73 → 1.9.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,6 +30,38 @@ For an installed package, skill checks are **discovery-only**: `update --check`
30
30
 
31
31
  The skill never edits artifacts or creates tasks. During audit remediation, the orchestrator is the only writer of active work artifacts and returns every gap to its owner; delegated writers still own their bounded code, test, and documentation tasks. Details: [docs/references/sdd-workflow.md](docs/references/sdd-workflow.md).
32
32
 
33
+ ### OpenCode v2: un solo runtime soportado
34
+
35
+ Stack mantiene un único `opencode` v2 entre los cuatro runtimes gestionados. El ID público sigue siendo `opencode`; no se publican alias `opencode-v1`/`opencode-v2`.
36
+
37
+ - **Version-gate antes de escribir.** El binario debe declarar `major === 2`. Las versiones v1, mayor desconocido, falta de binario o timeout se rechazan sin proyectar configuración, agentes, skills, MCP, plugins ni permisos en el HOME real. El gate es fail-closed.
38
+ - **Migración owned v1 → v2.** A nivel de campos del server config y de `~/.jorgex-stack/model-map.json`, una config existente solo se migra cuando la ruta y la propiedad son exactas: los identificadores legados conocidos del canon v1 (`openai/gpt-5.6-sol`, los límites `872000/744000/128000` y los IDs dotted basados en ese modelo) se transfieren al destino nativo v2 (`openai/gpt-6.1-sol`) con backup previo. Una elección manual, un valor modificado o un campo desconocido bloquean con remedio antes de cualquier escritura. Configuración ajena, hermanos no migrables o providers no canónicos se preservan intactos o fallan cerrado; `uninstall` no borra campos ajenos y conserva providers no canónicos.
39
+ - **`cli.json` y raíz efectiva.** El archivo compacto del cliente v2 (`cli.json`) es **distinto del server config** (`opencode.json` / `opencode.jsonc`) y vive en la misma raíz efectiva: `OPENCODE_CONFIG_DIR` si está definido; sin él, `$XDG_CONFIG_HOME/opencode` y, en su defecto, el default de HOME según la fuente oficial. `cli.json` cubre el cliente (presentación, audio, panel TUI) y el server config cubre el resto (modelos, providers, permissions, MCP). Mientras exista `tui.json`/`kv.json` legacy el migrador nativo del binario debe correr primero, así que Stack no precrea `cli.json`; abre OpenCode v2 una vez y repite `install` para sembrarlo. Stack siembra **solo en las hojas ausentes** los dos bloques siguientes (no parámetros de modelo):
40
+
41
+ - **Presentación y permisos locales**: `theme.name` y `theme.mode` en `system`; `session.verbosity` en `low`; `session.tps` y `debug.turn_tokens` activos; `session.permissions` en `autoaccept` — este ajuste no anula las reglas `deny` declaradas por el host, solo cambia cómo el cliente presenta las aprobaciones locales.
42
+
43
+ - **Audio nativo**: `attention.notifications` y `attention.sound` activos con `attention.volume` en `0.1`. El único sonido audible del cliente es `done.wav` (el evento nativo `done`, que también se dispara en `interrupted` y **no** marca final de roadmap); los cinco eventos restantes (`subagent_done`, `question`, `permission`, `error`, `default`) apuntan todos a un único `silent.wav` propio PCM16 mono de muestras cero no vacías, con overrides válidos y legibles evita los avisos audibles intermedios; si faltan o fallan, el host puede usar el fallback builtin. Plugins con audio directo (p. ej. Herdr u otros) **no** quedan cubiertos por estos overrides y siguen sonando lo suyo; Stack no los modifica ni sus notificaciones visuales.
44
+
45
+ La escritura se hace como edición JSONC preservando **comentarios y claves ajenas** —no se garantiza un orden concreto de campos— con ownership por campo y `unmerge`; un valor manual existente —igual al canon, distinto, `false` o `null`— se conserva sin reclamar. No se promete una reproducción audible verificada ni que el motor de audio del host esté apagado.
46
+ - **Roster y defaults nativos.** El agente principal v2 fija `model: openai/gpt-6.1-sol`; los subagentes reciben el roster de tiers (`strong`, `standard`, `cheap`) y overrides nominales por nombre con un `model#variant` único, serializado como escalar YAML seguro. `agents.title.model` y `agents.summary.model` usan modelos distintos del principal y la compactación corre sobre el modelo de sesión, no sobre uno aparte. El `agents.plan` queda `disabled: true` (Shift+Tab no es necesario para el flujo por defecto) y el wrapper primary sigue sin lock de modelo ni de effort. Los límites explícitos son metadatos locales solicitados hasta confirmar la aceptación del backend OAuth; no se promete una ventana universal de 1 M y se advierte explícitamente de la dependencia de cuenta.
47
+ - **Permisos canónicos v2.** El bloque nativo es un array ordenado `permissions` con tuplas `action/resource/effect`. La política canónica v2 fresh no declara gate humano (no hay `ask` para git/shell ordinarios); por eso el informe local de capabilities devuelve `tool-approval: unavailable` con razón que reconoce la política, no un estado `manual` ni un runtime certificado. Configuraciones ajenas, modificadas o ilegibles conservan una razón conservadora distinta. La regla `fresh-only` se mantiene: una config existente se preserva; el opt-in `install --upgrade-permissions` reemplaza el bloque entero con backup. Detalle operativo: [docs/references/permissions.md](docs/references/permissions.md).
48
+ - **Plugins y scripts.** Hooks, guardia de worktree y scripts de Stack viven en `stack/plugins/opencode/` y se cargan sin SDK v1. Los scripts propios OpenCode no dependen de otra instalación runtime; los plugins v1 no se reinterpretan como v2. Los **recursos estáticos** proyectados al host OpenCode (`plugins/hooks.ts`, `plugins/worktree.ts`, `scripts/post-pr-review.cjs`, `scripts/repair-worktree-config.cjs`) están listados byte-exactos en `src/lib/opencode-static-resources.json`, que acredita el canon legacy v1 publicado (no el hash actual) y cuya fila "current" se deriva de los bytes que la proyección va a escribir. Tabla única de reglas owned/unowned × current/legacy/unknown × install/update/uninstall en [docs/references/opencode-static-resources.md](docs/references/opencode-static-resources.md); aquí no se duplican.
49
+ - **Engram v2 oficial, aún no.** Stack no ejecuta el setup oficial Engram v1 sobre OpenCode ni declara Memory Protocol completo: la integración se completa en PR04 cuando el release oficial estable posterior a Engram #1526 (o equivalente) sea verificado y adoptado. Una instalación real con ese prerrequisito ausente devuelve `exit 1` parcial: Stack proyecta y verifica primero sus archivos gestionados (los deja aplicados con manifest/backup), pero no invoca `engram setup` y conserva el binario y la DB de Engram del usuario. El caller ve el código `1` y la razón accionable; no se afirma rollback total ni setup exitoso. `install` (con `dry-run` o `--target-dir`) mantiene sus skips intencionales (`ran:false`) y solo reporta el skip, no imprime el mensaje de prerrequisito como un warning completo.
50
+ - **Browser Control obligatorio (PR02) y panel/audio del cliente en este PR.** OpenCode v2 usa Browser Control (CLI/skill/MCP) como única vía de navegador gestionado: no hay selector Playwright CLI residual para OpenCode (`--playwright-runtimes=opencode` se rechaza con diagnóstico) y la invocación oficial es `jorgex-stack browser control <args>` con guard, no un binario global ni un MCP manual. La integración Browser Control se entrega en PR02; este PR (PR03) añade el panel TUI propio y la política de audio del cliente v2 sobre `cli.json` (defaults ausentes + `silent.wav` propio + `done.wav` audible) sin tocar Herdr ni notificaciones visuales de terceros. Auth física de los assets current-only y sus límites en [`docs/references/opencode-static-resources.md`](docs/references/opencode-static-resources.md); cliente vs. server config en [`docs/references/models.md#opencode-v2`](docs/references/models.md#opencode-v2). La política condicional de Playwright/DevTools para los demás runtimes no cambia por esta decisión.
51
+
52
+ - **Panel TUI del cliente (`jorgex.subagents`).** Se registra como entrada local `./tui/subagents` en `cli.json.plugins` mediante `editJsoncArray` (inserción/remoción indexada, prune del contenedor vacío). Una entrada manual preexistente igual (string o `{ "package": "./tui/subagents", …opciones }`) no se duplica ni se reclama; una disable directive (`-./tui/subagents`, `-jorgex.subagents` o `-*`) que alcance el panel se preserva y no se neutraliza con un override posterior. El panel se monta como slot `append: "sidebar.content"` y como slot `append: "app"` para registrar el keymap; el comando `/subagents` y la cabecera son clic-izquierdo (clic-derecho no hace nada), y un clic-izquierdo en una fila navega a la sesión hija correspondiente. Aparece plegado al entrar/cambiar/volver a una sesión y las filas listadas son solo las activas o bloqueadas; los contadores `running`/`done`/`needs input` (en inglés, etiqueta `Subagents`) reflejan todas las del grupo familiar, las filas `done` no se listan al abrir y desaparecen al terminar (no se conserva ni el tiempo ni la fila). Cada fila activa muestra `provider/model#variant` y tokens acumulados; la duración solo aparece cuando el `startedAt` es conocido. Stack no garantiza un intercalado exacto entre cards nativas, no sustituye tarjetas nativas ni las notas de Herdr, y el autoload de plugins del server config queda intacto.
53
+
54
+ - **Verificación observada.** Smoke verificado en Linux con Bubblewrap disponible y namespaces permitidos; versiones observadas, no fijadas. La copia es privada SHA-idéntica de OpenCode `2.0.22` (evidencia, no pin) y corre con FS allowlist del runtime de sistema (`/usr/bin`, `/lib`, `/lib64` en solo lectura) más root temporal propio y `/proc`/`dev`, sin montar el perfil real ni el repo; un probe antes de ejecutar verifica los namespaces y, si falla, no hay fallback; la red loopback queda compartida para los fixtures locales (no es aislamiento de red). Los tests reales con gate verifican el montaje del app/keymap, el clic izquierdo en la cabecera, la navegación a la sesión hija por `sessionID`, el repliegue al volver, el clic derecho como no-op, la fila `running`→`done` retirada y un hijo LIVE con formulario pendiente bajo barrera (`needs input` → fila bloqueada, tokens acumulados `5 tok`); el modelo de la sesión y el del fixture son visibles con el mismo `id` (`variant` nativo `#default` robusto); la duración solo aparece cuando el inicio es conocido (segunda corrida con TUI ya enganchado) y se omite en hijos lanzados antes de que el TUI arrancara; audio y alertas quedan off solo en el fixture, con API efímera propia y sin LLM remoto ni config personal. El gate `JORGEX_OPENCODE_V2_BIN=<binario-v2-instalado-compatible>` exige Linux + `bwrap` + probe de namespace, o falla cerrado; sin gate los 7 tests puros corren y los 3 reales se omiten; CI no ejerce este gate todavía y `bwrap` ya viene disponible en el entorno del smoke, no se instalan paquetes nuevos.
55
+
56
+ Comando de verificación:
57
+
58
+ ```bash
59
+ JORGEX_OPENCODE_V2_BIN=<binario-v2-instalado-compatible> pnpm exec vitest run tests/opencode-v2-tui.test.ts
60
+ ```
61
+ - **Sandbox para pruebas aisladas.** `JORGEX_OPENCODE_TARGET_MAJOR=2` solo es evidencia explícita de sandbox cuando se ejecuta junto a `--target-dir OpenCode`: el adapter ya sabe que `--target-dir` requiere esa declaración y, sin ella, no proyecta OpenCode v2 ni ejecuta el binario personal dentro del target (bloquea). En una instalación real sobre el HOME del usuario esa variable se ignora y el binario real se verifica siempre; el version-gate real lo decide el host, no Stack. No es una versión de release ni un bypass del verification real.
62
+
63
+ Más detalle de permisos en [docs/references/permissions.md](docs/references/permissions.md); roster y límites en [docs/references/models.md](docs/references/models.md); estado de Browser Control en [docs/references/browser-automation.md](docs/references/browser-automation.md); reconocimiento de capabilities en [docs/references/quality-receipt.md](docs/references/quality-receipt.md); recursos estáticos proyectados y verificación byte-auth en [docs/references/opencode-static-resources.md](docs/references/opencode-static-resources.md).
64
+
33
65
  ## Usage
34
66
 
35
67
  Install and run via npm without cloning the repository:
@@ -63,7 +95,7 @@ For development from a clone, run the same commands through `pnpm cli <command>`
63
95
 
64
96
  Every command supports `--dry-run`, `--yes`, and `--target-dir <dir>` for testing without touching the real config. Writes create automatic backups and verify idempotency; merges into user config are surgical (marked markdown sections, JSON/TOML upserts), so user-owned content is never touched. `--yes` does not authorize downloading missing Engram; use `--engram` for that explicit consent. The interactive install asks before installing it, while dry-run and target-dir never download it.
65
97
 
66
- Runtime defaults are documented in [docs/references/permissions.md](docs/references/permissions.md) for permissions and [docs/references/models.md](docs/references/models.md) for the Sol primary default, field-level ownership and independent subagent routing. The quality policy and `jorgex.quality.receipt` contract are documented in [docs/references/quality-receipt.md](docs/references/quality-receipt.md). OpenCode remains provider-agnostic for subagents; its primary defaults to the OpenAI OAuth model `openai/gpt-5.6-sol` unless the user replaces it.
98
+ Runtime defaults are documented in [docs/references/permissions.md](docs/references/permissions.md) for permissions and [docs/references/models.md](docs/references/models.md) for the Sol primary default, field-level ownership and independent subagent routing. The quality policy and `jorgex.quality.receipt` contract are documented in [docs/references/quality-receipt.md](docs/references/quality-receipt.md). OpenCode v2 sigue siendo provider-agnostic para subagentes; su agente principal fija `openai/gpt-6.1-sol` salvo que el usuario lo cambie (ver la sección OpenCode v2 arriba y la guía de modelos), y Codex/Pi conservan `gpt-5.6-sol` salvo sustitución del usuario.
67
99
 
68
100
  ### Modes: Human and Programmatic
69
101
 
@@ -91,7 +123,7 @@ Flags:
91
123
 
92
124
  This installs into all detected runtimes. To be explicit, add `--agents opencode,claude-code,codex,pi` or a comma-separated subset. Always pass `--mode programmatic`; without `--mode`, `--yes` and non-TTY installs default to `human`.
93
125
 
94
- OpenCode also requires an existing selection in `~/.jorgex-stack/model-map.json`; run `pnpm dlx jorgex-stack models --agents opencode` interactively once before a headless install.
126
+ OpenCode v2 usa los defaults del roster aprobado en config fresca y no exige selección previa para `install --yes` ni procesos sin TTY. Si quieres cambiar el roster o mantener una selección por agente explícita, ejecuta `pnpm dlx jorgex-stack models --agents opencode` interactivamente; las selecciones guardadas tienen precedencia sobre los defaults del roster.
95
127
 
96
128
  - `--mode human` cannot be combined with `--subagent-concurrency`.
97
129
  - Without `--mode`, the first run asks interactively; `--yes`, non-TTY, and `--target-dir` default to `human`.
@@ -153,11 +185,11 @@ Claude's official plugin still requires stable Engram 2.0.0 or newer: an existin
153
185
 
154
186
  ### Integración oficial de Engram
155
187
 
156
- En una instalación real, `install` delega Claude Code, Codex y OpenCode 1.x a `engram setup <runtime>` una vez por runtime durante esa instalación. Para Pi, si declara transporte nativo (`mcp-native-v1`), Stack ejecuta la fase nativa (`runNativePiMcpPhase`) y no invoca `engram setup pi`; en modo legacy Pi sigue ejecutando `engram setup pi` antes de activar el paquete. El comportamiento del provider oficial permanece sin cambios: Stack no proyecta el protocolo Engram ni filtra sus herramientas. No ejecuta ese setup durante `dry-run`, `--target-dir`, `doctor` ni `uninstall`; `install` lo invoca como parte de la reconciliación interna cuando es necesario. Stack respalda los archivos afectados, verifica los artefactos oficiales y revierte el cambio si falla; para Pi incluye `settings.json`, `mcp.json` y el árbol `npm`. No modifica `~/.engram`, sus memorias ni reemplaza un binario existente. Los artefactos oficiales se conservan al desinstalar. El `stack/plugins/opencode/engram.ts` legado ya no se despliega y queda retirado por esta integración.
188
+ En una instalación real, `install` delega Claude Code y Codex a `engram setup <runtime>` una vez por runtime durante esa instalación. Para Pi, si declara transporte nativo (`mcp-native-v1`), Stack ejecuta la fase nativa (`runNativePiMcpPhase`) y no invoca `engram setup pi`; en modo legacy Pi sigue ejecutando `engram setup pi` antes de activar el paquete. OpenCode v2 no ejecuta `engram setup` propio: la integración oficial Engram v2 sobre OpenCode se completa en PR04 cuando un release oficial estable posterior al gate Engram #1526 (o equivalente) sea verificado y adoptado; hasta entonces Stack conserva memorias/binario y muestra el prerrequisito externo sin ejecutar setup v1 sobre el runtime. El comportamiento del provider oficial permanece sin cambios: Stack no proyecta el protocolo Engram ni filtra sus herramientas. No ejecuta ese setup durante `install --dry-run`, `--target-dir`, `doctor` ni `uninstall`. Stack respalda los archivos afectados, verifica los artefactos oficiales y revierte el cambio si falla; para Pi incluye `settings.json`, `mcp.json` y el árbol `npm`. No modifica `~/.engram`, sus memorias ni reemplaza un binario existente. Los artefactos oficiales se conservan al desinstalar. El `stack/plugins/opencode/engram.ts` legado ya no se despliega y queda retirado por esta integración.
157
189
 
158
190
  Para Codex, Engram 2.0 ignora `CODEX_HOME` y escribe siempre en `$HOME/.codex`; por eso el setup oficial real requiere ese destino predeterminado. Si `CODEX_HOME` apunta a otro directorio, `install` falla cerrado en el preflight, antes de escribir la configuración de Codex, y recomienda usar `$HOME/.codex`. `dry-run` y `--target-dir` no invocan el setup oficial.
159
191
 
160
- La política de complementos distingue estrategias `exact` y `provider-managed`. DevTools MCP y Playwright CLI son `provider-managed`: un `install`/`update` deliberado resuelve y verifica el candidato exacto antes de activarlo, y guarda versión/integridad observadas para que `install`/`update` posteriores refresquen usando la preferencia persistida o el opt-in explícito. Las integraciones oficiales de Engram también son `provider-managed`/rolling, incluido Codex `main`, `pi-mcp-adapter` y el setup oficial de Pi sin pin en Stack. OpenCode 2 queda fuera de alcance y no bloquea esta integración.
192
+ La política de complementos distingue estrategias `exact` y `provider-managed`. DevTools MCP y Playwright CLI son `provider-managed`: un `install`/`update` deliberado resuelve y verifica el candidato exacto antes de activarlo, y guarda versión/integridad observadas para que la reconciliación interna (sin comando público de `sync`) no tenga que volver a consultar al proveedor. Las integraciones oficiales de Engram también son `provider-managed`/rolling, incluido Codex `main`, `pi-mcp-adapter` y el setup oficial de Pi sin pin en Stack. OpenCode v2 sigue su propio camino de adopción (Browser Control en PR02, Engram v2 oficial en PR04) y no bloquea la integración de los demás runtimes.
161
193
 
162
194
  Los receipts históricos, incluido `jorgex-pi@0.8.24`, son evidencia para recuperación/migración y no candidatos de instalación nuevos. La selección gestionada resuelve el `latest` publicado a una versión exacta y verifica el artefacto antes de activarlo. No edites receipts o hashes ni borres `HOME`, Engram o la proyección de otro runtime para forzar confianza. La migración y rollback están en [docs/references/pi-runtime.md](docs/references/pi-runtime.md).
163
195
 
@@ -183,15 +215,18 @@ El paquete Pi adoptado ya no inyecta un fallback propio con `Communication Style
183
215
 
184
216
  La automatización de navegador es opt-in. Stack retiene el release observado en un árbol privado, no en el directorio global de pnpm. Comprueba el tarball npm oficial y el cierre transitivo instalado en un stage aislado, promociona ese árbol con receipt y verifica launcher y árbol antes de las ejecuciones gestionadas. Un `playwright-cli` global o una invocación `pnpm dlx` **no** ofrecen esa garantía y nunca son fallback. La caché del navegador pertenece a Playwright; uninstall y update no borran perfiles, cookies, storage state, trazas ni capturas.
185
217
 
186
- - **Playwright CLI** (recomendado): `install --playwright` selecciona el último release estable del proveedor en ese momento, verifica el árbol gestionado, descarga Chromium desde ese árbol y comprueba un arranque headless local de `about:blank` antes de guardar el opt-in. Úsalo mediante `jorgex-stack browser playwright <args>` (o el dispatcher empaquetado `jorgex-stack-playwright` para el handoff Pi confiable), no mediante un binario global. Usa una sesión `-s=<nombre>` propia, `snapshot`, comprueba resultados y cierra solo tu sesión.
218
+ OpenCode v2 usa Browser Control (CLI/skill/MCP) como única vía de navegador gestionado; el adaptador rechaza `--playwright-runtimes=opencode` con diagnóstico accionable y proyecta la skill `browser-control`, su MCP en Code Mode y el prefijo CLI gestionado `jorgex-stack browser control <args>` para los ejemplos de la skill oficial. Los ejemplos del bloque `Browser automation` que invoquen `browser-control` se redirigen siempre al dispatcher gestionado: nunca al binario global ni a un `playwright-cli` alternativo. Solo el servicio Linux (systemd de usuario) es opt-in mediante `--browser-control-service` y solo aplica cuando OpenCode está entre los runtimes destino. La política condicional de Playwright/DevTools para los demás runtimes no cambia por esta decisión.
219
+
220
+ - **Playwright CLI** (recomendado para los runtimes que aún lo admiten): `install --playwright` selecciona el último release estable del proveedor en ese momento, verifica el árbol gestionado, descarga Chromium desde ese árbol y comprueba un arranque headless local de `about:blank` antes de guardar el opt-in. Úsalo mediante `jorgex-stack browser playwright <args>` (o el dispatcher empaquetado `jorgex-stack-playwright` para el handoff Pi confiable), no mediante un binario global. Usa una sesión `-s=<nombre>` propia, `snapshot`, comprueba resultados y cierra solo tu sesión.
187
221
  - **Chrome DevTools MCP** (diagnóstico avanzado): desactivado por defecto y seleccionado por runtime. Stack usa launcher local verificado y conserva `--isolated --redact-network-headers --no-performance-crux --no-usage-statistics`. La redacción de cabeceras no cubre los cuerpos request/response; evita sesiones sensibles. Esta integración no descarga Chrome.
188
222
  - **Pi**: el paquete compatible publicado acepta un handoff Playwright v2 con SHA del dispatcher Stack, launcher y árbol. Stack lo proyecta solo con receipt browser verificado, selección explícita y declaración `contract/browser-handoffs.v1.json` del Pi instalado; un Pi antiguo sin ella bloquea v2/v3 sin deducir soporte por versión. El lector v1 permanece para receipts antiguos, no como fallback nuevo. En Windows Pi ejecuta el `.js` autenticado con Node sin shell. DevTools v3 también es byte-bound.
189
223
 
190
224
  ```bash
191
225
  # Interactivo: el cursor Playwright parte en No.
192
226
  pnpm dlx jorgex-stack install
193
- # Opt-in no interactivo explícito para los runtimes de archivo seleccionados.
194
- pnpm dlx jorgex-stack install --yes --playwright --playwright-runtimes=opencode,claude-code
227
+ # Opt-in no interactivo explícito (OpenCode v2 queda fuera de la selección; su
228
+ # integración gestionada es Browser Control y no acepta `--playwright-runtimes=opencode`).
229
+ pnpm dlx jorgex-stack install --yes --playwright --playwright-runtimes=claude-code,codex
195
230
  # Ejecución gestionada tras la activación.
196
231
  jorgex-stack browser playwright -s=mi-tarea open --browser=chromium https://example.com
197
232
  jorgex-stack browser playwright -s=mi-tarea snapshot
@@ -373,7 +373,8 @@ function assertContained2(root, candidate, label) {
373
373
  return resolved;
374
374
  }
375
375
  function packageDirectoryName(packageName) {
376
- return packageName === "@playwright/cli" ? "playwright-cli" : "chrome-devtools-mcp";
376
+ if (packageName === "@playwright/cli") return "playwright-cli";
377
+ return packageName === "chrome-devtools-mcp" ? "chrome-devtools-mcp" : "browser-control";
377
378
  }
378
379
  function validateClosureIdentity(value, packageName, version, integrity) {
379
380
  if (!Array.isArray(value) || value.length === 0) fail2("staged closure must be a non-empty array");
@@ -540,7 +541,7 @@ function assertNoOrphanedManagedBrowserState(packageDir, allowActiveLock) {
540
541
  }
541
542
  }
542
543
  function loadVerifiedManagedBrowserReceipt(stateDir, packageName) {
543
- if (packageName !== "@playwright/cli" && packageName !== "chrome-devtools-mcp") {
544
+ if (packageName !== "@playwright/cli" && packageName !== "chrome-devtools-mcp" && packageName !== "@opencode-ai/browser-control") {
544
545
  fail2("unsupported managed browser package");
545
546
  }
546
547
  const statePath = absolutePath(stateDir, "stateDir");
@@ -812,10 +813,12 @@ const evaluateVerifiedLauncher = (verifiedSource) => new Function("return (async
812
813
  await evaluateVerifiedLauncher(launcherSource);
813
814
  `;
814
815
  }
815
- function planManagedBrowserInvocation(stateDir, packageName, runtimeArgs) {
816
- if (packageName !== "@playwright/cli" && packageName !== "chrome-devtools-mcp") {
816
+ function assertSupportedBrowserPackage(packageName) {
817
+ if (packageName !== "@playwright/cli" && packageName !== "chrome-devtools-mcp" && packageName !== "@opencode-ai/browser-control") {
817
818
  fail2("unsupported managed browser package");
818
819
  }
820
+ }
821
+ function assertBrowserRuntimeArgs(packageName, runtimeArgs) {
819
822
  if (!Array.isArray(runtimeArgs)) fail2("runtimeArgs must be an array");
820
823
  if (runtimeArgs.some((arg) => typeof arg !== "string" || CONTROL_CHARACTERS2.test(arg))) {
821
824
  fail2("runtimeArgs contain an invalid control character");
@@ -823,8 +826,8 @@ function planManagedBrowserInvocation(stateDir, packageName, runtimeArgs) {
823
826
  if (packageName === "chrome-devtools-mcp" && (runtimeArgs.length !== DEVTOOLS_PRIVACY_FLAGS.length || runtimeArgs.some((arg, index) => arg !== DEVTOOLS_PRIVACY_FLAGS[index]))) {
824
827
  fail2("chrome-devtools-mcp requires the fixed privacy flags in order");
825
828
  }
826
- const receipt = loadVerifiedManagedBrowserReceipt(stateDir, packageName);
827
- if (receipt === null) fail2("verified managed browser receipt not found");
829
+ }
830
+ function buildInvocationPlan(receipt, runtimeArgs) {
828
831
  return {
829
832
  command: process.execPath,
830
833
  args: [
@@ -836,6 +839,13 @@ function planManagedBrowserInvocation(stateDir, packageName, runtimeArgs) {
836
839
  ]
837
840
  };
838
841
  }
842
+ function planManagedBrowserInvocation(stateDir, packageName, runtimeArgs) {
843
+ assertSupportedBrowserPackage(packageName);
844
+ assertBrowserRuntimeArgs(packageName, runtimeArgs);
845
+ const receipt = loadVerifiedManagedBrowserReceipt(stateDir, packageName);
846
+ if (receipt === null) fail2("verified managed browser receipt not found");
847
+ return buildInvocationPlan(receipt, runtimeArgs);
848
+ }
839
849
 
840
850
  // src/lib/tool-preferences.ts
841
851
  import fs5 from "fs";
@@ -934,9 +944,9 @@ function runManagedPlaywrightCommand(args, stateDir = dataDir()) {
934
944
  if (result.error !== void 0) throw result.error;
935
945
  return result.status ?? 1;
936
946
  }
937
- function runVerifiedManagedPlaywright(stateDir, args, options = {}) {
938
- const plan = planManagedBrowserInvocation(stateDir, "@playwright/cli", args);
939
- const result = spawnSync2(plan.command, [...plan.args], {
947
+ function runVerifiedManagedBrowser(stateDir, packageName, args, options = {}) {
948
+ const plan = planManagedBrowserInvocation(stateDir, packageName, args);
949
+ return spawnSync2(plan.command, [...plan.args], {
940
950
  encoding: "utf8",
941
951
  stdio: options.captureOutput ? "pipe" : "inherit",
942
952
  shell: false,
@@ -944,7 +954,9 @@ function runVerifiedManagedPlaywright(stateDir, args, options = {}) {
944
954
  maxBuffer: 2 * 1024 * 1024,
945
955
  env: { ...process.env, NO_UPDATE_NOTIFIER: "1" }
946
956
  });
947
- return result;
957
+ }
958
+ function runVerifiedManagedPlaywright(stateDir, args, options = {}) {
959
+ return runVerifiedManagedBrowser(stateDir, "@playwright/cli", args, options);
948
960
  }
949
961
 
950
962
  // src/browser-playwright.ts
package/dist/cli.d.ts CHANGED
@@ -28,6 +28,8 @@ interface Flags {
28
28
  devtools: boolean;
29
29
  noDevtools: boolean;
30
30
  upgradePermissions: boolean;
31
+ /** Opt-in Linux explícito al servicio Browser Control (install/sync/update). */
32
+ browserControlService: boolean;
31
33
  /** Opt-in explícito a la variante temporal #1567 solo en install/update con Pi. */
32
34
  engramTypeboxCompat?: boolean;
33
35
  receipt?: string;