mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 10:45:22 +00:00
feat(pi-web-search): 搜索凭据迁移到 Pi auth.json
三个搜索 key 改为 auth.json 中的 tavily/exa/keenable api_key 凭据, pi-web-search/auth.ts 复刻上游 resolve-config-value 语义(!command、 $VAR 插值、credential env 优先、$$/$! 转义)并在每次调用时读取, 进程环境变量与 search.env 不再被直接读取。search_config.sh 重写为 scripts/search-auth.mjs 的薄封装:加锁原子写、权限 600、保留其他 凭据条目,支持 --migrate 从旧 search.env 一次性导入。 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent
ee1e6999d7
commit
0c9823ef5c
+68
-85
@@ -15,71 +15,54 @@ usage() {
|
||||
用法:
|
||||
./search_config.sh
|
||||
./search_config.sh --tavily KEY --exa KEY --keenable KEY
|
||||
./search_config.sh --migrate [search.env 路径]
|
||||
./search_config.sh --list
|
||||
|
||||
选项:
|
||||
--tavily KEY 配置 TAVILY_API_KEY
|
||||
--exa KEY 配置 EXA_API_KEY
|
||||
--keenable KEY 配置 KEENABLE_API_KEY
|
||||
--config FILE 覆盖配置文件路径
|
||||
--tavily KEY 配置 tavily api_key 凭据
|
||||
--exa KEY 配置 exa api_key 凭据
|
||||
--keenable KEY 配置 keenable api_key 凭据
|
||||
--migrate 从旧 search.env 导入缺失的 key
|
||||
--list 显示各 provider 配置状态(不输出 key)
|
||||
--auth FILE 覆盖 auth.json 路径
|
||||
-h, --help 显示帮助
|
||||
|
||||
不传 key 参数时进入隐藏输入的交互模式;传入任意 key 参数时不交互,
|
||||
未指定的服务保留已有值。配置文件默认是 ~/.config/my-pi/search.env。
|
||||
未指定的 provider 保留已有值。凭据写入 Pi 的 auth.json
|
||||
($PI_CODING_AGENT_DIR/auth.json 或 ~/.pi/agent/auth.json,权限 600)。
|
||||
key 支持 auth.json 原生语义:字面量、$ENV_VAR / ${ENV_VAR} 插值、
|
||||
以及 !command(执行命令取 stdout,如 "!op read op://Vault/Tavily/key")。
|
||||
EOF
|
||||
}
|
||||
|
||||
CONFIG_FILE=${MY_PI_SEARCH_CONFIG:-${XDG_CONFIG_HOME:-$HOME/.config}/my-pi/search.env}
|
||||
SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" && pwd)
|
||||
AUTH_MJS="$SCRIPT_DIR/scripts/search-auth.mjs"
|
||||
[ -f "$AUTH_MJS" ] || { warn "找不到 ${AUTH_MJS}"; exit 1; }
|
||||
|
||||
AUTH_FILE=
|
||||
mode=interactive
|
||||
tavily_set=0
|
||||
exa_set=0
|
||||
keenable_set=0
|
||||
tavily_value=
|
||||
exa_value=
|
||||
keenable_value=
|
||||
did_work=0
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case $1 in
|
||||
--tavily)
|
||||
[ "$#" -ge 2 ] || { warn "--tavily 缺少 KEY"; exit 2; }
|
||||
tavily_value=$2; tavily_set=1; mode=noninteractive; shift 2 ;;
|
||||
--exa)
|
||||
[ "$#" -ge 2 ] || { warn "--exa 缺少 KEY"; exit 2; }
|
||||
exa_value=$2; exa_set=1; mode=noninteractive; shift 2 ;;
|
||||
--keenable)
|
||||
[ "$#" -ge 2 ] || { warn "--keenable 缺少 KEY"; exit 2; }
|
||||
keenable_value=$2; keenable_set=1; mode=noninteractive; shift 2 ;;
|
||||
--config)
|
||||
[ "$#" -ge 2 ] || { warn "--config 缺少 FILE"; exit 2; }
|
||||
CONFIG_FILE=$2; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) warn "未知参数:$1"; usage >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
valid_key() {
|
||||
case $1 in
|
||||
''|*[!A-Za-z0-9._-]*) return 1 ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
run_auth() {
|
||||
if [ -n "$AUTH_FILE" ]; then
|
||||
node "$AUTH_MJS" --auth "$AUTH_FILE" "$@"
|
||||
else
|
||||
node "$AUTH_MJS" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
read_existing() {
|
||||
existing_name=$1
|
||||
[ -f "$CONFIG_FILE" ] || return 0
|
||||
awk -v name="$existing_name" '
|
||||
$1 == "export" { sub(/^export[ \t]+/, "") }
|
||||
index($0, name "=") == 1 { print substr($0, length(name) + 2); exit }
|
||||
' "$CONFIG_FILE"
|
||||
has_key() {
|
||||
run_auth --has "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
prompt_key() {
|
||||
prompt_label=$1
|
||||
prompt_current=$2
|
||||
prompt_configured=$2
|
||||
if [ ! -t 0 ]; then
|
||||
warn "交互模式需要终端;请改用 --tavily/--exa/--keenable 参数。"
|
||||
return 2
|
||||
fi
|
||||
if [ -n "$prompt_current" ]; then
|
||||
if [ "$prompt_configured" -eq 1 ]; then
|
||||
printf '%s API key(回车保留现有值): ' "$prompt_label" >&2
|
||||
else
|
||||
printf '%s API key(回车跳过): ' "$prompt_label" >&2
|
||||
@@ -92,45 +75,45 @@ prompt_key() {
|
||||
printf '%s\n' "$prompt_value"
|
||||
}
|
||||
|
||||
current_tavily=$(read_existing TAVILY_API_KEY)
|
||||
current_exa=$(read_existing EXA_API_KEY)
|
||||
current_keenable=$(read_existing KEENABLE_API_KEY)
|
||||
|
||||
if [ "$mode" = interactive ]; then
|
||||
tavily_value=$(prompt_key Tavily "$current_tavily") || exit $?
|
||||
exa_value=$(prompt_key Exa "$current_exa") || exit $?
|
||||
keenable_value=$(prompt_key Keenable "$current_keenable") || exit $?
|
||||
[ -n "$tavily_value" ] && tavily_set=1
|
||||
[ -n "$exa_value" ] && exa_set=1
|
||||
[ -n "$keenable_value" ] && keenable_set=1
|
||||
fi
|
||||
|
||||
[ "$tavily_set" -eq 1 ] || tavily_value=$current_tavily
|
||||
[ "$exa_set" -eq 1 ] || exa_value=$current_exa
|
||||
[ "$keenable_set" -eq 1 ] || keenable_value=$current_keenable
|
||||
|
||||
for key_value in "$tavily_value" "$exa_value" "$keenable_value"; do
|
||||
[ -z "$key_value" ] || valid_key "$key_value" || {
|
||||
warn "API key 只能包含字母、数字、点、下划线和连字符。"
|
||||
exit 2
|
||||
}
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case $1 in
|
||||
--tavily|--exa|--keenable)
|
||||
[ "$#" -ge 2 ] || { warn "$1 缺少 KEY"; exit 2; }
|
||||
mode=noninteractive; did_work=1
|
||||
run_auth --set "${1#--}=$2" || exit $?
|
||||
shift 2 ;;
|
||||
--migrate)
|
||||
mode=noninteractive; did_work=1
|
||||
if [ "$#" -ge 2 ] && [ "${2#--}" = "$2" ]; then
|
||||
run_auth --migrate "$2" || exit $?
|
||||
shift 2
|
||||
else
|
||||
run_auth --migrate || exit $?
|
||||
shift
|
||||
fi
|
||||
say "如确认 auth.json 可用,可手动删除旧 search.env 文件。" ;;
|
||||
--list) mode=noninteractive; did_work=1; run_auth --list || exit $?; shift ;;
|
||||
--auth)
|
||||
[ "$#" -ge 2 ] || { warn "--auth 缺少 FILE"; exit 2; }
|
||||
AUTH_FILE=$2; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) warn "未知参数:$1"; usage >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
config_dir=${CONFIG_FILE%/*}
|
||||
[ "$config_dir" = "$CONFIG_FILE" ] && config_dir=.
|
||||
umask 077
|
||||
mkdir -p "$config_dir" || exit 1
|
||||
config_tmp=$(mktemp "$config_dir/.search.env.XXXXXX") || exit 1
|
||||
trap 'rm -f "$config_tmp"' EXIT HUP INT TERM
|
||||
{
|
||||
printf '# Generated by my-pi/search_config.sh. Do not commit this file.\n'
|
||||
[ -z "$tavily_value" ] || printf 'export TAVILY_API_KEY=%s\n' "$tavily_value"
|
||||
[ -z "$exa_value" ] || printf 'export EXA_API_KEY=%s\n' "$exa_value"
|
||||
[ -z "$keenable_value" ] || printf 'export KEENABLE_API_KEY=%s\n' "$keenable_value"
|
||||
} > "$config_tmp" || exit 1
|
||||
chmod 600 "$config_tmp" || exit 1
|
||||
mv "$config_tmp" "$CONFIG_FILE" || exit 1
|
||||
trap - EXIT HUP INT TERM
|
||||
|
||||
say "搜索 API key 已安全写入 ${CONFIG_FILE}(权限 600)。"
|
||||
say "重启 Pi 后生效;显式环境变量仍优先于该文件。"
|
||||
if [ "$mode" = interactive ]; then
|
||||
for provider in tavily exa keenable; do
|
||||
case $provider in
|
||||
tavily) label=Tavily ;;
|
||||
exa) label=Exa ;;
|
||||
keenable) label=Keenable ;;
|
||||
esac
|
||||
if has_key "$provider"; then configured=1; else configured=0; fi
|
||||
value=$(prompt_key "$label" "$configured") || exit $?
|
||||
if [ -n "$value" ]; then
|
||||
run_auth --set "$provider=$value" || exit $?
|
||||
did_work=1
|
||||
fi
|
||||
done
|
||||
[ "$did_work" -eq 1 ] || say "未提供任何 key,auth.json 保持不变。"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user