dsh-bash-terminal-ts 0.7.0 → 0.7.1

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.ru.md ADDED
@@ -0,0 +1,205 @@
1
+ # dsh-bash-terminal-ts
2
+
3
+ > 🌐 [简体中文](README.md) | [English](README.en.md) | [Français](README.fr.md) | [Deutsch](README.de.md) | [Italiano](README.it.md) | [Русский](README.ru.md) | [Español](README.es.md) · Сообщество: [LINUX DO](https://linux.do) · [GitHub](https://github.com/drscrewdriver/dsh-bash-terminal-ts)
4
+
5
+ ![test](https://github.com/drscrewdriver/dsh-bash-terminal-ts/actions/workflows/test.yml/badge.svg)
6
+
7
+ Плагин DSH (DeepSeek Harness): инструмент `shell`, единообразно выполняющий в Windows команды **четырёх** терминалов — **PowerShell / Git Bash / MSYS2 / WSL**.
8
+
9
+ > **Этот репозиторий — переписанный на TypeScript вариант `MAXeaglet/dsh-bash-terminal`** с работающей поддержкой MSYS2/MINGW64. Линии совместимости выделяются по сегменту версии хоста DSH; все линии используют одно имя пакета `dsh-bash-terminal-ts` и различаются npm dist-tag'ами:
10
+ >
11
+ > | Сегмент DSH | Ветка | Версия плагина | npm dist-tag |
12
+ > |--------|------|----------|--------------|
13
+ > | `>=0.2.0-rc.1 <0.2.1-0` | `compat/0.2.0` (эта ветка) | 0.7.x | `dsh-0.2.0` |
14
+ > | 0.1.7-rc.1 – 0.1.7.x | `compat/0.1.7` | 0.6.x | `dsh-0.1.7` |
15
+ > | 0.1.5-alpha.1 – 0.1.5-rc.x | `ts/0.1.5` (историческая, заморожена) | ≤ 0.5.2 | `dsh-0.1.5` |
16
+ > | 0.1.2-alpha.1 – 0.1.2-rc.x | `main` (историческая, заморожена) | ≤ 0.4.2 | `dsh-0.1.2` |
17
+ >
18
+ > Выбирайте линию под вашу версию DSH; серии версий не пересекаются, поэтому установка с `^` не сможет разрешиться через границы линий.
19
+
20
+ | Бэкенд | Фактическое выполнение | Синтаксис / пути | Переменные окружения |
21
+ |------|----------|-------------|----------|
22
+ | `powershell` (по умолчанию) | `pwsh -NoLogo -NoProfile -NonInteractive -Command <cmd>` | PowerShell; `C:\...` | `$env:NAME` |
23
+ | `gitbash` | `bash -lc <cmd>` из Git for Windows | POSIX; `/d/workspace`; PATH включает `/usr/bin` и `/mingw64/bin` | `$NAME` |
24
+ | `msys2` | `bash -lc <cmd>` MSYS2 (`C:\msys64\usr\bin\bash.exe`) | POSIX; `/c/...`; PATH включает `/usr/bin` и `/mingw64/bin` (gcc / make) | `$NAME` (автоматически подставляется `MSYSTEM=MINGW64`) |
25
+ | `wsl` | `wsl [-d <distro>] -e bash -lc <cmd>` | Linux; `/mnt/d/...` | `$NAME` (через WSLENV) |
26
+
27
+ Каждый вызов запускает совершенно новую оболочку: **состояние не сохраняется** (cwd / переменные / алиасы) — передавайте `workdir`, а не используйте `cd`.
28
+
29
+ ## Предпросмотр интерфейса
30
+
31
+ Строка «Терминал по умолчанию» в Настройки → Общие: пользователь выбирает между PowerShell / Git Bash / MSYS2 / WSL, а инструмент `shell` выполняется строго согласно этой настройке — модель не может её обойти:
32
+
33
+ ![Строка настройки терминала по умолчанию](assets/shells.png)
34
+
35
+ ## Ключевые решения дизайна
36
+
37
+ - **Терминал выбирает пользователь, ИИ не может его сменить**: на странице настроек веб-UI (Настройки → Общие) появляется выпадающий список «Терминал по умолчанию» (PowerShell / Git Bash / MSYS2 / WSL); инструмент `shell` всегда использует только эту настройку и не раскрывает модели параметров терминала. Настройка сохраняется через систему settings DSH (settings.yaml).
38
+ - **Не занимает стык возможностей `ctx.shell`**: штатный sandbox-инструмент `pwsh` из DSH остаётся доступным как есть; инструмент `shell` этого плагина — **дополнительная** точка входа к нескольким терминалам.
39
+ - Процессы порождаются через общий seam `ctx.subprocess`: завершение дерева процессов (в Windows `taskkill /T`), SIGTERM → льготный период → SIGKILL, spill-файлы вывода — поведение идентично официальным инструментам `dsh-tool-bash` / `dsh-tool-pwsh`.
40
+ - Фоновые задачи регистрируются в универсальном реестре `jobs` с поддержкой `run_in_background` / `job_output` / `job_kill`.
41
+ - Строка «Терминал по умолчанию» на странице настроек фронтенда — это перечисление (UI автоматически рендерит его выпадающим списком); при каждом вызове модель исполняет только согласно этой настройке и не может сменить терминал по собственной инициативе.
42
+
43
+ ## Установка (web-профиль)
44
+
45
+ ### Стандартная установка (после публикации в npm, официальный механизм bundle)
46
+
47
+ Плагин несёт собственный официальный манифест `dsh.bundle` (`cordis.patch.yml` внутри пакета); как только профиль перечисляет этот пакет, DSH **автоматически применяет монтирование**, вручную править конфигурацию профиля не нужно:
48
+
49
+ ```powershell
50
+ # 1. Установить пакет плагина
51
+ npm install -g dsh-bash-terminal-ts
52
+ dsh plugin --profile web add dsh-bash-terminal-ts # автоматически добавляется в bundles профиля и применяет патч
53
+
54
+ # 2. Пропатчить whitelist настроек DSH (ограничение DSH, см. пояснение ниже)
55
+ powershell -ExecutionPolicy Bypass -File install.ps1 install
56
+
57
+ # 3. Перезапустить dsh web
58
+ ```
59
+
60
+ > Проверено на практике с временным профилем: `bundles: [dsh-bash-terminal-ts]` → запись `tool-bash-terminal` автоматически появляется в dump-config.
61
+
62
+ ### Установка для локальной разработки (junction, изменения исходников вступают в силу сразу)
63
+
64
+ ```powershell
65
+ # 1. Связать пакет плагина с node_modules профиля (junction, изменения исходников вступают в силу сразу)
66
+ $profile = "$env:USERPROFILE\.dsh\profiles\web"
67
+ New-Item -ItemType Junction -Path "$profile\node_modules\dsh-bash-terminal-ts" -Target "D:\workspace\projects\dsh-bash-terminal-ts" | Out-Null
68
+
69
+ # 2. Позволить плагину разрешать зависимости @deepseek-ai/* (junction на дерево зависимостей профиля; плагин и хост используют одни и те же экземпляры модулей)
70
+ New-Item -ItemType Junction -Path "D:\workspace\projects\dsh-bash-terminal-ts\node_modules\@deepseek-ai" -Target "$profile\..\node_modules\@deepseek-ai" | Out-Null
71
+
72
+ # 3. Дать профилю смонтировать плагин через официальный bundle (install.ps1 install делает это автоматически; эквивалентно добавлению "dsh-bash-terminal-ts" в dsh.profile.bundles)
73
+ # 4. (только после правок фронтенд-кода) пересобрать клиентский bundle:
74
+ # cd D:\workspace\projects\dsh-bash-terminal-ts && node scripts/build-client.mjs
75
+ # 5. Перезапустить dsh web
76
+ ```
77
+
78
+ > ⚠️ **Не запускайте `npm install` в этом проекте**: он удалит junction из шага 2 выше и установит для плагина **независимую** копию
79
+ > `@deepseek-ai/*` — плагин и хост перестанут использовать общие экземпляры модулей, и после обновления хоста плагин застрянет на старом API
80
+ > (этот проект однажды так застрял на 0.1.0-rc.6). Чтобы обновить только lock, используйте `npm install --package-lock-only`.
81
+
82
+ > **Совместимость**: требуется DSH ≥ **0.1.7-rc.1** (линия `compat/0.1.7`) или ≥ **0.2.0-rc.1** (линия `compat/0.2.0`, эта ветка).
83
+ > Историческая справка: в 0.1.5 `@deepseek-ai/dsh-client-runtime` в таблице клиентских модулей был переименован в `@deepseek-ai/dsh-client-store` и разрешается только по точному «голому» имени; старые bundle на новом хосте падают с
84
+ > `Failed to load plugins` / `require(...) missed the module table`.
85
+ >
86
+ > Кроме того: с 0.1.5 веб-поверхность настроек перешла на динамическое перечисление `settings.describe()`, **whitelist пространств имён больше не существует**
87
+ > (`settings-not-exposed` упразднён), а whitelist-патч в `install.ps1` — лишь исторический остаток, его можно игнорировать.
88
+
89
+ > Теперь вручную править `cordis.patch.yml` профиля не нужно: пакет плагина несёт собственный `dsh.bundle.patch` (`cordis.patch.yml` внутри пакета); достаточно, чтобы `dsh-bash-terminal-ts` был указан в `dsh.profile.bundles` профиля — DSH смонтирует плагин автоматически.
90
+
91
+ Проверить собранное дерево (без перезапуска):
92
+
93
+ ```powershell
94
+ node "$env:APPDATA\nvm\<node-version>\node_modules\@deepseek-ai\dsh\lib\bin.js" --profile web --dump-config | Select-String dsh-bash-terminal-ts
95
+ ```
96
+
97
+ ## Использование
98
+
99
+ **Пользователь задаёт терминал по умолчанию в веб-UI**: откройте настройки (шестерёнка) → Общие → выпадающий список «Терминал по умолчанию» и выберите один из PowerShell / Git Bash / MSYS2 / WSL. Изменение вступает в силу сразу и сохраняется.
100
+
101
+ Увидев инструмент `shell`, модель автоматически использует выбранный вами терминал (инструмент не раскрывает параметров терминала, модель не может изменить ваш выбор):
102
+
103
+ - Терминал по умолчанию = Git Bash: `shell(command: "git status")` идёт через Git Bash
104
+ - Терминал по умолчанию = MSYS2: `shell(command: "gcc --version")` идёт через MSYS2 (окружение MINGW64, доступны gcc и make из `/mingw64/bin`)
105
+ - Терминал по умолчанию = WSL: `shell(command: "ls -la /mnt/d/workspace")` идёт через WSL; `distro: "Ubuntu"` позволяет указать дистрибутив
106
+ - Терминал по умолчанию = PowerShell: `shell(command: "Get-Process node")` идёт через PowerShell
107
+
108
+ ## Примеры использования моделью
109
+
110
+ - Разовая команда (терминал по умолчанию): `shell(command: "git status", description: "посмотреть статус git")`
111
+ - Сохранение состояния между ходами (интерактивно): `terminal(action: "open")` → запишите `sessionId` → `terminal(action: "send", sessionId, input: "cd /d/project\n")` → `terminal(action: "send", sessionId, input: "npm run dev\n")` → `terminal(action: "close", sessionId)`
112
+ - Прервать запущенную программу: `terminal(action: "signal", sessionId, signal: "SIGINT")`
113
+ - Посмотреть активные сессии: `terminal(action: "list")`
114
+ - Эскалация после отказа песочницы: `shell(command: ..., sandbox_permissions: "workspace-write", justification: "...")`
115
+
116
+ ## Конфигурация
117
+
118
+ **Настройки веб-UI** (рекомендуется): Настройки → Общие → «Терминал по умолчанию».
119
+
120
+ `config` строки плагина (переопределяет значения по умолчанию и служит базой композиции для настроек):
121
+
122
+ | Ключ | По умолчанию | Описание |
123
+ |----|------|------|
124
+ | `defaultShell` | `powershell` | Бэкенд, если настройки не переопределили |
125
+ | `timeoutMs` | 120000 | Таймаут по умолчанию |
126
+ | `maxTimeoutMs` | 600000 | Верхний предел timeoutMs вызывающего |
127
+ | `pwshPath` | автопоиск | Фиксированный путь к pwsh.exe |
128
+ | `gitBashPath` | автопоиск | Фиксированный путь к git bash.exe |
129
+ | `msys2Path` | автопоиск (сначала `C:\msys64\usr\bin\bash.exe`, запасной вариант `msys2.exe`) | Фиксированный путь к MSYS2 bash.exe |
130
+ | `wslPath` | автопоиск | Фиксированный путь к wsl.exe |
131
+
132
+ ## Публикация (npm)
133
+
134
+ У учётной записи npm включена проверка 2FA при публикации, нужен одноразовый код:
135
+
136
+ ```powershell
137
+ cd D:\workspace\projects\dsh-bash-terminal-ts
138
+ npm publish --otp <код> # код берётся из вашего аутентификатора
139
+ ```
140
+
141
+ Перед публикацией выполните `npm pack --dry-run` для проверки содержимого и запустите `npm run build` для полной пересборки (серверная компиляция tsc + клиентский bundle + компиляция тестов).
142
+
143
+ ## Удаление
144
+
145
+ Достаточно выполнить:
146
+
147
+ ```powershell
148
+ powershell -ExecutionPolicy Bypass -File install.ps1 uninstall
149
+ ```
150
+
151
+ Команда удаляет junction, восстанавливает whitelist настроек, вычищает оставшиеся от старых версий блоки монтирования `cordis.patch.yml` и убирает `dsh-bash-terminal-ts` из `dsh.profile.bundles`. После этого перезапустите dsh web.
152
+
153
+ При ручном удалении, помимо удаления `node_modules\dsh-bash-terminal-ts`, не забудьте также убрать `dsh-bash-terminal-ts` из `dsh.profile.bundles` в `package.json` профиля.
154
+
155
+ ## Интерактивный терминал (инструмент `terminal`)
156
+
157
+ Инструмент `terminal` предоставляет **постоянные интерактивные сессии** на стыке PTY (node-pty; в Windows из-за того, что process inspector вышестоящего `spawnTerminal` поддерживает только POSIX, `lib/terminal.js` подключается к node-pty напрямую; вне Windows по-прежнему используется официальный `ctx.subprocess.spawnTerminal`):
158
+
159
+ - `action: open` запускает настоящую терминальную сессию (согласно вашему терминалу по умолчанию; для wsl можно передать `distro`) и возвращает `sessionId`
160
+ - `action: send` записывает ввод и читает новый вывод; `action: read` только читает, не пишет; `action: signal` отправляет сигнал группе процессов переднего плана (SIGINT = Ctrl+C)
161
+ - `action: close` завершает сессию
162
+ - **Состояние сессии сохраняется между вызовами** (cwd / переменные / алиасы), подходит для REPL, ssh и интерактивных CLI
163
+ - `send` ждёт стабилизации вывода (300 мс тишины, предел 5 с) и возвращает **полный ответ**; при выводе свыше 1 МБ выдаётся пометка `truncated`
164
+ - Ввод завершается `\\n` (или \\r), что означает нажатие Enter
165
+
166
+ ## Песочница (сопряжение с официальным механизмом)
167
+
168
+ Инструмент `shell` идёт через официальный стык песочницы DSH (`ctx.sandboxPolicy` + `ctx.sandbox`):
169
+
170
+ - При каждом вызове разрешается текущая политика песочницы; сессии `danger-full-access` выполняются напрямую (без обёртки).
171
+ - Бэкенд PowerShell оборачивает argv через `ctx.sandbox.confine` — та же **fail-closed**-семантика, что у штатных исполнителей: если запрошен ограниченный режим, а доступного бэкенда нет, выбрасывается `SandboxUnavailableError`, «голый» запуск отвергается.
172
+ - Бэкенд Git Bash не оборачивается: restricted-token runner с Windows ACL у DSH несовместим с Cygwin/MSYS2 (bash падает сразу при старте с ошибкой Win32 5 от `CreateFileMapping`), поэтому Git Bash работает без sandbox-обёртки и в ограниченном режиме; результат сообщает `enforcement: gitbash-unconfined`.
173
+ - Бэкенд MSYS2 тоже не оборачивается (та же несовместимость рантайма Cygwin/MSYS2); результат сообщает `enforcement: msys2-unconfined`.
174
+ - Бэкенд WSL не оборачивается: отдельная виртуальная машина Linux у WSL сама по себе является изоляцией (результат сообщает `enforcement: wsl-isolation`).
175
+ - При отказе песочницы в ограниченном режиме результат несёт официальный маркер `[sandbox: file access denied under <mode> mode]` и подсказку об эскалации в тот же ход; модель может инициировать одну эскалацию с `sandbox_permissions` + `justification` (одобрение пользователем через `ctx.approval`) — в точности как официальные инструменты bash/pwsh.
176
+ - Примечание: когда runner Windows ACL у DSH доступен, ограниченный режим PowerShell оборачивается через него; Git Bash и MSYS2 из-за несовместимости с Cygwin/MSYS2 остаются без обёртки.
177
+
178
+ ## ⚠️ Замечания по безопасности
179
+
180
+ Инструмент `shell` в ограниченном режиме: PowerShell оборачивается через `ctx.sandbox.confine` (fail-closed); Git Bash и MSYS2 из-за несовместимости Cygwin/MSYS2 с restricted-token Windows ACL **не оборачиваются** (те же права, что у процесса dsh); WSL не оборачивается благодаря отдельной Linux-VM. Это **дополнительная точка входа к нескольким терминалам**, она не подпадает под ограничения ConstrainedLanguage штатного инструмента `pwsh`. Инструменты работы с файлами DSH (read/write/edit) по-прежнему ограничены файловой песочницей. Используйте только в доверенных сессиях; для PowerShell под защитой песочницы продолжайте пользоваться штатным инструментом `pwsh`.
181
+
182
+ ## Известные ограничения интерактивного терминала (ConPTY)
183
+
184
+ - **PowerShell 5.1 не может запуститься в ConPTY** (0x8009001d) — для интерактивного PowerShell установите [PowerShell 7](https://github.com/PowerShell/PowerShell/releases) (разовые команды не затронуты).
185
+ - **Интерактивный режим wsl.exe под ConPTY может вызвать RPC-ошибку службы WSL** (0x8007072c, спорадически) — разовые команды `wsl -e bash -lc ...` работают нормально; для интерактивных сессий лучше использовать сам Windows Terminal / терминал WSL либо повторить попытку.
186
+ - **node-pty в Windows не принимает именованные сигналы**: `SIGINT` у `signal` отображается на Ctrl+C (`\x03`); прочие сигналы (`SIGTERM` / `SIGKILL` / `SIGTSTP` / `SIGHUP`) деградируют до завершения сессии.
187
+ - Интерактивные сессии Git Bash работают полностью штатно.
188
+
189
+ ## Известные ограничения
190
+
191
+ - Фоновые процессы WSL после таймаута/прерывания могут недолгое время оставаться внутри дистрибутива (инстанс WSL автоматически гаснет после завершения последнего процесса).
192
+ - Git Bash — это среда msys2, она отличается от Linux-поведения WSL (отображение путей, доступность пакетов).
193
+ - Бэкенду MSYS2 нужна локально установленная MSYS2 (по умолчанию `C:\msys64`); без установки `shell` сообщает `backend unavailable`, а `msys2Path` позволяет указать произвольное расположение. Порядок кандидатов всегда таков: сначала `bash.exe`, `msys2.exe` — запасной вариант; при stdio через конвейер `msys2.exe` молча возвращает ноль байт и годится лишь как последнее средство.
194
+ - Этот плагин регистрирует инструменты только на платформе `win32`.
195
+
196
+ ## Тесты
197
+
198
+ ```powershell
199
+ cd D:\workspace\projects\dsh-bash-terminal-ts
200
+ npm install # установить зависимости (включая typescript)
201
+ npm run build # компиляция tsc: src/*.ts → lib/*.js; client.tsx → lib/client.js + dist/client.js; test/*.ts → test-dist/
202
+ npm test # node test-dist/unit.js → apply.js → client.js → terminal.js
203
+ ```
204
+
205
+ Исходный код написан на TypeScript (`strict` + `noUncheckedIndexedAccess`); артефакты сборки `lib/` и `dist/` коммитятся вместе с репозиторием, DSH загружает сразу `lib/index.js` — можно пользоваться без установки.
@@ -1,151 +1,196 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __export = (target, all) => {
6
- for (var name in all)
7
- __defProp(target, name, { get: all[name], enumerable: true });
8
- };
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
16
- };
17
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
-
19
- // src/client.tsx
20
- var client_exports = {};
21
- __export(client_exports, {
22
- apply: () => apply,
23
- inject: () => inject
24
- });
25
- module.exports = __toCommonJS(client_exports);
26
- var import_react = require("react");
27
- var import_dsh_client_store = require("@deepseek-ai/dsh-client-store");
28
- var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
29
- var import_jsx_runtime = require("react/jsx-runtime");
30
- var SETTINGS_NS = "settings.bash-terminal";
31
- var ENTRY_ID = "tool-bash-terminal";
32
- var SHELLS = ["powershell", "gitbash", "msys2", "wsl"];
33
- var ROW_CSS = ".btRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.btRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.btTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.btDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.btSelector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.btSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}.btChevron{flex:none}";
34
- if (typeof document !== "undefined" && document.querySelector('style[data-plugin-css="bash-terminal-row"]') === null) {
35
- const tag = document.createElement("style");
36
- tag.dataset.plugin = "dsh-bash-terminal-ts";
37
- tag.dataset.pluginCss = "bash-terminal-row";
38
- tag.textContent = ROW_CSS;
39
- document.head.appendChild(tag);
40
- }
41
- var zh = {
42
- "shell.title": "\u9ED8\u8BA4\u7EC8\u7AEF",
43
- "shell.description": "shell \u5DE5\u5177\u6267\u884C\u547D\u4EE4\u65F6\u4F7F\u7528\u7684\u7EC8\u7AEF",
44
- "shell.powershell": "PowerShell",
45
- "shell.gitbash": "Git Bash",
46
- "shell.msys2": "MSYS2",
47
- "shell.wsl": "WSL",
48
- "shell.unavailable": "\u8BBE\u7F6E\u9762\u4E0D\u53EF\u7528\uFF1A\u5BBF\u4E3B\u672A\u63D0\u4F9B\u672C\u63D2\u4EF6\u7684 configForms \u6761\u76EE\uFF08\u68C0\u67E5 cordis.patch.yml \u7684 entry id\uFF09"
49
- };
50
- var en = {
51
- "shell.title": "Default terminal",
52
- "shell.description": "Terminal used by the shell tool",
53
- "shell.powershell": "PowerShell",
54
- "shell.gitbash": "Git Bash",
55
- "shell.msys2": "MSYS2",
56
- "shell.wsl": "WSL",
57
- "shell.unavailable": "Settings unavailable: the host exposes no configForms entry for this plugin (check the cordis.patch.yml entry id)"
58
- };
59
- var inject = ["slots", "locale", "configForms"];
60
- function ShellPreferenceRow({ t, useStore, setShell }) {
61
- const shell = useStore((s) => s.shell);
62
- const writable = useStore((s) => s.writable);
63
- const status = useStore((s) => s.status);
64
- const [open, setOpen] = (0, import_react.useState)(false);
65
- const items = SHELLS.map((id) => ({ id, label: t("shell." + id) }));
66
- if (status === "unavailable") {
67
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btRow", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "btRowText", children: [
68
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btTitle", children: t("shell.title") }),
69
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btDesc", children: t("shell.unavailable") })
70
- ] }) });
71
- }
72
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "btRow", children: [
73
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "btRowText", children: [
74
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btTitle", children: t("shell.title") }),
75
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btDesc", children: t("shell.description") })
76
- ] }),
77
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
78
- import_dsh_client_ui_primitives.Menu,
79
- {
80
- open,
81
- onClose: () => setOpen(false),
82
- items,
83
- selectedId: shell,
84
- onSelect: (id) => {
85
- setOpen(false);
86
- setShell(id);
87
- },
88
- align: "end",
89
- portal: true,
90
- anchor: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
91
- "button",
92
- {
93
- type: "button",
94
- className: "btSelector",
95
- "aria-haspopup": "menu",
96
- "aria-expanded": open,
97
- disabled: !writable,
98
- onClick: () => setOpen(!open),
99
- style: !writable ? { opacity: 0.5, cursor: "not-allowed" } : void 0,
100
- children: [
101
- t("shell." + shell),
102
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutlineMedium, { className: "btChevron" })
103
- ]
104
- }
105
- )
106
- }
107
- )
108
- ] });
109
- }
110
- var zhDictTitle = () => zh["shell.title"] ?? "\u7EC8\u7AEF";
111
- function apply(ctx) {
112
- ctx.effect(() => ctx.locale.register(SETTINGS_NS, { zh, en }), "bash-terminal: settings dictionaries");
113
- const scope = ctx.configForms.get(ENTRY_ID);
114
- const store = (0, import_dsh_client_store.defineStore)({
115
- init: () => ({ shell: "powershell", revision: -1, writable: false, status: "loading" }),
116
- actions: {
117
- sync: (d, shell, revision, writable, status) => {
118
- if (revision !== void 0 && revision <= d.revision) return;
119
- if (shell !== void 0) d.shell = shell;
120
- if (revision !== void 0) d.revision = revision;
121
- if (writable !== void 0) d.writable = writable;
122
- if (status !== void 0) d.status = status;
123
- }
124
- }
125
- });
126
- let bound;
127
- const push = (snap) => {
128
- bound?.sync(snap.value?.defaultShell, snap.revision, snap.writable, snap.status);
129
- };
130
- ctx.slots.inject(
131
- "dsh-family.tab",
132
- () => ctx.slots.register(
133
- {
134
- name: "dsh-family.tab",
135
- id: "bash-terminal-shell",
136
- order: 60,
137
- label: () => zhDictTitle(),
138
- store,
139
- locale: SETTINGS_NS,
140
- inject: (actions) => {
141
- bound = actions;
142
- push(scope.getSnapshot());
143
- return { setShell: (value) => void scope.set("defaultShell", value) };
144
- }
145
- },
146
- ShellPreferenceRow
147
- ),
148
- "bash-terminal: settings row"
149
- );
150
- ctx.effect(() => scope.subscribe(() => push(scope.getSnapshot())), "bash-terminal: settings watch");
151
- }
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/client.tsx
20
+ var client_exports = {};
21
+ __export(client_exports, {
22
+ apply: () => apply,
23
+ inject: () => inject
24
+ });
25
+ module.exports = __toCommonJS(client_exports);
26
+ var import_react = require("react");
27
+ var import_dsh_client_store = require("@deepseek-ai/dsh-client-store");
28
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
29
+ var import_jsx_runtime = require("react/jsx-runtime");
30
+ var SETTINGS_NS = "settings.bash-terminal";
31
+ var ENTRY_ID = "tool-bash-terminal";
32
+ var SHELLS = ["powershell", "gitbash", "msys2", "wsl"];
33
+ var ROW_CSS = ".btRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.btRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.btTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.btDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.btSelector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.btSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}.btChevron{flex:none}";
34
+ if (typeof document !== "undefined" && document.querySelector('style[data-plugin-css="bash-terminal-row"]') === null) {
35
+ const tag = document.createElement("style");
36
+ tag.dataset.plugin = "dsh-bash-terminal-ts";
37
+ tag.dataset.pluginCss = "bash-terminal-row";
38
+ tag.textContent = ROW_CSS;
39
+ document.head.appendChild(tag);
40
+ }
41
+ var zh = {
42
+ "shell.title": "\u9ED8\u8BA4\u7EC8\u7AEF",
43
+ "shell.description": "shell \u5DE5\u5177\u6267\u884C\u547D\u4EE4\u65F6\u4F7F\u7528\u7684\u7EC8\u7AEF",
44
+ "shell.powershell": "PowerShell",
45
+ "shell.gitbash": "Git Bash",
46
+ "shell.msys2": "MSYS2",
47
+ "shell.wsl": "WSL",
48
+ "shell.unavailable": "\u8BBE\u7F6E\u9762\u4E0D\u53EF\u7528\uFF1A\u5BBF\u4E3B\u672A\u63D0\u4F9B\u672C\u63D2\u4EF6\u7684 configForms \u6761\u76EE\uFF08\u68C0\u67E5 cordis.patch.yml \u7684 entry id\uFF09"
49
+ };
50
+ var en = {
51
+ "shell.title": "Default terminal",
52
+ "shell.description": "Terminal used by the shell tool",
53
+ "shell.powershell": "PowerShell",
54
+ "shell.gitbash": "Git Bash",
55
+ "shell.msys2": "MSYS2",
56
+ "shell.wsl": "WSL",
57
+ "shell.unavailable": "Settings unavailable: the host exposes no configForms entry for this plugin (check the cordis.patch.yml entry id)"
58
+ };
59
+ var fr = {
60
+ "shell.title": "Terminal par d\xE9faut",
61
+ "shell.description": "Terminal utilis\xE9 par l'outil shell",
62
+ "shell.powershell": "PowerShell",
63
+ "shell.gitbash": "Git Bash",
64
+ "shell.msys2": "MSYS2",
65
+ "shell.wsl": "WSL",
66
+ "shell.unavailable": "Param\xE8tres indisponibles : l'h\xF4te n'expose aucune entr\xE9e configForms pour ce plugin (v\xE9rifiez l'entry id du cordis.patch.yml)"
67
+ };
68
+ var de = {
69
+ "shell.title": "Standard-Terminal",
70
+ "shell.description": "Terminal, das vom Shell-Tool verwendet wird",
71
+ "shell.powershell": "PowerShell",
72
+ "shell.gitbash": "Git Bash",
73
+ "shell.msys2": "MSYS2",
74
+ "shell.wsl": "WSL",
75
+ "shell.unavailable": "Einstellungen nicht verf\xFCgbar: Der Host stellt keinen configForms-Eintrag f\xFCr dieses Plugin bereit (Entry-ID in cordis.patch.yml pr\xFCfen)"
76
+ };
77
+ var it = {
78
+ "shell.title": "Terminale predefinito",
79
+ "shell.description": "Terminale usato dallo strumento shell",
80
+ "shell.powershell": "PowerShell",
81
+ "shell.gitbash": "Git Bash",
82
+ "shell.msys2": "MSYS2",
83
+ "shell.wsl": "WSL",
84
+ "shell.unavailable": "Impostazioni non disponibili: l'host non espone alcuna voce configForms per questo plugin (verifica l'entry id in cordis.patch.yml)"
85
+ };
86
+ var ru = {
87
+ "shell.title": "\u0422\u0435\u0440\u043C\u0438\u043D\u0430\u043B \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E",
88
+ "shell.description": "\u0422\u0435\u0440\u043C\u0438\u043D\u0430\u043B, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u043C\u044B\u0439 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u043C shell",
89
+ "shell.powershell": "PowerShell",
90
+ "shell.gitbash": "Git Bash",
91
+ "shell.msys2": "MSYS2",
92
+ "shell.wsl": "WSL",
93
+ "shell.unavailable": "\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B: \u0445\u043E\u0441\u0442 \u043D\u0435 \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u0437\u0430\u043F\u0438\u0441\u044C configForms \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043F\u043B\u0430\u0433\u0438\u043D\u0430 (\u043F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 entry id \u0432 cordis.patch.yml)"
94
+ };
95
+ var es = {
96
+ "shell.title": "Terminal predeterminado",
97
+ "shell.description": "Terminal que usa la herramienta shell",
98
+ "shell.powershell": "PowerShell",
99
+ "shell.gitbash": "Git Bash",
100
+ "shell.msys2": "MSYS2",
101
+ "shell.wsl": "WSL",
102
+ "shell.unavailable": "Configuraci\xF3n no disponible: el host no expone ninguna entrada configForms para este plugin (comprueba el entry id de cordis.patch.yml)"
103
+ };
104
+ var inject = ["slots", "locale", "configForms"];
105
+ function ShellPreferenceRow({ t, useStore, setShell }) {
106
+ const shell = useStore((s) => s.shell);
107
+ const writable = useStore((s) => s.writable);
108
+ const status = useStore((s) => s.status);
109
+ const [open, setOpen] = (0, import_react.useState)(false);
110
+ const items = SHELLS.map((id) => ({ id, label: t("shell." + id) }));
111
+ if (status === "unavailable") {
112
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btRow", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "btRowText", children: [
113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btTitle", children: t("shell.title") }),
114
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btDesc", children: t("shell.unavailable") })
115
+ ] }) });
116
+ }
117
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "btRow", children: [
118
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "btRowText", children: [
119
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btTitle", children: t("shell.title") }),
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btDesc", children: t("shell.description") })
121
+ ] }),
122
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
123
+ import_dsh_client_ui_primitives.Menu,
124
+ {
125
+ open,
126
+ onClose: () => setOpen(false),
127
+ items,
128
+ selectedId: shell,
129
+ onSelect: (id) => {
130
+ setOpen(false);
131
+ setShell(id);
132
+ },
133
+ align: "end",
134
+ portal: true,
135
+ anchor: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
136
+ "button",
137
+ {
138
+ type: "button",
139
+ className: "btSelector",
140
+ "aria-haspopup": "menu",
141
+ "aria-expanded": open,
142
+ disabled: !writable,
143
+ onClick: () => setOpen(!open),
144
+ style: !writable ? { opacity: 0.5, cursor: "not-allowed" } : void 0,
145
+ children: [
146
+ t("shell." + shell),
147
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutlineMedium, { className: "btChevron" })
148
+ ]
149
+ }
150
+ )
151
+ }
152
+ )
153
+ ] });
154
+ }
155
+ var zhDictTitle = () => zh["shell.title"] ?? "\u7EC8\u7AEF";
156
+ function apply(ctx) {
157
+ ctx.effect(() => ctx.locale.register(SETTINGS_NS, { zh, en, fr, de, it, ru, es }), "bash-terminal: settings dictionaries");
158
+ const scope = ctx.configForms.get(ENTRY_ID);
159
+ const store = (0, import_dsh_client_store.defineStore)({
160
+ init: () => ({ shell: "powershell", revision: -1, writable: false, status: "loading" }),
161
+ actions: {
162
+ sync: (d, shell, revision, writable, status) => {
163
+ if (revision !== void 0 && revision <= d.revision) return;
164
+ if (shell !== void 0) d.shell = shell;
165
+ if (revision !== void 0) d.revision = revision;
166
+ if (writable !== void 0) d.writable = writable;
167
+ if (status !== void 0) d.status = status;
168
+ }
169
+ }
170
+ });
171
+ let bound;
172
+ const push = (snap) => {
173
+ bound?.sync(snap.value?.defaultShell, snap.revision, snap.writable, snap.status);
174
+ };
175
+ ctx.slots.inject(
176
+ "dsh-family.tab",
177
+ () => ctx.slots.register(
178
+ {
179
+ name: "dsh-family.tab",
180
+ id: "bash-terminal-shell",
181
+ order: 60,
182
+ label: () => zhDictTitle(),
183
+ store,
184
+ locale: SETTINGS_NS,
185
+ inject: (actions) => {
186
+ bound = actions;
187
+ push(scope.getSnapshot());
188
+ return { setShell: (value) => void scope.set("defaultShell", value) };
189
+ }
190
+ },
191
+ ShellPreferenceRow
192
+ ),
193
+ "bash-terminal: settings row"
194
+ );
195
+ ctx.effect(() => scope.subscribe(() => push(scope.getSnapshot())), "bash-terminal: settings watch");
196
+ }