Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,14 @@ function Prompt-Default($question, $default) {
if ([Console]::IsInputRedirected) { return $default }
$reply = Read-Host " $question [$default]"
if ([string]::IsNullOrWhiteSpace($reply)) { return $default }
return $reply.Trim()
# Strip whitespace and surrounding matched quotes — copy-pasted secret
# references and shell-like values often include literal quote chars.
$reply = $reply.Trim()
if (($reply.StartsWith('"') -and $reply.EndsWith('"')) -or
($reply.StartsWith("'") -and $reply.EndsWith("'"))) {
$reply = $reply.Substring(1, $reply.Length - 2)
}
return $reply
}

function Read-Existing($key) {
Expand Down Expand Up @@ -119,8 +126,15 @@ function Prompt-OpField {
$fields = Get-OpFields $OpItem

if (-not $fields -or $fields.Count -eq 0) {
Write-Warn "Could not enumerate fields for $OpItem (op missing, not signed in, or item not found)"
return (Prompt-Default "1Password field name (case-sensitive)" $Default)
Write-Warn "Could not enumerate fields for ${OpItem} (op missing, not signed in, or item not found)"
while ($true) {
$reply = Prompt-Default "1Password field name (case-sensitive)" $Default
if ($reply -like 'op://*') {
Write-Warn "Field name is just the label (e.g. 'API Key'), not a full op:// path"
continue
}
return $reply
}
}

Write-Host ""
Expand All @@ -140,6 +154,10 @@ function Prompt-OpField {
Write-Warn "Number out of range — try again"
continue
}
if ($reply -like 'op://*') {
Write-Warn "Field name is just the label (e.g. 'API Key'), not a full op:// path"
continue
}
return $reply
}
}
Expand Down Expand Up @@ -180,8 +198,22 @@ function Prompt-LocalConfig {

while ($true) {
$opItem = Prompt-Default "1Password item (op://Vault/Item, no field)" $defaultItem
if ($opItem -like 'op://*') { break }
Write-Warn "Must start with op:// — try again"
if (-not ($opItem -like 'op://*')) {
Write-Warn "Must start with op:// — try again"
continue
}
$vSegs = ($opItem -replace '^op://', '').Split('/')
if ($vSegs.Count -gt 2) {
$hintField = ($vSegs | Select-Object -Skip 2) -join '/'
Write-Warn "OP_ITEM should be just op://Vault/Item — you included the field in the path."
Write-Warn " Use op://$($vSegs[0])/$($vSegs[1]) here, then '$hintField' in the next prompt."
continue
}
if ($vSegs.Count -lt 2 -or -not $vSegs[0] -or -not $vSegs[1]) {
Write-Warn "OP_ITEM needs both Vault and Item — got '$opItem'"
continue
}
break
}

$opField = Prompt-OpField $opItem $defaultField
Expand Down
44 changes: 39 additions & 5 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,13 @@ prompt_default() {
IFS= read -r reply <&3 || reply=""
exec 3<&-
fi
printf '%s\n' "${reply:-$default}"
reply="${reply:-$default}"
# Strip surrounding matched quotes — copy-pasted values from docs/secret
# managers often arrive with quote chars that break naive validation.
if [[ "$reply" == \"*\" || "$reply" == \'*\' ]]; then
reply="${reply:1:${#reply}-2}"
fi
printf '%s\n' "$reply"
}

read_existing() {
Expand Down Expand Up @@ -134,8 +140,16 @@ prompt_op_field() {
fields=$(list_op_fields "$op_item")
if [[ -z "$fields" ]]; then
warn "Could not enumerate fields for $op_item (op missing, not signed in, or item not found)"
prompt_default "1Password field name (case-sensitive)" "$default_field"
return
while :; do
local reply
reply=$(prompt_default "1Password field name (case-sensitive)" "$default_field")
if [[ "$reply" == op://* ]]; then
warn "Field name is just the label (e.g. 'API Key'), not a full op:// path"
continue
fi
printf '%s\n' "$reply"
return
done
fi

echo >&2
Expand All @@ -161,6 +175,10 @@ prompt_op_field() {
warn "Number out of range — try again"
continue
fi
if [[ "$reply" == op://* ]]; then
warn "Field name is just the label (e.g. 'API Key'), not a full op:// path"
continue
fi
printf '%s\n' "$reply"
return
done
Expand Down Expand Up @@ -200,8 +218,24 @@ prompt_local_config() {

while :; do
op_item=$(prompt_default "1Password item (op://Vault/Item, no field)" "${current_item:-op://Employee/ai.apro.is litellm}")
[[ "$op_item" == op://* ]] && break
warn "Must start with op:// — try again"
if [[ "$op_item" != op://* ]]; then
warn "Must start with op:// — try again"
continue
fi
local _validate_stripped="${op_item#op://}"
local -a _validate_segs=()
IFS='/' read -ra _validate_segs <<< "$_validate_stripped"
if (( ${#_validate_segs[@]} > 2 )); then
local _hint_field="${_validate_stripped#${_validate_segs[0]}/${_validate_segs[1]}/}"
warn "OP_ITEM should be just op://Vault/Item — you included the field in the path."
warn " Use op://${_validate_segs[0]}/${_validate_segs[1]} here, then '${_hint_field}' in the next prompt."
continue
fi
if (( ${#_validate_segs[@]} < 2 )) || [[ -z "${_validate_segs[0]}" || -z "${_validate_segs[1]}" ]]; then
warn "OP_ITEM needs both Vault and Item — got '$op_item'"
continue
fi
break
done

op_field=$(prompt_op_field "$op_item" "${current_field:-API Key}")
Expand Down