feat(scripts): add cross-platform build and installation tools
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
function Invoke-PluginCommand {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$Command,
|
||||
[Parameter(Mandatory=$true)][string[]]$Arguments,
|
||||
[switch]$DryRun
|
||||
)
|
||||
$display = "$Command $($Arguments -join ' ')"
|
||||
if ($DryRun) { Write-Output "DRYRUN $display"; return }
|
||||
& $Command @Arguments
|
||||
if ($LASTEXITCODE -ne 0) { throw "Plugin command failed: $display" }
|
||||
}
|
||||
|
||||
function Get-ConfiguredPluginEntries {
|
||||
param([Parameter(Mandatory=$true)][string]$RepositoryRoot)
|
||||
$manifest = Join-Path $RepositoryRoot 'adapters/plugins.tsv'
|
||||
if (-not (Test-Path -LiteralPath $manifest)) { throw "Plugin manifest is missing: $manifest" }
|
||||
Import-Csv -LiteralPath $manifest -Delimiter ([char]9) | ForEach-Object {
|
||||
$_.claude_plugin = $_.claude_plugin.Trim()
|
||||
$_.codex_plugin = $_.codex_plugin.Trim()
|
||||
$_
|
||||
}
|
||||
}
|
||||
|
||||
function Get-InstalledPlugins {
|
||||
param([ValidateSet('Claude','Codex')][string]$Client)
|
||||
if ($Client -eq 'Claude') {
|
||||
if (-not (Get-Command claude -ErrorAction SilentlyContinue)) { throw 'Claude Code CLI is required to manage Claude plugins.' }
|
||||
$items = (claude plugin list --json | Out-String) | ConvertFrom-Json
|
||||
return @($items)
|
||||
}
|
||||
if (-not (Get-Command codex -ErrorAction SilentlyContinue)) { throw 'Codex CLI is required to manage Codex plugins.' }
|
||||
$items = (codex plugin list --json | Out-String) | ConvertFrom-Json
|
||||
return @($items.installed)
|
||||
}
|
||||
|
||||
function Ensure-CodexMarketplace {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$Source,
|
||||
[Parameter(Mandatory=$true)][string]$MarketplaceName,
|
||||
[switch]$DryRun
|
||||
)
|
||||
if ($DryRun) {
|
||||
Write-Output "DRYRUN codex plugin marketplace add $Source"
|
||||
return
|
||||
}
|
||||
$previousErrorActionPreference = $ErrorActionPreference
|
||||
try {
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$output = @(& codex plugin marketplace add $Source 2>&1)
|
||||
$exitCode = $LASTEXITCODE
|
||||
} catch {
|
||||
$output = @($_)
|
||||
$exitCode = 1
|
||||
} finally {
|
||||
$ErrorActionPreference = $previousErrorActionPreference
|
||||
}
|
||||
$output | ForEach-Object { Write-Output $_ }
|
||||
if ($exitCode -eq 0) { return }
|
||||
|
||||
$text = $output -join [Environment]::NewLine
|
||||
if ($text -match 'marketplace .* already added from a different source') {
|
||||
Write-Output "WARN Codex marketplace '$MarketplaceName' exists from another source; replacing it with: $Source"
|
||||
Invoke-PluginCommand -Command 'codex' -Arguments @('plugin','marketplace','remove',$MarketplaceName)
|
||||
Invoke-PluginCommand -Command 'codex' -Arguments @('plugin','marketplace','add',$Source)
|
||||
return
|
||||
}
|
||||
throw "Plugin command failed: codex plugin marketplace add $Source"
|
||||
}
|
||||
|
||||
function Install-ConfiguredPlugins {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$RepositoryRoot,
|
||||
[ValidateSet('Codex','Claude','Both')][Parameter(Mandatory=$true)][string]$Client,
|
||||
[switch]$DryRun,
|
||||
[switch]$Update
|
||||
)
|
||||
$entries = @(Get-ConfiguredPluginEntries -RepositoryRoot $RepositoryRoot)
|
||||
$clients = if ($Client -eq 'Both') { @('Claude','Codex') } else { @($Client) }
|
||||
|
||||
foreach ($selectedClient in $clients) {
|
||||
$installed = if ($DryRun) { @() } else { @(Get-InstalledPlugins -Client $selectedClient) }
|
||||
foreach ($entry in $entries) {
|
||||
$marketplace = if ($selectedClient -eq 'Claude') { $entry.claude_marketplace.Trim() } else { $entry.codex_marketplace.Trim() }
|
||||
if ($marketplace -eq '-') { $marketplace = '' }
|
||||
$plugin = if ($selectedClient -eq 'Claude') { $entry.claude_plugin } else { $entry.codex_plugin }
|
||||
if ([string]::IsNullOrWhiteSpace($plugin)) { throw ('Plugin selector missing for {0}: {1}' -f $selectedClient, $entry.name) }
|
||||
|
||||
$installedItem = if ($selectedClient -eq 'Claude') { $installed | Where-Object { $_.id -eq $plugin } | Select-Object -First 1 } else { $installed | Where-Object { $_.pluginId -eq $plugin } | Select-Object -First 1 }
|
||||
if ($Update -and $selectedClient -eq 'Claude' -and $null -ne $installedItem) {
|
||||
Invoke-PluginCommand -Command 'claude' -Arguments @('plugin','update',$plugin) -DryRun:$DryRun
|
||||
if (-not $installedItem.enabled) {
|
||||
Invoke-PluginCommand -Command 'claude' -Arguments @('plugin','enable',$plugin) -DryRun:$DryRun
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (-not $Update -and $null -ne $installedItem) {
|
||||
if ($selectedClient -eq 'Claude' -and -not $installedItem.enabled) {
|
||||
Invoke-PluginCommand -Command 'claude' -Arguments @('plugin','enable',$plugin) -DryRun:$DryRun
|
||||
}
|
||||
Write-Output "PASS $selectedClient plugin already installed: $plugin"
|
||||
continue
|
||||
}
|
||||
|
||||
if ($selectedClient -eq 'Claude') {
|
||||
if (-not [string]::IsNullOrWhiteSpace($marketplace)) {
|
||||
Invoke-PluginCommand -Command 'claude' -Arguments @('plugin','marketplace','add',$marketplace) -DryRun:$DryRun
|
||||
}
|
||||
Invoke-PluginCommand -Command 'claude' -Arguments @('plugin','install',$plugin,'--scope','user') -DryRun:$DryRun
|
||||
} else {
|
||||
if (-not [string]::IsNullOrWhiteSpace($marketplace) -and $marketplace -ne 'openai-curated-remote') {
|
||||
$marketplaceName = ($plugin -split '@')[-1]
|
||||
Ensure-CodexMarketplace -Source $marketplace -MarketplaceName $marketplaceName -DryRun:$DryRun
|
||||
}
|
||||
Invoke-PluginCommand -Command 'codex' -Arguments @('plugin','add',$plugin) -DryRun:$DryRun
|
||||
}
|
||||
Write-Output "PASS $selectedClient plugin ensured: $plugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
plugin_installed() {
|
||||
local client=$1 plugin=$2
|
||||
if [ "$client" = claude ]; then
|
||||
claude plugin list --json 2>/dev/null | grep -Fq "\"id\": \"$plugin\""
|
||||
else
|
||||
codex plugin list --json 2>/dev/null | grep -Fq "\"pluginId\": \"$plugin\""
|
||||
fi
|
||||
}
|
||||
|
||||
run_plugin_command() {
|
||||
local dry_run=$1
|
||||
shift
|
||||
if [ "$dry_run" = true ]; then
|
||||
printf 'DRYRUN %s\n' "$*"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_codex_marketplace() {
|
||||
local dry_run=$1 source=$2 marketplace_name=$3 output
|
||||
if [ "$dry_run" = true ]; then
|
||||
printf 'DRYRUN codex plugin marketplace add %s\n' "$source"
|
||||
return 0
|
||||
fi
|
||||
if output=$(codex plugin marketplace add "$source" 2>&1); then
|
||||
[ -z "$output" ] || printf '%s\n' "$output"
|
||||
return 0
|
||||
fi
|
||||
printf '%s\n' "$output" >&2
|
||||
if printf '%s\n' "$output" | grep -Eiq 'marketplace .* already added from a different source'; then
|
||||
printf "WARN Codex marketplace '%s' exists from another source; replacing it with: %s\n" "$marketplace_name" "$source"
|
||||
codex plugin marketplace remove "$marketplace_name"
|
||||
codex plugin marketplace add "$source"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
install_configured_plugins() {
|
||||
local root=$1 client_selection=$2 dry_run=$3 update=$4
|
||||
local manifest="$root/adapters/plugins.tsv"
|
||||
[ -f "$manifest" ] || { printf 'Plugin manifest is missing: %s\n' "$manifest" >&2; return 1; }
|
||||
|
||||
local clients=()
|
||||
case "$client_selection" in
|
||||
codex) clients=(codex);;
|
||||
claude) clients=(claude);;
|
||||
both) clients=(claude codex);;
|
||||
*) printf 'Unknown plugin client selection: %s\n' "$client_selection" >&2; return 1;;
|
||||
esac
|
||||
|
||||
local client name claude_marketplace claude_plugin codex_marketplace codex_plugin marketplace plugin
|
||||
while IFS=$'\t' read -r name claude_marketplace claude_plugin codex_marketplace codex_plugin; do
|
||||
[ "$name" = name ] && continue
|
||||
[ -n "$name" ] || continue
|
||||
[ "$claude_marketplace" = - ] && claude_marketplace=
|
||||
[ "$codex_marketplace" = - ] && codex_marketplace=
|
||||
for client in "${clients[@]}"; do
|
||||
if [ "$client" = claude ]; then marketplace=$claude_marketplace; plugin=$claude_plugin; else marketplace=$codex_marketplace; plugin=$codex_plugin; fi
|
||||
[ -n "$plugin" ] || { printf 'Plugin selector missing for %s: %s\n' "$client" "$name" >&2; return 1; }
|
||||
if [ "$dry_run" = false ] && plugin_installed "$client" "$plugin"; then
|
||||
if [ "$update" = true ] && [ "$client" = claude ]; then
|
||||
run_plugin_command "$dry_run" claude plugin update "$plugin"
|
||||
run_plugin_command "$dry_run" claude plugin enable "$plugin"
|
||||
elif [ "$update" = true ] && [ "$client" = codex ]; then
|
||||
run_plugin_command "$dry_run" codex plugin add "$plugin"
|
||||
else
|
||||
printf 'PASS %s plugin already installed: %s\n' "$client" "$plugin"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$client" = claude ]; then
|
||||
[ -z "$marketplace" ] || run_plugin_command "$dry_run" claude plugin marketplace add "$marketplace"
|
||||
run_plugin_command "$dry_run" claude plugin install "$plugin" --scope user
|
||||
else
|
||||
if [ -n "$marketplace" ] && [ "$marketplace" != openai-curated-remote ]; then
|
||||
marketplace_name=${plugin##*@}
|
||||
ensure_codex_marketplace "$dry_run" "$marketplace" "$marketplace_name"
|
||||
fi
|
||||
run_plugin_command "$dry_run" codex plugin add "$plugin"
|
||||
fi
|
||||
printf 'PASS %s plugin ensured: %s\n' "$client" "$plugin"
|
||||
done
|
||||
done < "$manifest"
|
||||
}
|
||||
Reference in New Issue
Block a user