diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f3bfd7..549b993 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,26 @@ `{"ok": true, "spend": 12.06, "budget": 100.0, "duration": "1d", "pct": 12.1, "alias": "testoff", "total_spend": 26.5, "reset_at": "2026-11-06T00:00:00+00:00", "source": "… из .env"}`. +### 1.7.7, пересборка: окно приложения больше не теряется + +Дефект, найденный на тестовом ноутбуке 05.11.2026: ночное обновление 1.7.6 → 1.7.7 прошло +«успешно» (в `update-check.log` — `обновление установлено`), а папки `App` на диске не осталось: +ярлык на рабочем столе есть, запускать нечего. + +Причина: в `app-setup.zip` на сервере маркер `app-version.txt` был `1.7.6`, а дистрибутив уже +`1.7.7`. Установщик распаковывал архив **поверх** рабочей папки, видел несовпадение версий и +удалял `App` целиком — «окно не ставлю». Метка `.ailab-install.json` при этом уже была записана +как `1.7.7`, поэтому следующая ночная проверка считала машину актуальной и окно не возвращала. + +Что изменено: + +| Файл | Было | Стало | +|---|---|---| +| `setup.ps1`, шаг 9/9 | распаковка поверх `App`, при несовпадении версии `Remove-Item $AppDir` | распаковка в `App.new`, сверка `app-version.txt`, подмена папки только при совпадении; при несовпадении рабочее окно остаётся нетронутым | +| `setup.ps1`, финал | при отсутствии окна метка установки врала | в `.ailab-install.json` добавляется `app_missing: true` | +| `update-check.ps1` | сравнивал только версии | если нет `App\Hermes.exe` — ставит заново независимо от версии | +| `build-app.ps1` | архив окна мог остаться от прошлой версии | при каждом выпуске архив пересобирается с `app-version.txt` = версии дистрибутива | + ## 1.7.6 — план (не выпущено) **Статус: состав накапливается. Сборка не запускается — Олег добавляет нововведения, релиз собирается после закрытия состава.** @@ -269,3 +289,57 @@ - [ ] Записано, как выключается плагин без пересборки (флаг в `config.yaml`) — маска не должна требовать релиза, чтобы её отключить при массовых ложных срабатываниях. - [ ] Назначен ответственный за журнал инцидентов на первые две недели после выдачи. - [ ] Через две недели — разбор: сколько записей в журнале, сколько отказов по красному, сколько ложных масок. + +--- + +## 1.7.8 — план (под вопросом) + +**Статус: под вопросом. Собирать только после явного «да» Олега при сборке.** + +Тема: **один ключ сотрудника (`sk-…`) открывает модели и MCP ГОСТ-ассистента; прямой +`api.gostassistent.com` из клиента убирается.** + +Основание: ТЗ §7 («MCP гостассиента `https://gw.ailabscrume.ru/mcp` тем же ключом») +не доехало в 1.7.6/1.7.7 — клиент ходит напрямую с отдельным upstream-ключом 64 символа. +Проверено живьём 06.11.2026: ключ сотрудника `sk-…` (25 симв.) на шлюзе +`gw.ailabscrume.ru/mcp` → `initialize`/`tools/list`/`tools/call` = 200, 4 инструмента, +реальная карточка ГОСТ; тот же ключ на `api.gostassistent.com/api/mcp` → 401. + +### Что меняется + +| № | Что | Где | +|---|---|---| +| 0 | **Разделение сборок флагом `AILAB_DISTRIBUTION`.** Скрытие служебных вкладок (биллинг, «О программе», экспорт/импорт/сброс, секции chat/workspace/safety/browser) выполняется только при `AILAB_DISTRIBUTION=1`. Чистый Hermes владельца собирается без флага и показывает всё. Реализовано: `src/lib/build-flags.ts`, `vite.config.ts`, `settings/constants.ts`, `settings/index.tsx` | сборка | +| 1 | `mcp_servers.gostassistent.url` → `https://gw.ailabscrume.ru/mcp`, авторизация тем же ключом, что модели (через `key_cmd`), без отдельной переменной | `config.yaml` | +| 2 | `MCP_GOSTASSISTENT_API_KEY` убрать из `env_requires` (сейчас `required: false`, но само наличие переменной создаёт путаницу) | `distribution.yaml` | +| 3 | Имена инструментов станут `gostassistent_-document_*` (в агенте `mcp__gostassistent___*`). Скиллы на старые имена не зашиты — проверено | сборка | +| 4 | Ключ без `object_permission.mcp_servers` → 403 с понятным текстом, а не молчание | шлюз/клиент | +| 5 | Тексты: `INSTALL.md` (сейчас «включается своим ключом в `.env`»), `README.md` стр. 97, `CHANGELOG` | документы | + +### Вопросы, которые задать Олегу при сборке + +1. **Главный:** переводим MCP на шлюз или оставляем прямой путь? Переезд означает один + общий upstream-ключ ГОСТ на всех — персонального стопа на уровне базы не остаётся, + ограничение только на ключе шлюза (и это ограничение неденежное — LiteLLM MCP-вызовы + не тарифицирует, `max_budget` на них не действует). +2. **Права:** у `duda.v` и `anushov.k` сейчас 0 MCP-серверов (проверено `/key/list?return_full_object=true`, + 06.11.2026). После переезда они получат 403. Выдать право или зафиксировать, что + инструменты им не положены? +3. **Мерж конфига:** установщик копирует `config.yaml` целиком с `-Force`, без `.bak` и без + мержа (проверено `setup.ps1`, строки 565–571). Правка `url` доедет до всех ночным + автообновлением — но у тех, кто уже что-то настроил локально, настройки исчезнут. + Делаем `config.local.yaml` + бэкап в этом релизе или отдельно? + +### Критерий приёмки + +- [ ] На чистой установке в `.env` одна переменная ключа; `grep api.gostassistent.com` по профилю → 0 совпадений. +- [ ] `initialize` / `tools/list` / `tools/call` ключом сотрудника → 200. +- [ ] Ключ без права MCP → 403 с текстом, а не молчание. +- [ ] Модели продолжают работать тем же ключом. + +### Блокеры и риски + +- Установщик перезаписывает `config.yaml` и `skills/` без бэкапа и мержа — правка в `config.yaml` + без этого механизма бессмысленна для тех, кто уже настроил локальные дополнения. +- `duda.v`, `anushov.k` — без права MCP. +- Общий upstream-ключ ГОСТ: нет персонального стопа, нет денежного учёта MCP-вызовов. diff --git a/update-check.ps1 b/update-check.ps1 index d3c0f6b..6a60747 100644 --- a/update-check.ps1 +++ b/update-check.ps1 @@ -123,6 +123,15 @@ $cmp = Compare-Version $installed $latest +# Маркер версии говорит «актуально», но окна на диске может не быть: прошлое +# обновление упало на подмене папки, а версия уже записалась. Без этой проверки +# машина навсегда остаётся с битым ярлыком и без приложения. +$AppExe = Join-Path $env:LOCALAPPDATA 'AILabSCRUME\App\Hermes.exe' +if (-not (Test-Path $AppExe)) { + Write-Log (("окно приложения отсутствует ({0}) — ставлю {1} заново" -f $AppExe, $resp.version)) + $cmp = -1 +} + if ($cmp -ge 0) { Write-Log ("актуально: установлено {0}, на GitHub {1}" -f $installedRaw, $resp.version) exit 0