diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 41917b2..c4a0638 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -4,29 +4,57 @@ ## Что нужно на той машине -- DeepSeek Harness, версия **0.2.0-rc.2** (проверить: `~/.dsh/dsh-runtimes/dsh-primary-runtime/runtime.json`, поле `desktopVersion`). +- DeepSeek Harness **0.2.x** (проверено на `0.2.1-rc.3`). Точную версию печатает `runtime.json`, поле `desktopVersion` — файл лежит в одном из двух мест, смотря как установлен харнесс: + + ```powershell + $runtimeJson = @( + "$env:USERPROFILE\.dsh\dsh-runtimes\dsh-primary-runtime\runtime.json", + "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources\runtime\primary-runtime\runtime.json" + ) | Where-Object { Test-Path $_ } | Select-Object -First 1 + (Get-Content $runtimeJson -Raw | ConvertFrom-Json).desktopVersion + ``` + + Первый путь — рантайм, скачанный харнессом отдельно; второй — рантайм, вложенный в установленное приложение (так на машине, где плагин писался). Второй путь ищется от `$env:LOCALAPPDATA`, поэтому при установке в другое место подставьте свой. + - Модель должна идти через **routerai**: стоимость приходит в ответе провайдера, другие провайдеры её не отдают, и ячейка просто молчит. - Ничего больше: ключ `ROUTERAI_API_KEY` плагину не нужен. Он читает стоимость из ответа, который харнесс и так получает, и сам никуда не ходит. ## Установка -Ниже — PowerShell. Путь к архиву подставьте свой. +Ставим **в профиль `desktop`** — это профиль, который поднимает приложение. + +Самый короткий путь — одна команда `dsh plugin`: она и ставит пакет, и дописывает его в список бандлов профиля (проверено: после неё `dsh-session-cost` уже в `dsh.profile.bundles`). ```powershell -$node = "$env:USERPROFILE\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe" -$pnpm = "$env:USERPROFILE\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\pnpm\bin\pnpm.mjs" +# dsh.cmd лежит рядом с установленным приложением; ищем, не угадывая путь +$dsh = @( + "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources\runtime\cli\bin\dsh.cmd", + "C:\Program Files\DeepSeek Harness\resources\runtime\cli\bin\dsh.cmd" +) | Where-Object { Test-Path $_ } | Select-Object -First 1 +$tgz = "C:\путь\к\dsh-session-cost-0.1.0.tgz" + +& $dsh plugin --profile desktop add "file:$tgz" +``` + +Оба пути — рядом с `DeepSeek Harness.exe`: установщик кладёт приложение либо в `%LOCALAPPDATA%\Programs`, либо в `C:\Program Files`, смотря как ставили. + +Профиль `desktop` принадлежит приложению, поэтому обычный запуск и дамп конфигурации по нему закрыты: `dsh --profile desktop --dump-config` отвечает `error: profile "desktop" is managed exclusively by the Electron application`. Это ограничение на **запуск**, а не на управление пакетами: `dsh plugin --profile desktop ...` в поставке десктопа разрешён и работает. + +### Если ставите pnpm напрямую + +Штатный `dsh plugin` — обёртка над pnpm, и разница только в одном: **он же ведёт список бандлов**. Голый pnpm список не трогает, поэтому после него шаг со списком обязателен. + +```powershell +$node = "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources\runtime\primary-runtime\dependencies\node\bin\node.exe" +$pnpm = "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources\runtime\primary-runtime\dependencies\pnpm\bin\pnpm.mjs" $profile = "$env:USERPROFILE\.dsh\profiles\desktop" $tgz = "C:\путь\к\dsh-session-cost-0.1.0.tgz" # 1. поставить пакет в профиль & $node $pnpm add "file:$tgz" --dir $profile -``` -Дальше **обязательный** шаг — добавить плагин в композицию. Без него пакет лежит в профиле, но не загружается: именно так сейчас обстоит дело с `dsh-bottom-info-bar`, который у вас установлен, но не активен. - -```powershell -# 2. добавить в список бандлов -$manifestPath = "$profile\package.json" +# 2. дописать плагин в список бандлов (голый pnpm этого не делает) +$manifestPath = Join-Path $profile 'package.json' $m = Get-Content $manifestPath -Raw | ConvertFrom-Json if ($m.dsh.profile.bundles -notcontains 'dsh-session-cost') { $m.dsh.profile.bundles += 'dsh-session-cost' @@ -34,13 +62,39 @@ if ($m.dsh.profile.bundles -notcontains 'dsh-session-cost') { $m | ConvertTo-Json -Depth 10 | Set-Content $manifestPath -Encoding utf8 ``` -Проще и надёжнее — сделать это через UI: **Plugins** в боковой панели, найти `dsh-session-cost` и включить переключателем. Тумблер сам правит список бандлов. +Без шага 2 пакет лежит в профиле и не загружается. Ровно в таком состоянии на машине, где плагин писался, оказался `dsh-bottom-info-bar`: установлен, но не активен. -**3. Перезапустить DSH.** Плагины собираются при старте хоста; перезагрузки страницы недостаточно. +Тумблером это тоже делается: **Plugins** в боковой панели → `dsh-session-cost` → включить. Страница правит тот же список бандлов (её host-половина `plugin-manager` на десктопе включена, потому что он передаёт `profileContext`). + +## `file:` и `link:` — чем отличаются на Windows + +Формат записи в `package.json` профиля решает, как пакет попадёт в `node_modules`, и от этого зависит, что будет при правке исходника. + +- **`file:`** (так ставится `.tgz` и так стояло на машине-источнике изначально). pnpm кладёт содержимое в свой store и делает **хардлинки** в `node_modules` профиля. Это не копия: у файлов один и тот же File ID, поэтому правка **на месте** видна в профиле сразу, без переустановки. Но атомарная замена файла (сохранение через временный файл и `rename` — так делают многие редакторы, `pnpm pack` и инструменты вроде нашего) **рвёт хардлинк**: в профиле остаётся старая версия, и нужна переустановка. Проверить, жив ли ещё линк: + + ```powershell + fsutil hardlink list "$env:USERPROFILE\.dsh\profiles\desktop\node_modules\dsh-session-cost\lib\index.js" + ``` + + Два пути в выводе — линк цел; один — в профиле уже отдельная копия. + +- **`link:`** — junction на папку с исходником. Правки видны всегда, чем бы их ни делали, переустановка не нужна вовсе. Цена: pnpm предупреждает, что `peerDependencies` (у плагина это `react`) из целевой папки не разрешатся, — для клиентской половины это неважно, `react` подставляет модульная система браузера. Плагин в профиле `desktop` стоит именно так, чтобы правки `lib/` доезжали до харнесса без переустановки. + +Живой линк ставится так (именно так плагин стоит на машине-источнике сейчас): + +```powershell +& $node $pnpm add "link:C:\Users\anton\src\dsh-cost-plugin" --dir $profile +``` + +## Нужен ли перезапуск + +На десктопе — **нет**. Базовый бандл включает `hmr`, когда приложение передаёт `profileContext`, а `dsh-hmr` следит за манифестом профиля и обоими файлами патчей: правка `dsh.profile.bundles` пересобирает дерево на ходу, и клиентская половина подхватывается открытой страницей через HMR-транспорт. Проверено: приложение стартовало в 16:36, пакет встал в 16:41, ячейка появилась в том же процессе. + +Перезапуск нужен, если HMR выключен — в бандлах `headless`, `sdk` и `acp` он погашен в YAML, и там же, где профиль правили руками без пересборки. Перезагрузки одной страницы в этом случае недостаточно: плагины собираются при старте хоста. ## Проверка -После перезапуска открыть любой диалог, отправить сообщение. В нижнем ряду, сразу после родных ячеек, должна появиться сумма вида `₽ 0,1234`. +Открыть любой диалог, отправить сообщение. В нижнем ряду, сразу после родных ячеек, должна появиться сумма вида `₽ 0,1234`. Если пусто, открыть консоль (F12) и посмотреть строки `[dsh-session-cost]`: @@ -59,16 +113,16 @@ $m | ConvertTo-Json -Depth 10 | Set-Content $manifestPath -Encoding utf8 ## Как обновить плагин -Собрать новый архив здесь (`pnpm pack --pack-destination dist`), перенести, повторить шаг 1 и перезапустить. +Собрать новый архив здесь (`pnpm pack --pack-destination dist`), перенести, повторить установку. -Если на другой машине плагин ставился **ссылкой** на папку (`link:`), как на этой, то обновление не нужно вовсе: правится исходник, и после перезапуска подхватывается новое. +Если плагин стоит **ссылкой** (`link:`), обновление не нужно: правится исходник, и после ближайшей пересборки дерева подхватывается новое. Если он стоит через `file:`, правка исходника доедет только при записи на месте — после атомарной замены файла переустановите пакет. ## Как удалить ```powershell -& $node $pnpm remove dsh-session-cost --dir $profile +& $dsh plugin --profile desktop remove dsh-session-cost ``` -Затем убрать `dsh-session-cost` из `dsh.profile.bundles` (или выключить тумблером в Plugins) и перезапустить. +Штатная команда убирает и пакет, и запись из `dsh.profile.bundles` (проверено). Если ставили голым pnpm — уберите `dsh-session-cost` из списка вручную или выключите тумблером в Plugins. Реестр `~/.dsh/dsh-session-cost/` при удалении остаётся, как и у `dsh-bottom-info-bar`; удалите папку вручную, если хотите чистый лист. diff --git a/docs/superpowers/specs/2026-10-15-session-cost-bar-design.md b/docs/superpowers/specs/2026-10-15-session-cost-bar-design.md index 15fdf40..6f31bf9 100644 --- a/docs/superpowers/specs/2026-10-15-session-cost-bar-design.md +++ b/docs/superpowers/specs/2026-10-15-session-cost-bar-design.md @@ -197,14 +197,16 @@ Cannot assign to read only property 'url' of object '#<_Response>' **Зависимостей от других плагинов нет.** -**Установка.** Менеджер плагинов принимает локальный путь как спецификацию (`inspect` читает `package.json` по абсолютному пути; GitHub-проверка для путей пропускается). Установка идёт через страницу плагинов в приложении: профиль рабочего стола управляется приложением, из CLI в него ставить нельзя. Ставится долгой командой референс-плагина, форма та же. +**Установка.** Менеджер плагинов принимает локальный путь как спецификацию (`inspect` читает `package.json` по абсолютному пути; GitHub-проверка для путей пропускается). Ставить можно и со страницы плагинов в приложении, и из CLI: `dsh plugin --profile desktop add ` в десктопной поставке разрешён (запуск и `--dump-config` по этому профилю закрыты, управление пакетами — нет). Ровно эта команда и была использована. + +*Поправка после установки (2026-10-28).* Здесь стояло «из CLI в него ставить нельзя» — неверно: launcher отбивает `desktop` только в режиме запуска, а `plugin` пропускает, если приложение передало `manageDesktopProfile`. Два требования, которые надо соблюсти, иначе установка откажется: -- пакет должен объявить совместимость с рантаймом (`peerDependencies` на DSH) — версия рабочего рантайма **0.2.0-rc.2**; +- пакет должен объявить совместимость с рантаймом (`peerDependencies` на DSH) — версия рабочего рантайма при установке была **0.2.1-rc.3**, и пакет прошёл проверку без исключений в `compatibility.json`; - пакет должен объявлять бандл (`dsh.bundle.patch`), иначе установка сообщит `not-a-bundle`. -**Про перезапуск.** Плагины собираются при старте хоста. Если HMR в профиле не включён, уже запущенная композиция остаётся до перезапуска — перезагрузки страницы недостаточно. В `cordis.yml` рабочего профиля HMR не упомянут, поэтому исходить надо из того, что **потребуется перезапуск приложения**, а он прервёт текущий разговор. Порядок такой: сначала собрать и проверить пакет, перезапуск — последним шагом. +**Про перезапуск.** *Поправка после установки (2026-10-28).* Здесь стояло «потребуется перезапуск приложения» — на десктопе не потребовался. Базовый бандл включает `hmr` при живом `profileContext` (а приложение его передаёт), `dsh-hmr` следит за манифестом профиля, и правка `dsh.profile.bundles` пересобирает дерево на ходу; клиентская половина доезжает до открытой страницы по HMR-транспорту. Проверено: приложение стартовало в 16:36, пакет встал в 16:41, ячейка появилась в том же процессе. Перезапуск нужен только там, где HMR погашен (бандлы `headless`, `sdk`, `acp`) или профиль правили в обход пересборки; перезагрузки страницы в этом случае действительно недостаточно. ## Что остаётся сборщику @@ -218,7 +220,7 @@ Cannot assign to read only property 'url' of object '#<_Response>' ## Как проверять -**Готовые инструменты** (лежат в `.recon/`, запускаются встроенным Node из `C:\Users\Anton\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe`): +**Готовые инструменты** (лежат в `.recon/`, запускаются встроенным Node из `C:\Users\Anton\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe`; на машине, где ставился плагин, такого пути нет — рантайм вложен в приложение, см. `INSTALL.md`): | Файл | Что делает | |---|---| @@ -244,6 +246,24 @@ Cannot assign to read only property 'url' of object '#<_Response>' ## Решённое -**Установка:** владелец выбрал ставить плагин в рабочий харнесс. Потребуется запись за пределами рабочей песочницы (в `~/.dsh/profiles/desktop`) и перезапуск приложения. +**Установка:** владелец выбрал ставить плагин в рабочий харнесс. Запись идёт в `~/.dsh/profiles/desktop`; перезапуск приложения, как выяснилось при установке, не требуется — HMR на десктопе включён (см. поправку выше). -**Git:** владелец выбрал завести локальный репозиторий. Заведён в `C:\Users\Anton\Documents\src\costs-plugin`, дизайн и спайки закоммичены. +**Git:** владелец выбрал завести локальный репозиторий. Заведён в `C:\Users\Anton\Documents\src\costs-plugin`, дизайн и спайки закоммичены; позже перенесён на `git.trtd.pro/antonov/dsh-cost-plugin`. + +## Как это встало на самом деле (2026-10-28) + +Записано, чтобы следующая установка не повторяла проверок заново. + +| Что проверяли | Итог | +|---|---| +| `dsh plugin --profile desktop add file:` | работает; сам дописывает бандл в `dsh.profile.bundles` | +| `dsh plugin --profile desktop remove …` | работает; сам убирает запись из списка бандлов | +| `dsh --profile desktop --dump-config` | отказ: `profile "desktop" is managed exclusively by the Electron application` | +| голая `pnpm add --dir …` | ставит пакет, но список бандлов не трогает — шаг со списком обязателен | +| `file:` на Windows | pnpm делает **хардлинки** из store: правка на месте видна, атомарная замена файла рвёт линк | +| `link:` на Windows | junction на исходник: правки видны всегда; pnpm предупреждает про `peerDependencies` (у нас `react`), для браузерной половины это неважно | +| перезапуск приложения | не потребовался: `hmr` включён через `profileContext`, ячейка появилась в работающем процессе | +| тумблер в Plugins | доступен: host-строка `plugin-manager` включается тем же `profileContext` | +| `plugin-manager`/`tool-plugin-manager` в `dsh-base` | `plugin-manager` — `!!js "!ctx.get('profileContext')"`, `tool-plugin-manager` — всегда `disabled: true` | + +Открытым осталось расхождение валюты: хост кладёт в ответ `currency: 'RUB'`, а клиент печатает `\u20BD` жёстко и `currency` не читает. Пока провайдер один, это незаметно; при втором — цифра будет подписана неверно.