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