diff --git a/.codex/skills/server-manager/SKILL.md b/.codex/skills/server-manager/SKILL.md new file mode 100644 index 0000000..d93d448 --- /dev/null +++ b/.codex/skills/server-manager/SKILL.md @@ -0,0 +1,88 @@ +--- +name: server-manager +description: Use ServerManager's shared local server inventory and ssh.py utility to manage configured SSH, Telnet, SQL, Redis, S3/MinIO, Grafana, Prometheus, and WinRM endpoints by alias without exposing credentials. Use when the user asks to operate on servers managed by ServerManager or when editing ServerManager's Claude/Codex integration. +metadata: + short-description: Safe remote ops through ServerManager aliases +--- + +# Server Manager + +Use this skill for two cases: + +1. The user wants work done on a server or service already configured in ServerManager. +2. The user wants to modify ServerManager's CLI/integration layer so Claude/Codex can use it safely. + +## First Step + +Before any server operation: + +```bash +$HOME/.server-connections/codex-ssh --list +``` + +Read the `Type` column before choosing commands. Do not guess the server type. + +If the wrapper is missing, run the doctor script for your platform: + +```bash +$HOME/.codex/skills/server-manager/scripts/server-manager-doctor.sh +``` + +On Windows, use: + +```bat +%USERPROFILE%\.codex\skills\server-manager\scripts\server-manager-doctor.cmd +``` + +## Hard Rules + +- Never read `~/.server-connections/servers.json`, `settings.json`, or `encryption.py` directly. +- Never use `--list-full`. +- Never use raw `ssh`, `scp`, `rsync`, `redis-cli`, `mysql`, `psql`, `mc`, `aws s3`, or similar tools unless the user explicitly asks to bypass ServerManager. +- Maximum one connection attempt per action. If it times out or fails, report it and stop. +- `ALIAS "command"` is only for `ssh` and `telnet`. +- `rdp` and `vnc` are GUI-only. Do not invent CLI access. +- For S3/MinIO, list buckets and paths before upload, delete, or URL generation. +- Ask for confirmation before destructive actions if the user's intent is not explicit. + +## Preferred Entry Points + +Use the shared wrapper: + +```bash +$HOME/.server-connections/codex-ssh ... +``` + +It delegates to the installed `ssh.py` backend without requiring a `python` alias. + +Safe discovery commands: + +```bash +$HOME/.server-connections/codex-ssh --list +$HOME/.server-connections/codex-ssh --info ALIAS +$HOME/.server-connections/codex-ssh --status +``` + +Read [references/command-matrix.md](references/command-matrix.md) when you need the per-type command matrix. + +## Server Operation Workflow + +1. Run `--list`. +2. Match the alias using notes/type, not credentials. +3. Pick commands strictly from the server type. +4. Execute exactly one action. +5. Report the result without exposing IPs, logins, passwords, ports, or secrets. + +## Working On ServerManager Itself + +Read [references/project.md](references/project.md) before changing integration code. + +Source-of-truth files: + +- `tools/ssh.py`: local CLI used by Claude/Codex +- `tools/skill-ssh.md`: current Claude `/ssh` instructions +- `core/claude_setup.py`: Claude installer logic +- `build.py`: auto-deploys shared CLI files after build +- `README.md` and `CLAUDE.md`: project-level rules and architecture + +If you change command semantics in `tools/ssh.py`, update the user-facing instructions alongside it. diff --git a/.codex/skills/server-manager/references/command-matrix.md b/.codex/skills/server-manager/references/command-matrix.md new file mode 100644 index 0000000..4826589 --- /dev/null +++ b/.codex/skills/server-manager/references/command-matrix.md @@ -0,0 +1,91 @@ +# Command Matrix + +Always identify the server type first with: + +```bash +$HOME/.server-connections/codex-ssh --list +``` + +## Type To Command Map + +| Type | Use | Do Not Use | +| --- | --- | --- | +| `ssh` | `ALIAS "command"`, `--upload`, `--download`, `--ping`, `--install-key` | n/a | +| `telnet` | `ALIAS "command"` | `--upload`, `--download`, `--install-key` | +| `mariadb`, `mssql`, `postgresql` | `--sql`, `--sql-databases`, `--sql-tables` | `ALIAS "command"` | +| `redis` | `--redis`, `--redis-info`, `--redis-keys` | `ALIAS "command"` | +| `s3` | `--s3-buckets`, `--s3-ls`, `--s3-upload`, `--s3-download`, `--s3-delete`, `--s3-url`, `--s3-create-bucket` | `ALIAS "command"`, SSH/SFTP commands | +| `grafana` | `--grafana-dashboards`, `--grafana-alerts` | `ALIAS "command"` | +| `prometheus` | `--prom-query`, `--prom-targets`, `--prom-alerts` | `ALIAS "command"` | +| `winrm` | `--ps`, `--cmd` | `ALIAS "command"` | +| `rdp`, `vnc` | GUI only | all CLI actions | + +## Common Safe Commands + +```bash +$HOME/.server-connections/codex-ssh --list +$HOME/.server-connections/codex-ssh --info ALIAS +$HOME/.server-connections/codex-ssh --status +$HOME/.server-connections/codex-ssh --set-note ALIAS "description" +``` + +## SSH And Telnet + +```bash +$HOME/.server-connections/codex-ssh ALIAS "command" +$HOME/.server-connections/codex-ssh ALIAS --no-sudo "command" +$HOME/.server-connections/codex-ssh ALIAS --upload "local" //remote/path +$HOME/.server-connections/codex-ssh ALIAS --download //remote/path "local" +$HOME/.server-connections/codex-ssh ALIAS --ping +``` + +Use double slashes for remote SSH/SFTP paths when working from Git Bash style environments. + +## SQL + +```bash +$HOME/.server-connections/codex-ssh --sql ALIAS "SELECT * FROM table LIMIT 10" +$HOME/.server-connections/codex-ssh --sql-databases ALIAS +$HOME/.server-connections/codex-ssh --sql-tables ALIAS [database] +``` + +## Redis + +```bash +$HOME/.server-connections/codex-ssh --redis ALIAS "GET key" +$HOME/.server-connections/codex-ssh --redis-info ALIAS +$HOME/.server-connections/codex-ssh --redis-keys ALIAS "pattern:*" +``` + +## S3 / MinIO + +Before modifying objects: + +```bash +$HOME/.server-connections/codex-ssh --s3-buckets ALIAS +$HOME/.server-connections/codex-ssh --s3-ls ALIAS bucket/prefix/ +``` + +Then act: + +```bash +$HOME/.server-connections/codex-ssh --s3-upload ALIAS "local" bucket/key +$HOME/.server-connections/codex-ssh --s3-download ALIAS bucket/key "local" +$HOME/.server-connections/codex-ssh --s3-delete ALIAS bucket/key +$HOME/.server-connections/codex-ssh --s3-url ALIAS bucket/key [seconds] +$HOME/.server-connections/codex-ssh --s3-create-bucket ALIAS bucket-name +``` + +Do not treat S3 as a shell filesystem. + +## Grafana / Prometheus / WinRM + +```bash +$HOME/.server-connections/codex-ssh --grafana-dashboards ALIAS +$HOME/.server-connections/codex-ssh --grafana-alerts ALIAS +$HOME/.server-connections/codex-ssh --prom-query ALIAS "up" +$HOME/.server-connections/codex-ssh --prom-targets ALIAS +$HOME/.server-connections/codex-ssh --prom-alerts ALIAS +$HOME/.server-connections/codex-ssh --ps ALIAS "Get-Process" +$HOME/.server-connections/codex-ssh --cmd ALIAS "dir" +``` diff --git a/.codex/skills/server-manager/references/project.md b/.codex/skills/server-manager/references/project.md new file mode 100644 index 0000000..d1ae527 --- /dev/null +++ b/.codex/skills/server-manager/references/project.md @@ -0,0 +1,68 @@ +# Project Notes + +This skill is based on `/home/code/CODING/server-manager`. + +## What ServerManager Is + +ServerManager is a cross-platform desktop GUI built with CustomTkinter. It manages multiple remote endpoint types through one local encrypted inventory: + +- SSH / Telnet +- MariaDB / MSSQL / PostgreSQL +- Redis +- S3 / MinIO +- Grafana +- Prometheus +- WinRM +- RDP / VNC launchers + +## Core Integration Model + +The GUI and CLI share one local backend: + +```text +ServerManager GUI <-> ~/.server-connections/servers.json <-> ~/.server-connections/ssh.py +``` + +The AI never needs raw credentials. It only uses aliases and the local CLI. + +## Important Files + +- `README.md`: product overview and install flow +- `CLAUDE.md`: project rules, architecture, security, workflow +- `tools/ssh.py`: CLI entry point used by AI tools +- `tools/skill-ssh.md`: current Claude `/ssh` instructions +- `core/claude_setup.py`: installer for shared CLI files, Claude command, and Codex skill +- `build.py`: auto-deploys `ssh.py`, `encryption.py`, Claude skill, and Codex skill after builds + +## Architectural Shape + +- `core/server_store.py`: encrypted storage, CRUD, observers, backups +- `core/connection_factory.py`: type-to-client factory with lazy imports +- `core/*_client.py`: protocol-specific backends +- `gui/app.py`: tab registry, conditional tabs by server type +- `gui/tabs/`: protocol-specific GUI surfaces + +## Existing Local Agent Integration + +Current setup installs: + +- `~/.server-connections/ssh.py` +- `~/.server-connections/encryption.py` +- `~/.claude/commands/ssh.md` +- `~/.codex/skills/server-manager/` +- `~/.server-connections/codex-ssh` or `codex-ssh.cmd` +- a `~/.claude/CLAUDE.md` guidance block + +The Codex skill mirrors the same safety model: + +- use aliases only +- use the shared local CLI +- never read credentials directly +- choose commands by server type + +## Local Findings + +- `ssh.py` is executable and uses a `python3` shebang, so Codex does not need a `python` alias. +- `ssh.py` has no `--help`; use `--list`, `--info`, and `--status` for safe discovery. +- The Unix wrapper path covers both Linux and macOS through `codex-ssh-wrapper.sh`. +- Windows-native Codex wrapper support exists through `codex-ssh-wrapper.cmd`. diff --git a/.codex/skills/server-manager/scripts/codex-ssh-wrapper.cmd b/.codex/skills/server-manager/scripts/codex-ssh-wrapper.cmd new file mode 100644 index 0000000..73cc43d --- /dev/null +++ b/.codex/skills/server-manager/scripts/codex-ssh-wrapper.cmd @@ -0,0 +1,28 @@ +@echo off +setlocal + +set "SHARED_DIR=%SERVER_MANAGER_SHARED_DIR%" +if "%SHARED_DIR%"=="" set "SHARED_DIR=%USERPROFILE%\.server-connections" +set "SSH_SCRIPT=%SHARED_DIR%\ssh.py" + +if not exist "%SSH_SCRIPT%" ( + echo error: missing ssh.py at %SSH_SCRIPT% 1>&2 + echo hint: install ServerManager shared CLI files first 1>&2 + exit /b 1 +) + +where py >nul 2>&1 +if not errorlevel 1 ( + py -3 "%SSH_SCRIPT%" %* + exit /b %errorlevel% +) + +where python >nul 2>&1 +if not errorlevel 1 ( + python "%SSH_SCRIPT%" %* + exit /b %errorlevel% +) + +echo error: neither py nor python is available in PATH 1>&2 +echo hint: install Python launcher or use ServerManager Setup on a machine with Python present 1>&2 +exit /b 1 diff --git a/.codex/skills/server-manager/scripts/codex-ssh-wrapper.sh b/.codex/skills/server-manager/scripts/codex-ssh-wrapper.sh new file mode 100644 index 0000000..723f665 --- /dev/null +++ b/.codex/skills/server-manager/scripts/codex-ssh-wrapper.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +shared_dir="${SERVER_MANAGER_SHARED_DIR:-$HOME/.server-connections}" +ssh_script="${shared_dir}/ssh.py" + +if [[ ! -x "$ssh_script" ]]; then + echo "error: missing executable ssh.py at ${ssh_script}" >&2 + echo "hint: install ServerManager's shared CLI files first" >&2 + exit 1 +fi + +exec "$ssh_script" "$@" diff --git a/.codex/skills/server-manager/scripts/server-manager-doctor.cmd b/.codex/skills/server-manager/scripts/server-manager-doctor.cmd new file mode 100644 index 0000000..637e37e --- /dev/null +++ b/.codex/skills/server-manager/scripts/server-manager-doctor.cmd @@ -0,0 +1,41 @@ +@echo off +setlocal + +set "SHARED_DIR=%SERVER_MANAGER_SHARED_DIR%" +if "%SHARED_DIR%"=="" set "SHARED_DIR=%USERPROFILE%\.server-connections" +set "CODEX_HOME=%USERPROFILE%\.codex\skills\server-manager" + +set "FAILED=0" + +if exist "%SHARED_DIR%\ssh.py" ( + echo ok: found %SHARED_DIR%\ssh.py +) else ( + echo error: missing %SHARED_DIR%\ssh.py 1>&2 + set "FAILED=1" +) + +if exist "%SHARED_DIR%\encryption.py" ( + echo ok: found %SHARED_DIR%\encryption.py +) else ( + echo error: missing %SHARED_DIR%\encryption.py 1>&2 + set "FAILED=1" +) + +if exist "%SHARED_DIR%\codex-ssh.cmd" ( + echo ok: found %SHARED_DIR%\codex-ssh.cmd +) else ( + echo error: missing %SHARED_DIR%\codex-ssh.cmd 1>&2 + set "FAILED=1" +) + +if exist "%CODEX_HOME%\SKILL.md" ( + echo ok: found %CODEX_HOME%\SKILL.md +) else ( + echo error: missing %CODEX_HOME%\SKILL.md 1>&2 + set "FAILED=1" +) + +if "%FAILED%"=="1" exit /b 1 + +echo ok: ServerManager Codex integration looks installed +exit /b 0 diff --git a/.codex/skills/server-manager/scripts/server-manager-doctor.sh b/.codex/skills/server-manager/scripts/server-manager-doctor.sh new file mode 100644 index 0000000..d21b18f --- /dev/null +++ b/.codex/skills/server-manager/scripts/server-manager-doctor.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -euo pipefail + +shared_dir="${SERVER_MANAGER_SHARED_DIR:-$HOME/.server-connections}" +ssh_script="${shared_dir}/ssh.py" +encryption_module="${shared_dir}/encryption.py" +wrapper="${shared_dir}/codex-ssh" + +status=0 + +check_file() { + local path="$1" + if [[ -f "$path" ]]; then + printf '[ok] file %s\n' "$path" + else + printf '[missing] file %s\n' "$path" >&2 + status=1 + fi +} + +check_exec() { + local path="$1" + if [[ -x "$path" ]]; then + printf '[ok] executable %s\n' "$path" + else + printf '[missing] executable %s\n' "$path" >&2 + status=1 + fi +} + +check_file "$encryption_module" +check_exec "$ssh_script" +check_exec "$wrapper" + +if [[ -d "/home/code/CODING/server-manager" ]]; then + printf '[ok] source repo /home/code/CODING/server-manager\n' +else + printf '[warn] source repo /home/code/CODING/server-manager not found\n' +fi + +exit "$status" diff --git a/CHANGELOG.md b/CHANGELOG.md index 3917920..3764418 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [Unreleased] + +### Added +- Add Codex integration to Setup tab with dedicated install buttons and status rows +- Add packaged Codex skill deployment and local `codex-ssh` wrapper installation +- Add Windows `.cmd` wrappers for Codex skill installation/runtime + +### Changed +- Extend `build.py` auto-deploy to sync Codex artifacts alongside Claude artifacts +- Update docs and i18n strings to describe Claude Code + Codex setup flow ## [1.8.24] - 2026-02-24 diff --git a/CODEX_SKILL_SETUP.md b/CODEX_SKILL_SETUP.md new file mode 100644 index 0000000..7a2ed94 --- /dev/null +++ b/CODEX_SKILL_SETUP.md @@ -0,0 +1,409 @@ +# Развёртывание Codex Skill Для ServerManager + +Этот документ описывает текущее состояние интеграции `ServerManager -> Codex`, автоматическое и ручное развёртывание, проверку и все известные edge cases. + +Поддерживаемый deployment target для этой интеграции: + +- Linux +- macOS +- Windows + +## Что именно разворачивается + +Интеграция для Codex состоит из трёх слоёв: + +1. Общий локальный backend: + - `~/.server-connections/ssh.py` + - `~/.server-connections/encryption.py` + - `~/.server-connections/servers.json` +2. Codex skill package: + - `~/.codex/skills/server-manager/` +3. Безопасный wrapper для вызова backend из Codex: + - `~/.server-connections/codex-ssh` на Linux/macOS + - `~/.server-connections/codex-ssh.cmd` на Windows + +В репозитории исходники skill лежат здесь: + +- [SKILL.md](/home/code/CODING/server-manager/.codex/skills/server-manager/SKILL.md) +- [command-matrix.md](/home/code/CODING/server-manager/.codex/skills/server-manager/references/command-matrix.md) +- [project.md](/home/code/CODING/server-manager/.codex/skills/server-manager/references/project.md) +- [server-manager-doctor.sh](/home/code/CODING/server-manager/.codex/skills/server-manager/scripts/server-manager-doctor.sh) +- [server-manager-doctor.cmd](/home/code/CODING/server-manager/.codex/skills/server-manager/scripts/server-manager-doctor.cmd) +- [codex-ssh-wrapper.sh](/home/code/CODING/server-manager/.codex/skills/server-manager/scripts/codex-ssh-wrapper.sh) +- [codex-ssh-wrapper.cmd](/home/code/CODING/server-manager/.codex/skills/server-manager/scripts/codex-ssh-wrapper.cmd) + +## Как это работает + +Модель безопасности та же, что и у Claude integration: + +```text +Codex skill -> ~/.server-connections/codex-ssh -> ~/.server-connections/ssh.py -> encrypted servers.json +``` + +Ключевая идея: + +- Codex видит только алиасы и безопасные результаты команд. +- `ssh.py` сам читает credentials из локального зашифрованного хранилища. +- Codex не должен читать `servers.json`, `settings.json` или `encryption.py` напрямую. + +## Что уже автоматизировано + +Теперь Codex integration встроена в продуктовый setup flow: + +- `core/claude_setup.py` ставит `ssh.py`, `encryption.py`, `~/.claude/commands/ssh.md`, `~/.codex/skills/server-manager/`, wrapper `~/.server-connections/codex-ssh` и блок в `~/.claude/CLAUDE.md` +- вкладка `Setup` в GUI показывает отдельные статусы для Claude skill, Codex skill и Codex wrapper +- `build.py` после сборки автоматически синхронизирует Claude- и Codex-артефакты в локальный runtime + +Платформенный split такой: + +- Linux/macOS: используются `codex-ssh-wrapper.sh` и `server-manager-doctor.sh` +- Windows: используются `codex-ssh-wrapper.cmd` и `server-manager-doctor.cmd` + +Ручная установка всё ещё полезна как fallback path, если нужен точечный repair или offline debugging. + +## Предварительные условия + +Перед установкой Codex skill должны уже существовать или быть установлены через `Setup`: + +1. `~/.server-connections/ssh.py` +2. `~/.server-connections/encryption.py` +3. `~/.server-connections/servers.json` +4. `codex` CLI + +Проверка: + +```bash +ls -la ~/.server-connections +codex --help +``` + +Если `~/.server-connections/ssh.py` отсутствует: + +1. Открыть ServerManager GUI +2. Перейти в `Setup` +3. Нажать `Install Everything` + +Это поставит backend, Claude skill и Codex skill целиком. + +## Рекомендуемый путь: установка через GUI + +1. Открыть ServerManager +2. Перейти в `Setup` +3. Нажать `Install Everything` +4. Проверить, что зелёные статусы появились у: + - `ssh.py` + - `Encryption module` + - `Claude /ssh skill` + - `Codex skill` + - `Codex wrapper` + - `SSH key` + +Для точечного ремонта можно использовать отдельные кнопки `Claude skill` и `Codex skill` в той же вкладке. + +## Ручная установка Codex Skill + +### 1. Скопировать skill package в глобальный Codex home + +```bash +mkdir -p ~/.codex/skills +cp -R .codex/skills/server-manager ~/.codex/skills/server-manager +``` + +### 2. Установить wrapper в shared runtime directory + +Linux/macOS: + +```bash +install -m 755 .codex/skills/server-manager/scripts/codex-ssh-wrapper.sh ~/.server-connections/codex-ssh +``` + +Windows: + +```bat +copy .codex\skills\server-manager\scripts\codex-ssh-wrapper.cmd %USERPROFILE%\.server-connections\codex-ssh.cmd +``` + +### 3. Проверить doctor script + +Linux/macOS: + +```bash +~/.codex/skills/server-manager/scripts/server-manager-doctor.sh +``` + +Windows: + +```bat +%USERPROFILE%\.codex\skills\server-manager\scripts\server-manager-doctor.cmd +``` + +Ожидается: + +- `ssh.py` найден +- `encryption.py` найден +- `codex-ssh` executable + +### 4. Проверить wrapper без раскрытия credentials + +Linux/macOS: + +```bash +~/.server-connections/codex-ssh --list +``` + +Windows: + +```bat +%USERPROFILE%\.server-connections\codex-ssh.cmd --list +``` + +Это безопасная базовая проверка. Она должна вывести список алиасов и типов серверов. + +### 5. Перезапустить Codex + +Если у вас уже была открыта интерактивная Codex session, её нужно перезапустить. Новый skill обычно подхватывается новым процессом Codex, а не уже живой сессией. + +## Как проверить, что Codex реально видит skill + +Самый надёжный способ: + +```bash +codex exec --skip-git-repo-check -s read-only -C /tmp \ + "A user asks: Using the locally installed ServerManager integration, what is the safest first command to enumerate configured servers? Reply with only the command." +``` + +Если skill подхватился корректно, Codex должен сам прочитать `~/.codex/skills/server-manager/SKILL.md` и ответить: + +```bash +$HOME/.server-connections/codex-ssh --list +``` + +## Что Codex должен делать через skill + +Правильный workflow для любой server operation: + +1. Сначала `--list` +2. Прочитать колонку `Type` +3. Выбрать команду строго по типу сервера +4. Выполнить ровно одно подключение/одно действие +5. Вернуть результат без IP/логинов/паролей/портов + +Безопасные discovery-команды: + +```bash +$HOME/.server-connections/codex-ssh --list +$HOME/.server-connections/codex-ssh --info ALIAS +$HOME/.server-connections/codex-ssh --status +``` + +## Источники истины по интеграции + +Если меняется поведение интеграции, проверять нужно в таком порядке: + +1. [tools/ssh.py](/home/code/CODING/server-manager/tools/ssh.py) +2. [tools/skill-ssh.md](/home/code/CODING/server-manager/tools/skill-ssh.md) +3. [core/claude_setup.py](/home/code/CODING/server-manager/core/claude_setup.py) +4. [build.py](/home/code/CODING/server-manager/build.py) +5. [SKILL.md](/home/code/CODING/server-manager/.codex/skills/server-manager/SKILL.md) +6. [command-matrix.md](/home/code/CODING/server-manager/.codex/skills/server-manager/references/command-matrix.md) + +Если меняется семантика `ssh.py`, нужно обновлять и Claude skill, и Codex skill. + +## Edge Cases + +### 1. `python` alias отсутствует + +В этой среде `python` отсутствует, но `ssh.py` имеет shebang `#!/usr/bin/env python3` и executable bit. + +Поэтому wrapper вызывает `ssh.py` напрямую: + +```bash +~/.server-connections/codex-ssh --list +``` + +Это намеренно лучше, чем завязка на `python ~/.server-connections/ssh.py`. + +### 2. `ssh.py --help` не поддерживается + +`ssh.py` не имеет полноценного `--help`. Попытка вызвать `--help` возвращает список доступных alias'ов, а не usage. + +Поэтому для безопасной проверки используются: + +- `--list` +- `--info ALIAS` +- `--status` + +### 3. Skill установлен в repo, но не установлен глобально + +Наличие `.codex/skills/server-manager/` внутри репозитория полезно как source of truth, но новый Codex процесс по умолчанию ищет глобальные skills в `~/.codex/skills`. + +Если skill есть только в repo: + +- документация в проекте будет на месте +- глобальный Codex может его не увидеть + +Для надёжности нужен именно глобальный install в `~/.codex/skills/server-manager`. + +### 4. Wrapper отсутствует, а skill уже установлен + +В этом случае Codex прочитает skill, но не сможет выполнить рекомендуемую команду `$HOME/.server-connections/codex-ssh ...`. + +Проверка: + +```bash +~/.codex/skills/server-manager/scripts/server-manager-doctor.sh +``` + +Исправление: + +```bash +install -m 755 .codex/skills/server-manager/scripts/codex-ssh-wrapper.sh ~/.server-connections/codex-ssh +``` + +### 5. Backend отсутствует + +Если нет `~/.server-connections/ssh.py` или `encryption.py`, skill бесполезен: он знает workflow, но не имеет локального transport layer. + +Исправление: + +1. Запустить ServerManager +2. `Setup -> Install Everything` + +### 6. Интерактивный Codex уже был запущен до установки skill + +Новая интерактивная сессия обычно увидит skill, старая может не увидеть. + +Исправление: + +- закрыть старую Codex session +- запустить новый процесс `codex` + +### 7. `codex exec` не может проверить skill из-за sandbox/network policy + +Во время non-interactive проверки Codex может упереться не в skill, а в сетевую политику среды: + +- websocket backend заблокирован +- sandbox запрещает соединение + +Симптом: + +```text +failed to connect to websocket ... Operation not permitted +``` + +Это не означает, что skill неверный. Это означает, что проверка упёрлась в runtime policy Codex backend. + +### 8. Повторная установка поверх существующего skill + +GUI installer и `install_codex_skill()` синхронизируют дерево skill поверх существующей директории без полного удаления. Это безопасно для обычных обновлений. + +Но при ручном `cp -R` возможен stale state, если какие-то файлы были удалены из repo, а старая глобальная копия осталась. + +Полная ручная пересинхронизация нужна только если вы осознанно хотите очистить старые файлы: + +1. удалить старую копию осознанно +2. снова скопировать skill целиком + +Пример: + +```bash +rm -rf ~/.codex/skills/server-manager +cp -R .codex/skills/server-manager ~/.codex/skills/server-manager +``` + +Делать это только если вы уверены, что хотите полностью пересобрать глобальную копию. + +### 9. Изменился `tools/ssh.py`, но глобальная установка осталась старой + +Это самый вероятный operational drift. + +Что может устареть: + +- `~/.server-connections/ssh.py` +- `~/.codex/skills/server-manager/*` +- `~/.server-connections/codex-ssh` + +После изменения `tools/ssh.py` или skill docs нужно заново синхронизировать: + +```bash +cp tools/ssh.py ~/.server-connections/ssh.py +cp core/encryption.py ~/.server-connections/encryption.py +rm -rf ~/.codex/skills/server-manager +cp -R .codex/skills/server-manager ~/.codex/skills/server-manager +install -m 755 .codex/skills/server-manager/scripts/codex-ssh-wrapper.sh ~/.server-connections/codex-ssh +``` + +### 10. Windows / macOS / Linux split + +Сейчас runtime path intentionally разделён по платформам: + +- Linux/macOS: shell wrapper `codex-ssh-wrapper.sh` +- Windows: native wrapper `codex-ssh-wrapper.cmd` + +Installer на Windows кладёт wrapper как: + +- `~/.server-connections/codex-ssh.cmd` + +Installer на Linux/macOS кладёт wrapper как: + +- `~/.server-connections/codex-ssh` + +Что это закрывает: + +- Linux/macOS path без platform-specific разветвления в skill +- запуск через `cmd.exe` +- запуск из PowerShell +- отсутствие bash-зависимости для стандартного Windows deployment path + +Ограничение остаётся одно: в текущей среде я прогнал end-to-end smoke только на Linux. macOS и Windows path подготовлены в installer/docs, но не smoke-tested здесь из-за отсутствия соответствующих runner'ов. + +### 11. `ssh.py` intentionally не должен читать secrets в контекст AI + +Это не баг. Даже если кажется проще открыть `servers.json`, делать этого нельзя. + +Skill намеренно запрещает: + +- `cat ~/.server-connections/servers.json` +- `cat ~/.server-connections/settings.json` +- `python -c "...read servers.json..."` +- `--list-full` + +### 12. fail2ban / anti-bruteforce edge case + +Повторные неудачные подключения опасны. Поэтому skill зафиксирован как: + +- максимум 1 попытка на действие +- при timeout/ошибке остановиться и сообщить пользователю + +Это обязательное правило, а не рекомендация. + +## Рекомендуемый Update Workflow + +После любых изменений, затрагивающих Codex integration: + +1. Обновить исходники в repo: + - `tools/ssh.py` + - `.codex/skills/server-manager/*` + - этот документ +2. Синхронизировать глобальную установку +3. Прогнать doctor +4. Прогнать `~/.server-connections/codex-ssh --list` +5. Прогнать свежий `codex exec` smoke test + +## Минимальный Smoke Test + +```bash +~/.codex/skills/server-manager/scripts/server-manager-doctor.sh +~/.server-connections/codex-ssh --list +codex exec --skip-git-repo-check -s read-only -C /tmp \ + "A user asks: Using the locally installed ServerManager integration, what is the safest first command to enumerate configured servers? Reply with only the command." +``` + +## Что ещё желательно автоматизировать позже + +Чтобы интеграция стала production-complete, в проект ещё полезно добавить: + +1. отдельный smoke test script внутри репозитория для проверки именно Codex integration +2. e2e smoke test на Windows runner +3. e2e smoke test на macOS runner +4. optional PowerShell-native wrapper, если понадобится richer Windows logging diff --git a/README.md b/README.md index bf68617..e3ad22d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

Desktop GUI for managing remote servers
- CustomTkinter + Paramiko | Dark Theme | Claude Code Integration + CustomTkinter + Paramiko | Dark Theme | Claude Code + Codex Integration

@@ -22,7 +22,7 @@ - **SFTP Transfer** — upload/download files with progress bar - **SSH Keys** — generate ed25519, install on server, copy to clipboard - **Status Monitor** — background check every 60 sec (online/offline badges) -- **Claude Code Integration** — one-click setup, shared config with `/ssh` skill +- **Claude Code + Codex Integration** — one-click setup, shared config with `/ssh` skill and Codex skill - **TOTP / 2FA** — Google Authenticator compatible codes with live countdown, one-click copy - **Encryption** — servers.json encrypted with Fernet (passwords never stored in plaintext) - **Backups** — manual and automatic backups with one-click restore @@ -62,23 +62,25 @@ Output goes to `releases/ServerManager-vX.Y.Z-{platform}.exe` 3. **Terminal** — select server → Terminal tab → type command → Run 4. **Files** — select server → Files tab → set paths → Upload/Download 5. **Keys** — Keys tab → Generate Key → Install on Server -6. **Setup** — Setup tab → "Install Everything" → Claude Code ready +6. **Setup** — Setup tab → "Install Everything" → Claude Code and Codex ready 7. Status badges update automatically (green = online, red = offline) -### Claude Code Integration +### Claude Code + Codex Integration -ServerManager and Claude Code share the same config file: `~/.server-connections/servers.json` +ServerManager, Claude Code, and Codex share the same config file: `~/.server-connections/servers.json` + +For Codex deployment and operational edge cases, see [`CODEX_SKILL_SETUP.md`](CODEX_SKILL_SETUP.md). **How it works:** ``` -ServerManager GUI ←→ ~/.server-connections/servers.json ←→ ssh.py (Claude Code) +ServerManager GUI ←→ ~/.server-connections/servers.json ←→ ssh.py backend ↕ ↕ - Add/edit/delete /ssh skill - servers in GUI executes commands + Add/edit/delete Claude /ssh + Codex skill + servers in GUI execute commands ``` -- Add a server in GUI → Claude Code sees it immediately via `/ssh list` -- Both use the same `ssh.py` + `servers.json` +- Add a server in GUI → Claude Code and Codex see it immediately +- Both agents use the same `ssh.py` + `servers.json` - Passwords **never** pass through the AI API **New SSH Commands:** @@ -93,12 +95,14 @@ ServerManager GUI ←→ ~/.server-connections/servers.json ←→ ssh.py (C **Setup on a new machine:** 1. Install ServerManager (clone repo or download binary) 2. Open Setup tab → click "Install Everything" -3. Done. Claude Code now has `/ssh` skill and access to your servers +3. Done. Claude Code now has `/ssh`, and Codex now has the `server-manager` skill with access to your servers The Setup tab installs: - `ssh.py` → `~/.server-connections/` (SSH utility) - `encryption.py` → `~/.server-connections/` (encryption module for CLI) - `/ssh` skill → `~/.claude/commands/ssh.md` (Claude Code skill) +- `server-manager` skill → `~/.codex/skills/server-manager/` (Codex skill package) +- `codex-ssh` wrapper → `~/.server-connections/` (safe Codex entry point) - SSH key (ed25519) — if not exists - Checks for duplicates — safe to run multiple times @@ -140,7 +144,7 @@ App executes: sudo -S -p '' bash -c 'systemctl restart nginx' - Passwords stored locally only, **never sent to any AI/API** - SSH keys (ed25519) — recommended auth method - sudo password sent via stdin (not visible in process list) -- When used with Claude Code: only alias + command are passed through the AI API, passwords stay in the local encrypted file +- When used with Claude Code or Codex: only alias + command are passed through the AI API, passwords stay in the local encrypted file - Automatic pre-encryption backup on first migration ### Project Structure @@ -154,7 +158,7 @@ ServerManager/ │ ├── server_store.py # CRUD + encrypted JSON + observer + backups │ ├── encryption.py # Fernet encryption module │ ├── ssh_client.py # Paramiko SSH/SFTP wrapper -│ ├── claude_setup.py # Claude Code integration installer +│ ├── claude_setup.py # Claude Code + Codex integration installer │ ├── status_checker.py # Background monitoring │ ├── totp.py # TOTP/2FA module (pyotp) │ ├── logger.py # Rotating file logger @@ -166,7 +170,7 @@ ServerManager/ │ ├── tabs/ # Terminal, Files, Info, Keys, Setup │ └── widgets/ # StatusBadge ├── tools/ # CLI tools (installed to ~/.server-connections/) -│ ├── ssh.py # SSH utility for Claude Code +│ ├── ssh.py # SSH utility for Claude Code / Codex │ └── skill-ssh.md # /ssh skill template ├── config/ # Example configs ├── releases/ # Built executables @@ -187,7 +191,7 @@ pip install -r requirements.txt python main.py # → Setup tab → Install Everything # → Add your servers via + Add -# → Done! Both GUI and Claude Code are ready +# → Done! GUI, Claude Code, and Codex are ready ``` --- @@ -201,7 +205,7 @@ python main.py - **SFTP** — загрузка и скачивание файлов с прогресс-баром - **SSH-ключи** — генерация ed25519, установка на сервер, копирование - **Мониторинг** — фоновая проверка каждые 60 сек (бейджи online/offline) -- **Интеграция с Claude Code** — установка в один клик, общий конфиг со скиллом `/ssh` +- **Интеграция с Claude Code + Codex** — установка в один клик, общий конфиг со скиллом `/ssh` и Codex skill - **TOTP / 2FA** — коды Google Authenticator с обратным отсчётом, копирование в один клик - **Шифрование** — servers.json зашифрован Fernet (пароли не хранятся в открытом виде) - **Бэкапы** — ручные и автоматические с восстановлением в один клик @@ -241,23 +245,23 @@ python build.py 3. **Терминал** — выберите сервер → вкладка Terminal → введите команду → Run 4. **Файлы** — выберите сервер → вкладка Files → укажите пути → Upload/Download 5. **Ключи** — вкладка Keys → Generate Key → Install on Server -6. **Настройка Claude** — вкладка Setup → "Install Everything" → Claude Code готов +6. **Настройка** — вкладка Setup → "Install Everything" → Claude Code и Codex готовы 7. Бейджи статуса обновляются автоматически (зелёный = online, красный = offline) -### Интеграция с Claude Code +### Интеграция с Claude Code + Codex -ServerManager и Claude Code используют **один и тот же файл конфигурации**: `~/.server-connections/servers.json` +ServerManager, Claude Code и Codex используют **один и тот же файл конфигурации**: `~/.server-connections/servers.json` **Как это работает:** ``` -ServerManager GUI ←→ ~/.server-connections/servers.json ←→ ssh.py (Claude Code) +ServerManager GUI ←→ ~/.server-connections/servers.json ←→ backend ssh.py ↕ ↕ - Добавил/изменил скилл /ssh - сервер в GUI выполняет команды + Добавил/изменил Claude /ssh + Codex skill + серверы в GUI выполняют команды ``` -- Добавил сервер в GUI → Claude Code сразу видит его через `/ssh list` -- Оба используют один `ssh.py` + `servers.json` +- Добавил сервер в GUI → Claude Code и Codex сразу видят его +- Оба агента используют один `ssh.py` + `servers.json` - Пароли **никогда** не проходят через API нейронки **Новые SSH команды:** @@ -272,12 +276,14 @@ ServerManager GUI ←→ ~/.server-connections/servers.json ←→ ssh.py (C **Настройка на новой машине:** 1. Установить ServerManager (клонировать репо или скачать бинарник) 2. Открыть вкладку Setup → нажать "Install Everything" -3. Готово. Claude Code теперь имеет скилл `/ssh` и доступ к серверам +3. Готово. Claude Code получает скилл `/ssh`, а Codex получает skill `server-manager` и доступ к серверам Вкладка Setup устанавливает: - `ssh.py` → `~/.server-connections/` (SSH-утилита) - `encryption.py` → `~/.server-connections/` (модуль шифрования для CLI) - скилл `/ssh` → `~/.claude/commands/ssh.md` (скилл Claude Code) +- skill `server-manager` → `~/.codex/skills/server-manager/` (скилл Codex) +- wrapper `codex-ssh` → `~/.server-connections/` (безопасная точка входа для Codex) - SSH-ключ (ed25519) — если ещё не создан - Проверяет дубли — безопасно запускать повторно @@ -319,7 +325,7 @@ ServerManager GUI ←→ ~/.server-connections/servers.json ←→ ssh.py (C - Пароли хранятся только локально, **никогда не передаются в AI/API** - SSH-ключи (ed25519) — рекомендуемый метод аутентификации - sudo-пароль передаётся через stdin (не виден в списке процессов) -- При использовании с Claude Code: через API нейронки проходят только alias + команда, пароли остаются в зашифрованном локальном файле +- При использовании с Claude Code или Codex: через API нейронки проходят только alias + команда, пароли остаются в зашифрованном локальном файле - Автоматический пред-шифровальный бэкап при первой миграции ### Развёртывание на новой машине @@ -336,7 +342,7 @@ pip install -r requirements.txt python main.py # → Вкладка Setup → Install Everything # → Добавить серверы через + Add -# → Готово! GUI и Claude Code работают с одним конфигом +# → Готово! GUI, Claude Code и Codex работают с одним конфигом ``` --- @@ -350,7 +356,7 @@ python main.py - **SFTP传输** — 带进度条的文件上传/下载 - **SSH密钥** — 生成ed25519、安装到服务器、复制到剪贴板 - **状态监控** — 每60秒后台检查(在线/离线徽标) -- **Claude Code集成** — 一键设置,与`/ssh`技能共享配置 +- **Claude Code + Codex 集成** — 一键设置,与 `/ssh` 技能和 Codex skill 共享配置 - **TOTP / 2FA** — 兼容Google Authenticator的验证码,实时倒计时,一键复制 - **加密** — servers.json使用Fernet加密(密码不再以明文存储) - **备份** — 手动和自动备份,一键恢复 @@ -390,34 +396,36 @@ python build.py 3. **终端** — 选择服务器 → Terminal标签 → 输入命令 → Run 4. **文件** — 选择服务器 → Files标签 → 设置路径 → Upload/Download 5. **密钥** — Keys标签 → Generate Key → Install on Server -6. **设置Claude** — Setup标签 → "Install Everything" → Claude Code就绪 +6. **设置** — Setup标签 → "Install Everything" → Claude Code 和 Codex 就绪 7. 状态徽标自动更新(绿色 = 在线,红色 = 离线) -### Claude Code集成 +### Claude Code + Codex 集成 -ServerManager和Claude Code共享**同一个配置文件**:`~/.server-connections/servers.json` +ServerManager、Claude Code 和 Codex 共享**同一个配置文件**:`~/.server-connections/servers.json` **工作原理:** ``` -ServerManager GUI ←→ ~/.server-connections/servers.json ←→ ssh.py (Claude Code) +ServerManager GUI ←→ ~/.server-connections/servers.json ←→ ssh.py 后端 ↕ ↕ - 在GUI中添加/编辑 /ssh技能 + 在GUI中添加/编辑 Claude /ssh + Codex skill 服务器 执行命令 ``` -- 在GUI中添加服务器 → Claude Code立即通过 `/ssh list` 看到 -- 两者使用相同的 `ssh.py` + `servers.json` +- 在GUI中添加服务器 → Claude Code 和 Codex 都会立即看到 +- 两个代理都使用同一个 `ssh.py` + `servers.json` - 密码**绝不**通过AI API传递 **在新机器上设置:** 1. 安装ServerManager(克隆仓库或下载二进制文件) 2. 打开Setup标签 → 点击 "Install Everything" -3. 完成。Claude Code现在拥有 `/ssh` 技能并可访问您的服务器 +3. 完成。Claude Code 现在拥有 `/ssh` 技能,Codex 现在拥有 `server-manager` 技能并可访问您的服务器 Setup标签安装: - `ssh.py` → `~/.server-connections/`(SSH工具) - `encryption.py` → `~/.server-connections/`(CLI加密模块) - `/ssh` 技能 → `~/.claude/commands/ssh.md`(Claude Code技能) +- `server-manager` 技能 → `~/.codex/skills/server-manager/`(Codex技能包) +- `codex-ssh` 包装器 → `~/.server-connections/`(Codex安全入口) - SSH密钥(ed25519)— 如果不存在 - 检查重复 — 可安全重复运行 @@ -459,7 +467,7 @@ Setup标签安装: - 密码仅存储在本地,**绝不发送到任何AI/API** - SSH密钥(ed25519)— 推荐的认证方式 - sudo密码通过stdin传递(在进程列表中不可见) -- 与Claude Code配合使用时:只有别名和命令通过AI API传递,密码保留在本地加密文件中 +- 与 Claude Code 或 Codex 配合使用时:只有别名和命令通过 AI API 传递,密码保留在本地加密文件中 - 首次迁移时自动创建加密前备份 ### 在新机器上部署 @@ -476,7 +484,7 @@ pip install -r requirements.txt python main.py # → Setup标签 → Install Everything # → 通过 + Add 添加服务器 -# → 完成!GUI和Claude Code使用同一个配置 +# → 完成!GUI、Claude Code 和 Codex 使用同一个配置 ``` --- diff --git a/build.py b/build.py index f29804f..c050a92 100644 --- a/build.py +++ b/build.py @@ -115,6 +115,7 @@ def build(): "--add-data", f"tools/ssh.py{os.pathsep}tools", "--add-data", f"tools/skill-ssh.md{os.pathsep}tools", "--add-data", f"core/encryption.py{os.pathsep}core", + "--add-data", f".codex/skills/server-manager{os.pathsep}.codex/skills/server-manager", ] # PNG icons for GUI (Material Design) @@ -350,34 +351,32 @@ def cleanup_old_releases(): def deploy_shared_files(): - """Auto-deploy ssh.py, encryption.py, skill to shared dirs after build. + """Auto-deploy shared CLI files and local agent integrations after build.""" + from core.claude_setup import ( + install_claude_skill, + install_codex_skill, + install_ssh_script, + ) - Ensures Claude Code /ssh skill always uses the latest version. - Without this, editing tools/ssh.py updates the exe but NOT the live - ~/.server-connections/ssh.py that Claude Code actually calls. - """ - shared_dir = os.path.expanduser("~/.server-connections") - skill_dir = os.path.expanduser("~/.claude/commands") - - deploy_map = [ - (os.path.join(PROJECT_DIR, "tools", "ssh.py"), - os.path.join(shared_dir, "ssh.py")), - (os.path.join(PROJECT_DIR, "core", "encryption.py"), - os.path.join(shared_dir, "encryption.py")), - (os.path.join(PROJECT_DIR, "tools", "skill-ssh.md"), - os.path.join(skill_dir, "ssh.md")), + deploy_steps = [ + install_ssh_script, + install_claude_skill, + install_codex_skill, ] deployed = [] - for src, dst in deploy_map: - if not os.path.exists(src): - continue - os.makedirs(os.path.dirname(dst), exist_ok=True) - shutil.copy2(src, dst) - deployed.append(os.path.basename(dst)) + for step in deploy_steps: + try: + result = step() + if result: + deployed.append(result.replace("\n", "; ")) + except Exception as exc: + print(f"WARNING: auto-deploy step failed ({step.__name__}): {exc}") if deployed: - print(f"Auto-deployed to local: {', '.join(deployed)}") + print("Auto-deployed to local:") + for item in deployed: + print(f"- {item}") if __name__ == "__main__": diff --git a/core/claude_setup.py b/core/claude_setup.py index 5ed6e51..d05cf5f 100644 --- a/core/claude_setup.py +++ b/core/claude_setup.py @@ -1,12 +1,15 @@ """ -Claude Code integration setup. -Installs ssh.py, encryption.py, /ssh skill, SSH key — everything needed -for Claude Code to manage servers via the shared servers.json. +Local AI agent integration setup. +Installs the shared ssh.py/encryption.py backend, Claude /ssh command, +Codex skill package, platform-specific wrappers, and SSH key material. """ import os -import sys +import re import shutil +import subprocess +import sys + from core.logger import log SHARED_DIR = os.path.expanduser("~/.server-connections") @@ -19,13 +22,24 @@ else: SSH_SCRIPT_SRC = os.path.join(_BASE_DIR, "tools", "ssh.py") ENCRYPTION_SRC = os.path.join(_BASE_DIR, "core", "encryption.py") -SKILL_SRC = os.path.join(_BASE_DIR, "tools", "skill-ssh.md") +CLAUDE_SKILL_SRC = os.path.join(_BASE_DIR, "tools", "skill-ssh.md") -SKILL_DST_DIR = os.path.expanduser("~/.claude/commands") -SKILL_DST = os.path.join(SKILL_DST_DIR, "ssh.md") +CLAUDE_SKILL_DST_DIR = os.path.expanduser("~/.claude/commands") +CLAUDE_SKILL_DST = os.path.join(CLAUDE_SKILL_DST_DIR, "ssh.md") SSH_KEY_PATH = os.path.expanduser("~/.ssh/id_ed25519") GLOBAL_CLAUDE_MD = os.path.expanduser("~/.claude/CLAUDE.md") +CODEX_SKILL_SRC_DIR = os.path.join(_BASE_DIR, ".codex", "skills", "server-manager") +CODEX_SKILL_DST_ROOT = os.path.expanduser("~/.codex/skills") +CODEX_SKILL_DST_DIR = os.path.join(CODEX_SKILL_DST_ROOT, "server-manager") +CODEX_SKILL_ENTRY = os.path.join(CODEX_SKILL_DST_DIR, "SKILL.md") +CODEX_WRAPPER_SRC_SH = os.path.join(CODEX_SKILL_SRC_DIR, "scripts", "codex-ssh-wrapper.sh") +CODEX_WRAPPER_SRC_CMD = os.path.join(CODEX_SKILL_SRC_DIR, "scripts", "codex-ssh-wrapper.cmd") +CODEX_WRAPPER_DST = os.path.join( + SHARED_DIR, + "codex-ssh.cmd" if sys.platform == "win32" else "codex-ssh", +) + _BLOCK_START = "" _BLOCK_END = "" @@ -67,6 +81,27 @@ python ~/.server-connections/ssh.py --status # online/offline """ +def _ensure_executable(path: str): + if sys.platform == "win32" or not os.path.exists(path): + return + mode = os.stat(path).st_mode + os.chmod(path, mode | 0o755) + + +def _copy_file(src: str, dst: str, executable: bool = False) -> str: + os.makedirs(os.path.dirname(dst), exist_ok=True) + shutil.copy2(src, dst) + if executable: + _ensure_executable(dst) + return dst + + +def _copy_tree(src: str, dst: str) -> str: + os.makedirs(dst, exist_ok=True) + shutil.copytree(src, dst, dirs_exist_ok=True) + return dst + + def check_status() -> dict: """Check what's installed and what's missing.""" return { @@ -74,7 +109,9 @@ def check_status() -> dict: "servers_json": os.path.exists(os.path.join(SHARED_DIR, "servers.json")), "ssh_script": os.path.exists(os.path.join(SHARED_DIR, "ssh.py")), "encryption": os.path.exists(os.path.join(SHARED_DIR, "encryption.py")), - "skill_installed": os.path.exists(SKILL_DST), + "claude_skill_installed": os.path.exists(CLAUDE_SKILL_DST), + "codex_skill_installed": os.path.exists(CODEX_SKILL_ENTRY), + "codex_wrapper_installed": os.path.exists(CODEX_WRAPPER_DST), "ssh_key_exists": os.path.exists(SSH_KEY_PATH), "ssh_key_pub": os.path.exists(SSH_KEY_PATH + ".pub"), } @@ -85,10 +122,9 @@ def install_ssh_script() -> str: os.makedirs(SHARED_DIR, exist_ok=True) results = [] - # Copy ssh.py dst = os.path.join(SHARED_DIR, "ssh.py") if os.path.exists(SSH_SCRIPT_SRC): - shutil.copy2(SSH_SCRIPT_SRC, dst) + _copy_file(SSH_SCRIPT_SRC, dst, executable=True) log.info(f"ssh.py installed: {dst}") results.append(f"ssh.py installed: {dst}") elif os.path.exists(dst): @@ -96,10 +132,9 @@ def install_ssh_script() -> str: else: results.append("ERROR: ssh.py source not found") - # Copy encryption.py enc_dst = os.path.join(SHARED_DIR, "encryption.py") if os.path.exists(ENCRYPTION_SRC): - shutil.copy2(ENCRYPTION_SRC, enc_dst) + _copy_file(ENCRYPTION_SRC, enc_dst) log.info(f"encryption.py installed: {enc_dst}") results.append(f"encryption.py installed: {enc_dst}") elif os.path.exists(enc_dst): @@ -110,22 +145,59 @@ def install_ssh_script() -> str: return "\n".join(results) -def install_skill() -> str: +def install_claude_skill() -> str: """Install /ssh skill for Claude Code.""" - os.makedirs(SKILL_DST_DIR, exist_ok=True) - if os.path.exists(SKILL_SRC): - shutil.copy2(SKILL_SRC, SKILL_DST) - log.info(f"Skill installed: {SKILL_DST}") - return f"Skill installed: {SKILL_DST}" - # Fallback: check existing - if os.path.exists(SKILL_DST): - return f"Skill already exists: {SKILL_DST}" - # Generate minimal skill + os.makedirs(CLAUDE_SKILL_DST_DIR, exist_ok=True) + if os.path.exists(CLAUDE_SKILL_SRC): + _copy_file(CLAUDE_SKILL_SRC, CLAUDE_SKILL_DST) + log.info(f"Claude skill installed: {CLAUDE_SKILL_DST}") + return f"Claude skill installed: {CLAUDE_SKILL_DST}" + if os.path.exists(CLAUDE_SKILL_DST): + return f"Claude skill already exists: {CLAUDE_SKILL_DST}" + skill_content = _generate_skill_content() - with open(SKILL_DST, "w", encoding="utf-8") as f: + with open(CLAUDE_SKILL_DST, "w", encoding="utf-8") as f: f.write(skill_content) - log.info(f"Skill generated: {SKILL_DST}") - return f"Skill generated: {SKILL_DST}" + log.info(f"Claude skill generated: {CLAUDE_SKILL_DST}") + return f"Claude skill generated: {CLAUDE_SKILL_DST}" + + +def install_codex_skill() -> str: + """Install ServerManager skill package for Codex and the local wrapper.""" + results = [] + + if os.path.isdir(CODEX_SKILL_SRC_DIR): + _copy_tree(CODEX_SKILL_SRC_DIR, CODEX_SKILL_DST_DIR) + for rel_path in [ + os.path.join("scripts", "server-manager-doctor.sh"), + os.path.join("scripts", "server-manager-doctor.cmd"), + os.path.join("scripts", "codex-ssh-wrapper.sh"), + os.path.join("scripts", "codex-ssh-wrapper.cmd"), + ]: + _ensure_executable(os.path.join(CODEX_SKILL_DST_DIR, rel_path)) + log.info(f"Codex skill installed: {CODEX_SKILL_DST_DIR}") + results.append(f"Codex skill installed: {CODEX_SKILL_DST_DIR}") + elif os.path.exists(CODEX_SKILL_ENTRY): + results.append(f"Codex skill already exists: {CODEX_SKILL_DST_DIR}") + else: + results.append("ERROR: Codex skill source not found") + + wrapper_src = CODEX_WRAPPER_SRC_CMD if sys.platform == "win32" else CODEX_WRAPPER_SRC_SH + if os.path.exists(wrapper_src): + _copy_file(wrapper_src, CODEX_WRAPPER_DST, executable=(sys.platform != "win32")) + log.info(f"Codex wrapper installed: {CODEX_WRAPPER_DST}") + results.append(f"Codex wrapper installed: {CODEX_WRAPPER_DST}") + elif os.path.exists(CODEX_WRAPPER_DST): + results.append(f"Codex wrapper already exists: {CODEX_WRAPPER_DST}") + else: + results.append("ERROR: Codex wrapper source not found") + + return "\n".join(results) + + +def install_skill() -> str: + """Backward-compatible alias for the Claude /ssh skill installer.""" + return install_claude_skill() def generate_ssh_key() -> str: @@ -135,7 +207,6 @@ def generate_ssh_key() -> str: os.makedirs(os.path.dirname(SSH_KEY_PATH), exist_ok=True) - import subprocess try: subprocess.run( ["ssh-keygen", "-t", "ed25519", "-f", SSH_KEY_PATH, @@ -156,11 +227,7 @@ def generate_ssh_key() -> str: def install_global_claude_md() -> str: - """Add/update server manager section in global ~/.claude/CLAUDE.md. - - Uses start/end markers to safely replace existing block without duplication. - """ - import re + """Add/update server manager section in global ~/.claude/CLAUDE.md.""" os.makedirs(os.path.dirname(GLOBAL_CLAUDE_MD), exist_ok=True) existing = "" @@ -174,29 +241,28 @@ def install_global_claude_md() -> str: ) if pattern.search(existing): - # Блок уже есть — заменяем на актуальную версию updated = pattern.sub(GLOBAL_CLAUDE_MD_BLOCK.strip(), existing) with open(GLOBAL_CLAUDE_MD, "w", encoding="utf-8") as f: f.write(updated) log.info(f"Global CLAUDE.md block updated: {GLOBAL_CLAUDE_MD}") return f"Global CLAUDE.md block updated: {GLOBAL_CLAUDE_MD}" - else: - # Блока нет — добавляем в конец - with open(GLOBAL_CLAUDE_MD, "a", encoding="utf-8") as f: - if existing and not existing.endswith("\n"): - f.write("\n") - f.write("\n" + GLOBAL_CLAUDE_MD_BLOCK) - log.info(f"Global CLAUDE.md block added: {GLOBAL_CLAUDE_MD}") - return f"Global CLAUDE.md block added: {GLOBAL_CLAUDE_MD}" + + with open(GLOBAL_CLAUDE_MD, "a", encoding="utf-8") as f: + if existing and not existing.endswith("\n"): + f.write("\n") + f.write("\n" + GLOBAL_CLAUDE_MD_BLOCK) + log.info(f"Global CLAUDE.md block added: {GLOBAL_CLAUDE_MD}") + return f"Global CLAUDE.md block added: {GLOBAL_CLAUDE_MD}" def install_all() -> list[str]: - """Full setup — install everything.""" + """Full setup — install everything for Claude Code and Codex.""" results = [] steps = [ ("ssh_script", install_ssh_script), - ("skill", install_skill), + ("claude_skill", install_claude_skill), + ("codex_skill", install_codex_skill), ("ssh_key", generate_ssh_key), ("global_claude_md", install_global_claude_md), ] diff --git a/core/i18n.py b/core/i18n.py index 748b446..02ed086 100644 --- a/core/i18n.py +++ b/core/i18n.py @@ -46,7 +46,7 @@ _EN = { "about_desc": ( "Desktop application for managing remote servers.\n" "SSH terminal, SFTP file transfer, key management,\n" - "encrypted credentials, and Claude Code integration." + "encrypted credentials, and Claude Code / Codex integration." ), "about_features_title": "⚡ Features", "about_features": ( @@ -56,13 +56,13 @@ _EN = { "• TOTP / 2FA (Google Authenticator)\n" "• Encrypted credentials (Fernet)\n" "• Automatic backups\n" - "• Claude Code integration" + "• Claude Code and Codex integration" ), "about_howto_title": "🚀 Quick Start", "about_howto": ( "1. Click \"+ Add\" to add a server\n" "2. Select server → Terminal / Files\n" - "3. Setup tab → Claude Code integration" + "3. Setup tab → Claude Code / Codex integration" ), "version": "Version", "author": "Author", @@ -157,6 +157,12 @@ _EN = { "no_public_key": "[!] No public key to copy", # Setup + "agent_integration": "AI Agent Integration", + "agent_desc": ( + "Setup everything so Claude Code and Codex can manage your servers via shared local skills.\n" + "ServerManager, Claude Code, and Codex share the same servers.json — add a server here,\n" + "both agents see it immediately." + ), "claude_integration": "Claude Code Integration", "claude_desc": ( "Setup everything so Claude Code can manage your servers via /ssh skill.\n" @@ -169,11 +175,16 @@ _EN = { "status_ssh_script": "ssh.py (CLI tool)", "status_encryption": "Encryption module", "status_skill": "/ssh skill for Claude Code", + "status_claude_skill": "/ssh skill for Claude Code", + "status_codex_skill": "ServerManager skill for Codex", + "status_codex_wrapper": "Codex wrapper (codex-ssh)", "status_ssh_key": "SSH key (ed25519)", "install_everything": "Install Everything", "installing_all": "Installing...", "install_ssh_py": "ssh.py", "install_skill": "/ssh skill", + "install_claude_skill": "Claude skill", + "install_codex_skill": "Codex skill", "install_ssh_key": "SSH key", "refresh": "Refresh", "configuration": "Configuration", @@ -183,7 +194,7 @@ _EN = { "select_backup": "Select backup...", "no_backups": "No backups", "restore": "Restore", - "install_done": "Done! Claude Code can now use /ssh to manage your servers.", + "install_done": "Done! Claude Code and Codex can now use ServerManager to manage your servers.", "config_changed": "Config path changed: {path}", "backup_created": "Backup created: {name}", "backup_failed": "Backup failed: {e}", @@ -603,7 +614,7 @@ _RU = { "about_desc": ( "Настольное приложение для управления удалёнными серверами.\n" "SSH-терминал, SFTP-передача файлов, управление ключами,\n" - "шифрование паролей и интеграция с Claude Code." + "шифрование паролей и интеграция с Claude Code / Codex." ), "about_features_title": "⚡ Возможности", "about_features": ( @@ -613,13 +624,13 @@ _RU = { "• TOTP / 2FA (Google Authenticator)\n" "• Шифрование паролей (Fernet)\n" "• Автоматические бэкапы\n" - "• Интеграция с Claude Code" + "• Интеграция с Claude Code и Codex" ), "about_howto_title": "🚀 Быстрый старт", "about_howto": ( "1. Нажмите \"+ Добавить\" для добавления сервера\n" "2. Выберите сервер → Терминал / Файлы\n" - "3. Вкладка Настройка → интеграция Claude Code" + "3. Вкладка Настройка → интеграция Claude Code / Codex" ), "version": "Версия", "author": "Автор", @@ -714,6 +725,12 @@ _RU = { "no_public_key": "[!] Нет публичного ключа", # Setup + "agent_integration": "Интеграция AI-агентов", + "agent_desc": ( + "Настройте всё, чтобы Claude Code и Codex могли управлять серверами через локальные skills.\n" + "ServerManager, Claude Code и Codex используют один и тот же servers.json — добавьте сервер здесь,\n" + "и оба агента увидят его сразу." + ), "claude_integration": "Интеграция с Claude Code", "claude_desc": ( "Настройте всё, чтобы Claude Code мог управлять серверами через скилл /ssh.\n" @@ -726,11 +743,16 @@ _RU = { "status_ssh_script": "ssh.py (CLI-утилита)", "status_encryption": "Модуль шифрования", "status_skill": "Скилл /ssh для Claude Code", + "status_claude_skill": "Скилл /ssh для Claude Code", + "status_codex_skill": "Скилл ServerManager для Codex", + "status_codex_wrapper": "Обёртка Codex (codex-ssh)", "status_ssh_key": "SSH-ключ (ed25519)", "install_everything": "Установить всё", "installing_all": "Установка...", "install_ssh_py": "ssh.py", "install_skill": "Скилл /ssh", + "install_claude_skill": "Скилл Claude", + "install_codex_skill": "Скилл Codex", "install_ssh_key": "SSH-ключ", "refresh": "Обновить", "configuration": "Конфигурация", @@ -740,7 +762,7 @@ _RU = { "select_backup": "Выберите бэкап...", "no_backups": "Нет бэкапов", "restore": "Восстановить", - "install_done": "Готово! Claude Code теперь может использовать /ssh для управления серверами.", + "install_done": "Готово! Claude Code и Codex теперь могут использовать ServerManager для управления серверами.", "config_changed": "Путь конфига изменён: {path}", "backup_created": "Бэкап создан: {name}", "backup_failed": "Ошибка бэкапа: {e}", @@ -1160,7 +1182,7 @@ _ZH = { "about_desc": ( "用于管理远程服务器的桌面应用程序。\n" "SSH终端、SFTP文件传输、密钥管理、\n" - "凭据加密以及Claude Code集成。" + "凭据加密以及Claude Code / Codex集成。" ), "about_features_title": "⚡ 功能特点", "about_features": ( @@ -1170,13 +1192,13 @@ _ZH = { "• TOTP / 2FA(Google Authenticator)\n" "• 凭据加密(Fernet)\n" "• 自动备份\n" - "• Claude Code集成" + "• Claude Code 和 Codex 集成" ), "about_howto_title": "🚀 快速开始", "about_howto": ( "1. 点击\"+ 添加\"来添加服务器\n" "2. 选择服务器 → 终端 / 文件\n" - "3. 设置标签 → Claude Code集成" + "3. 设置标签 → Claude Code / Codex 集成" ), "version": "版本", "author": "作者", @@ -1271,6 +1293,12 @@ _ZH = { "no_public_key": "[!] 没有公钥可复制", # Setup + "agent_integration": "AI代理集成", + "agent_desc": ( + "完成设置后,Claude Code 和 Codex 都可以通过共享的本地技能来管理您的服务器。\n" + "ServerManager、Claude Code 和 Codex 共用同一个 servers.json — 在此添加服务器后,\n" + "两个代理都会立即看到。" + ), "claude_integration": "Claude Code集成", "claude_desc": ( "设置一切以便Claude Code通过/ssh技能管理您的服务器。\n" @@ -1283,11 +1311,16 @@ _ZH = { "status_ssh_script": "ssh.py(CLI工具)", "status_encryption": "加密模块", "status_skill": "Claude Code的/ssh技能", + "status_claude_skill": "Claude Code 的 /ssh 技能", + "status_codex_skill": "Codex 的 ServerManager 技能", + "status_codex_wrapper": "Codex 包装器(codex-ssh)", "status_ssh_key": "SSH密钥(ed25519)", "install_everything": "全部安装", "installing_all": "安装中...", "install_ssh_py": "ssh.py", "install_skill": "/ssh技能", + "install_claude_skill": "Claude 技能", + "install_codex_skill": "Codex 技能", "install_ssh_key": "SSH密钥", "refresh": "刷新", "configuration": "配置", @@ -1297,7 +1330,7 @@ _ZH = { "select_backup": "选择备份...", "no_backups": "无备份", "restore": "恢复", - "install_done": "完成!Claude Code现在可以使用/ssh来管理您的服务器。", + "install_done": "完成!Claude Code 和 Codex 现在都可以使用 ServerManager 来管理您的服务器。", "config_changed": "配置路径已更改:{path}", "backup_created": "备份已创建:{name}", "backup_failed": "备份失败:{e}", diff --git a/gui/tabs/setup_tab.py b/gui/tabs/setup_tab.py index a784983..0828186 100644 --- a/gui/tabs/setup_tab.py +++ b/gui/tabs/setup_tab.py @@ -1,5 +1,5 @@ """ -Setup tab — one-click installation for Claude Code integration. +Setup tab — one-click installation for local AI agent integration. Includes configuration path management and backup/restore. """ @@ -8,7 +8,14 @@ import threading from datetime import datetime from tkinter import filedialog, messagebox import customtkinter as ctk -from core.claude_setup import check_status, install_all, install_ssh_script, install_skill, generate_ssh_key +from core.claude_setup import ( + check_status, + generate_ssh_key, + install_all, + install_claude_skill, + install_codex_skill, + install_ssh_script, +) from core.i18n import t from core.icons import icon_text, make_icon_button from core.logger import log @@ -25,13 +32,13 @@ class SetupTab(ctk.CTkFrame): # Header self.header_label = ctk.CTkLabel( - self._scroll, text=t("claude_integration"), + self._scroll, text=t("agent_integration"), font=ctk.CTkFont(size=20, weight="bold") ) self.header_label.pack(padx=20, pady=(20, 5)) self.desc_label = ctk.CTkLabel( - self._scroll, text=t("claude_desc"), + self._scroll, text=t("agent_desc"), text_color="#9ca3af", justify="center" ) self.desc_label.pack(padx=20, pady=(0, 15)) @@ -53,7 +60,9 @@ class SetupTab(ctk.CTkFrame): ("servers_json", "status_servers_json"), ("ssh_script", "status_ssh_script"), ("encryption", "status_encryption"), - ("skill_installed", "status_skill"), + ("claude_skill_installed", "status_claude_skill"), + ("codex_skill_installed", "status_codex_skill"), + ("codex_wrapper_installed", "status_codex_wrapper"), ("ssh_key_exists", "status_ssh_key"), ] for key, i18n_key in status_items: @@ -82,17 +91,40 @@ class SetupTab(ctk.CTkFrame): ind_frame = ctk.CTkFrame(btn_frame, fg_color="transparent") ind_frame.pack(fill="x") - self.ssh_py_btn = make_icon_button(ind_frame, "confirm", t("install_ssh_py"), width=110, fg_color="#6b7280", - command=self._install_script) + top_btn_row = ctk.CTkFrame(ind_frame, fg_color="transparent") + top_btn_row.pack(fill="x", pady=(0, 5)) + + self.ssh_py_btn = make_icon_button( + top_btn_row, "confirm", t("install_ssh_py"), width=120, fg_color="#6b7280", + command=self._install_script + ) self.ssh_py_btn.pack(side="left", padx=(0, 5)) - self.skill_btn = make_icon_button(ind_frame, "confirm", t("install_skill"), width=110, fg_color="#6b7280", - command=self._install_skill) - self.skill_btn.pack(side="left", padx=5) - self.ssh_key_btn = make_icon_button(ind_frame, "confirm", t("install_ssh_key"), width=110, fg_color="#6b7280", - command=self._gen_key) - self.ssh_key_btn.pack(side="left", padx=5) - self.refresh_btn = make_icon_button(ind_frame, "refresh", t("refresh"), width=90, fg_color="#3b82f6", - command=self._refresh_status) + + self.claude_skill_btn = make_icon_button( + top_btn_row, "confirm", t("install_claude_skill"), width=130, fg_color="#6b7280", + command=self._install_claude_skill + ) + self.claude_skill_btn.pack(side="left", padx=5) + + self.codex_skill_btn = make_icon_button( + top_btn_row, "confirm", t("install_codex_skill"), width=130, fg_color="#6b7280", + command=self._install_codex_skill + ) + self.codex_skill_btn.pack(side="left", padx=5) + + bottom_btn_row = ctk.CTkFrame(ind_frame, fg_color="transparent") + bottom_btn_row.pack(fill="x") + + self.ssh_key_btn = make_icon_button( + bottom_btn_row, "confirm", t("install_ssh_key"), width=120, fg_color="#6b7280", + command=self._gen_key + ) + self.ssh_key_btn.pack(side="left", padx=(0, 5)) + + self.refresh_btn = make_icon_button( + bottom_btn_row, "refresh", t("refresh"), width=90, fg_color="#3b82f6", + command=self._refresh_status + ) self.refresh_btn.pack(side="right") # ── Monitoring section ───────────────────────── @@ -328,8 +360,18 @@ class SetupTab(ctk.CTkFrame): self._log(msg) self._refresh_status() + def _install_claude_skill(self): + msg = install_claude_skill() + self._log(msg) + self._refresh_status() + + def _install_codex_skill(self): + msg = install_codex_skill() + self._log(msg) + self._refresh_status() + def _install_skill(self): - msg = install_skill() + msg = install_claude_skill() self._log(msg) self._refresh_status()