diff --git a/README.md b/README.md index 017e359..d62ce61 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ Chezmoi использует отдельный source dir `chezmoi/` внутр - **UTF-8** - `chcp 65001` + `Console.InputEncoding/OutputEncoding` только для pwsh 7, не для PS 5.1 - **Codex Proxy shortcut** - Start Menu ярлык для запуска Codex GUI с proxy-переменными - **Claude Proxy** - ярлык меню "Пуск" для запуска Claude Desktop с переменными прокси +- **Kimi Proxy** - ярлык меню "Пуск" для запуска Kimi Desktop с параметрами прокси Electron - **Extension points** - `~/.config/powershell/profile.extra.ps1` и `profile.d/*.ps1` для локальных добавок ### AI-инструменты (кросс-платформа) @@ -128,6 +129,11 @@ Codex и Claude Code могут брать прокси из своих наст Если Claude уже запущен обычным способом, его нужно полностью закрыть перед запуском через `Claude Proxy`. +Ярлык `Kimi Proxy` запускает Kimi Desktop (`%LOCALAPPDATA%\Programs\Kimi\Kimi.exe`) +с теми же параметрами Electron `--proxy-server` и `--proxy-bypass-list`, что и +`Codex Proxy`. Если Kimi уже запущен, его нужно полностью закрыть перед запуском +через `Kimi Proxy`. + Список хостов, которые идут мимо прокси, продублирован в профилях Codex, PowerShell и VS Code: одного общего файла у Windows и WSL нет. Эталон - `DEFAULT_NO_PROXY_EXTRA` в `proxy-client/.local/bin/proxy-client`; меняя его, @@ -212,6 +218,7 @@ PowerShell и VS Code: одного общего файла у Windows и WSL н | `~/.local/bin/start-claude-proxy.ps1` | Скрипт запуска Claude Desktop для Windows с переменными прокси и параметрами Electron | | `~/.local/bin/start-claude-proxy.vbs` | Скрытый запуск PowerShell-скрипта без привязки Claude к видимому окну терминала | | `~/.local/bin/start-codex-proxy.ps1` | Запуск Codex для Windows через системный идентификатор; прокси Electron задается параметрами, а `codex.exe` читает `~/.codex/.env` | +| `~/.local/bin/start-kimi-proxy.ps1` | Запуск Kimi Desktop для Windows с параметрами прокси Electron | | `~/.local/bin/start-chatgpt-proxy` | Запуск Linux-приложения ChatGPT через активную настройку `proxy-client` | | `~/.local/share/applications/chatgpt-proxy.desktop` | Отдельный Linux-ярлык `ChatGPT Proxy`, не подменяющий штатный `ChatGPT` | | `~/.config/powershell/profile.extra.ps1` | Локальные дополнения к pwsh-профилю | diff --git a/chezmoi/.chezmoiscripts/run_32-create-kimi-proxy-shortcut.ps1 b/chezmoi/.chezmoiscripts/run_32-create-kimi-proxy-shortcut.ps1 new file mode 100644 index 0000000..8189a13 --- /dev/null +++ b/chezmoi/.chezmoiscripts/run_32-create-kimi-proxy-shortcut.ps1 @@ -0,0 +1,66 @@ +$ErrorActionPreference = 'Stop' + +if (-not ($IsWindows -or $PSVersionTable.PSEdition -eq 'Desktop')) { + return +} + +$LauncherPath = Join-Path $HOME '.local\bin\start-kimi-proxy.ps1' +$ProgramsPath = [Environment]::GetFolderPath('Programs') +if ([string]::IsNullOrWhiteSpace($ProgramsPath)) { + Write-Warning 'Start Menu Programs folder was not found.' + return +} + +$ShortcutPath = Join-Path $ProgramsPath 'Kimi Proxy.lnk' +$PwshAliasPath = Join-Path $env:LOCALAPPDATA 'Microsoft\WindowsApps\pwsh.exe' +$PwshCommand = Get-Command pwsh -ErrorAction SilentlyContinue +if (Test-Path -LiteralPath $PwshAliasPath) { + $TargetPath = $PwshAliasPath +} elseif ($null -ne $PwshCommand) { + $TargetPath = $PwshCommand.Source +} else { + $TargetPath = 'pwsh.exe' +} + +$Arguments = "-NoLogo -NoProfile -ExecutionPolicy Bypass -File `"$LauncherPath`"" +$WorkingDirectory = $env:USERPROFILE +$IconLocation = "$TargetPath,0" + +$KimiExecutable = Join-Path $env:LOCALAPPDATA 'Programs\Kimi\Kimi.exe' +if (Test-Path -LiteralPath $KimiExecutable) { + $IconLocation = "$KimiExecutable,0" +} + +New-Item -ItemType Directory -Path (Split-Path -Parent $ShortcutPath) -Force | Out-Null + +$Shell = New-Object -ComObject WScript.Shell +$Shortcut = $Shell.CreateShortcut($ShortcutPath) + +$Changed = $false +if ($Shortcut.TargetPath -ne $TargetPath) { + $Shortcut.TargetPath = $TargetPath + $Changed = $true +} +if ($Shortcut.Arguments -ne $Arguments) { + $Shortcut.Arguments = $Arguments + $Changed = $true +} +if ($Shortcut.WorkingDirectory -ne $WorkingDirectory) { + $Shortcut.WorkingDirectory = $WorkingDirectory + $Changed = $true +} +if ($Shortcut.IconLocation -ne $IconLocation) { + $Shortcut.IconLocation = $IconLocation + $Changed = $true +} +if ($Shortcut.Description -ne 'Launch Kimi with hproxy.local proxy environment') { + $Shortcut.Description = 'Launch Kimi with hproxy.local proxy environment' + $Changed = $true +} + +if ($Changed -or -not (Test-Path -LiteralPath $ShortcutPath)) { + $Shortcut.Save() + Write-Host "Kimi Proxy shortcut: updated $ShortcutPath" +} else { + Write-Host 'Kimi Proxy shortcut: already up to date.' +} diff --git a/chezmoi/dot_local/bin/start-kimi-proxy.ps1 b/chezmoi/dot_local/bin/start-kimi-proxy.ps1 new file mode 100644 index 0000000..0d600b3 --- /dev/null +++ b/chezmoi/dot_local/bin/start-kimi-proxy.ps1 @@ -0,0 +1,38 @@ +$ErrorActionPreference = 'Stop' + +$ProxyHostName = 'hproxy.local' +$ProxyPort = 12334 +$HttpProxy = "http://${ProxyHostName}:${ProxyPort}" +$ProxyBypassList = "${ProxyHostName};localhost;127.0.0.1;[::1];*.concept-software.ru;dementev.space;*.dementev.space" + +function Get-KimiApplication { + $Executable = Join-Path $env:LOCALAPPDATA 'Programs\Kimi\Kimi.exe' + if (-not (Test-Path -LiteralPath $Executable)) { + throw "Kimi Desktop не найден: ожидался файл '$Executable'." + } + + return $Executable +} + +function Assert-KimiNotRunning { + param( + [Parameter(Mandatory)] + [string]$Executable + ) + + $RunningProcess = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | + Where-Object { $_.ExecutablePath -eq $Executable } | + Select-Object -First 1 + + if ($null -ne $RunningProcess) { + throw 'Kimi уже запущен. Полностью закройте приложение и повторно откройте ярлык Kimi Proxy.' + } +} + +$KimiExecutable = Get-KimiApplication +Assert-KimiNotRunning -Executable $KimiExecutable + +Start-Process -FilePath $KimiExecutable -ArgumentList @( + "--proxy-server=${HttpProxy}", + "--proxy-bypass-list=${ProxyBypassList}" +) diff --git a/tests/test_proxy_env_sync.py b/tests/test_proxy_env_sync.py index eda142f..fcbd94c 100644 --- a/tests/test_proxy_env_sync.py +++ b/tests/test_proxy_env_sync.py @@ -38,6 +38,7 @@ COPIES = ( BYPASS_FILES = ( "chezmoi/dot_local/bin/start-claude-proxy.ps1", "chezmoi/dot_local/bin/start-codex-proxy.ps1", + "chezmoi/dot_local/bin/start-kimi-proxy.ps1", ) BYPASS_RE = re.compile(r'^\$ProxyBypassList = "\$\{ProxyHostName\};(.+)"\s*$', re.M) @@ -74,7 +75,9 @@ class NoProxySyncTest(unittest.TestCase): match, "{}: не найден ProxyBypassList".format(relative_path) ) values.append(match.group(1)) - self.assertEqual(values[0], values[1], "ProxyBypassList разошёлся") + self.assertEqual( + len(set(values)), 1, "ProxyBypassList разошёлся между файлами" + ) if __name__ == "__main__":