Split the Windows script into modules; drop the Arkylx Index pieces

claude-mode.ps1 was 2,407 lines. It is now 153: the help block, parameters,
paths, the managed-key list, a loader, and the dispatch. The rest moved,
verbatim, into eleven files under lib/ - providers, output, files, core,
vault, switch, guards, health, catalogue, commands, menu - dot-sourced into
the script's scope in their original order, with the same check as the bash
split that every original line landed in exactly one file. Inside a module
$PSScriptRoot is lib\, so the one path beside the main script
(providers.json) now goes through $script:Here.

install.ps1 ships lib\, clearing old modules first. The Windows suite
parses every module and install.ps1 (36 checks, all green on Windows
PowerShell 5.1); install.ps1 is parsed but never run, since it edits the
real profile and User PATH.

linux/bootstrap.sh and scripts/build-package.ps1 existed only to build and
serve packages for the Arkylx Index. Both installers fetch the repository's
own archive, so a push to master is the release; the two scripts, the dist/
ignore and their mentions in the docs are gone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
smoido
2026-09-15 02:06:45 +03:00
co-authored by Claude Opus 5
parent 441166d003
commit 4091746d4e
21 changed files with 2421 additions and 2527 deletions
+85
View File
@@ -0,0 +1,85 @@
# lib/vault.ps1 - the DPAPI key vault, and a preset's auth.
#
# Part of claude-mode.ps1, which dot-sources it into its own script scope after
# the settings at its top. Not meant to run on its own. ASCII only: Windows
# PowerShell 5.1 reads a .ps1 without a BOM as ANSI. $PSScriptRoot here would be
# lib\, so paths beside the main script go through $script:Here.
# ---------------------------------------------------------------------------
# Vault (DPAPI: CurrentUser scope)
# ---------------------------------------------------------------------------
function Get-VaultPath { param([string] $Ref) return (Join-Path $script:VaultDir "$Ref.cred") }
function ConvertFrom-SecureStringPlain {
param([System.Security.SecureString] $Secure)
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($Secure)
try { return [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) }
finally { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) }
}
function Set-VaultKey {
param([string] $Ref, [string] $Key = '')
Initialize-Root
if ($Key) {
Write-Warn2 'the key was given on the command line, so it is in this shell history - the hidden prompt leaves no trace'
$plain = $Key
# The vault is written from a SecureString (DPAPI), so the inline form
# has to produce one too.
$secure = ConvertTo-SecureString -String $Key -AsPlainText -Force
} else {
Write-Host "Paste the API key for ref '$Ref' (input hidden):"
$secure = Read-Host -AsSecureString
$plain = ConvertFrom-SecureStringPlain $secure
}
if ([string]::IsNullOrWhiteSpace($plain)) { throw 'empty key, aborted' }
# A hidden prompt will happily swallow a mis-paste. Guard the two shapes that
# are never a real key, because the failure is otherwise invisible until the
# provider answers 401 and the UI just spins.
if ($plain -match '\s') {
throw "that value contains whitespace, so it is not an API key (a pasted command line?). Nothing was stored."
}
if ($plain -like 'claude-mode*') {
throw "that value is a claude-mode command, not an API key. Nothing was stored."
}
if ($plain.Length -lt 16) {
$unit = if ($plain.Length -eq 1) { 'character' } else { 'characters' }
Write-Warn2 "that key is only $($plain.Length) $unit - unusually short. Storing anyway."
}
if ($Ref -eq 'openrouter' -and $plain -notlike 'sk-or-*') {
Write-Warn2 "key does not start with 'sk-or-' - storing anyway"
}
$path = Get-VaultPath $Ref
# ConvertFrom-SecureString with no -Key uses DPAPI, CurrentUser scope.
ConvertFrom-SecureString -SecureString $secure |
Set-Content -LiteralPath $path -Encoding ASCII -NoNewline
Protect-FileAcl $path
Write-Ok "stored DPAPI-encrypted key at $path"
}
function Get-VaultKey {
param([string] $Ref)
$path = Get-VaultPath $Ref
if (-not (Test-Path -LiteralPath $path)) { return $null }
$blob = (Get-Content -LiteralPath $path -Raw).Trim()
if ([string]::IsNullOrWhiteSpace($blob)) { return $null }
try { return ConvertFrom-SecureStringPlain (ConvertTo-SecureString $blob) }
catch { return $null }
}
function Format-KeyMask {
param([string] $Key)
if ([string]::IsNullOrEmpty($Key)) { return '(none)' }
if ($Key.Length -le 12) { return '****' }
return ($Key.Substring(0, 8) + '...' + $Key.Substring($Key.Length - 4))
}
function Get-PresetAuth {
param($Preset)
$auth = $Preset['auth']
if ($null -eq $auth) { $auth = [ordered]@{ mode = 'vault'; keyRef = 'openrouter' } }
if (-not $auth.Contains('mode')) { $auth['mode'] = 'vault' }
return $auth
}