diff --git a/claude.ps1 b/claude.ps1 index 3523cf5..8ef5c75 100644 --- a/claude.ps1 +++ b/claude.ps1 @@ -2,6 +2,46 @@ param( [string]$ApiKey ) +function ConvertTo-Hashtable { + param( + [Parameter(ValueFromPipeline = $true)] + [object]$InputObject + ) + + if ($null -eq $InputObject) { + return $null + } + + if ($InputObject -is [System.Collections.IDictionary]) { + $table = @{} + foreach ($key in $InputObject.Keys) { + $table[$key] = ConvertTo-Hashtable $InputObject[$key] + } + return $table + } + + if ( + $InputObject -is [System.Collections.IEnumerable] -and + -not ($InputObject -is [string]) + ) { + $items = @() + foreach ($item in $InputObject) { + $items += ,(ConvertTo-Hashtable $item) + } + return $items + } + + if ($InputObject -is [psobject]) { + $table = @{} + foreach ($property in $InputObject.PSObject.Properties) { + $table[$property.Name] = ConvertTo-Hashtable $property.Value + } + return $table + } + + return $InputObject +} + if ([string]::IsNullOrWhiteSpace($ApiKey)) { $secure = Read-Host "请输入 API Key" -AsSecureString $ptr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure) @@ -27,19 +67,15 @@ $data = @{} if (Test-Path $SettingsFile) { $raw = Get-Content $SettingsFile -Raw -ErrorAction Stop if (-not [string]::IsNullOrWhiteSpace($raw)) { - $parsed = $raw | ConvertFrom-Json -Depth 100 - - $data = @{} - foreach ($p in $parsed.PSObject.Properties) { - $data[$p.Name] = $p.Value - } + $parsed = $raw | ConvertFrom-Json + $data = ConvertTo-Hashtable $parsed } } -if ($data.ContainsKey("env") -and $null -ne $data["env"]) { +if ($data.ContainsKey("env") -and $data["env"] -is [System.Collections.IDictionary]) { $envTable = @{} - foreach ($p in $data["env"].PSObject.Properties) { - $envTable[$p.Name] = $p.Value + foreach ($key in $data["env"].Keys) { + $envTable[$key] = $data["env"][$key] } } else { $envTable = @{} diff --git a/tests/test_config_dirs.py b/tests/test_config_dirs.py index f2cae29..45cddfc 100644 --- a/tests/test_config_dirs.py +++ b/tests/test_config_dirs.py @@ -1,4 +1,9 @@ import pathlib +import json +import os +import shutil +import subprocess +import tempfile import unittest @@ -36,6 +41,46 @@ class ConfigDirTests(unittest.TestCase): self.assertIn("`CODEX_HOME`", content) self.assertIn("`CLAUDE_CONFIG_DIR`", content) + def test_claude_ps1_preserves_existing_env_on_windows_powershell(self) -> None: + temp_dir = tempfile.mkdtemp(prefix="provider-setup-claude-") + self.addCleanup(lambda: shutil.rmtree(temp_dir, ignore_errors=True)) + + settings_file = pathlib.Path(temp_dir) / "settings.json" + settings_file.write_text( + json.dumps({"env": {"EXISTING": "1"}}, ensure_ascii=False), + encoding="utf-8", + ) + + env = os.environ.copy() + env["CLAUDE_CONFIG_DIR"] = temp_dir + + completed = subprocess.run( + [ + "powershell", + "-ExecutionPolicy", + "Bypass", + "-File", + str(REPO_ROOT / "claude.ps1"), + "-ApiKey", + "test-key", + ], + cwd=REPO_ROOT, + env=env, + capture_output=True, + text=True, + check=False, + ) + + self.assertEqual( + completed.returncode, + 0, + msg=f"stdout:\n{completed.stdout}\n\nstderr:\n{completed.stderr}", + ) + + data = json.loads(settings_file.read_text(encoding="utf-8-sig")) + self.assertEqual(data["env"]["EXISTING"], "1") + self.assertEqual(data["env"]["ANTHROPIC_AUTH_TOKEN"], "test-key") + if __name__ == "__main__": unittest.main()