feat: 组合包配置以包内 config/ 为唯一来源,并新增受保护写入人工审批

配置统一:权限策略、硬防御、Tool Search、提示词封面以及新迁入的
RTK、pi-compaction、pi-worktree、pi-hermes-memory 配置全部由扩展
直接读取已安装组合包内的 config/,pi install / pi update 即完成部署;
删除 rebuild-bundle-config.mjs 与 bundle-manifest.json,my-pi/ 只保留
日志与缓存。update.sh 成功升级后将默认 git 来源的已安装副本
reset 回发布版本。AGENTS.md 新增「组合包配置原则」。

脚本整合:search_config.sh / ssh_config.sh 合并为 configure.sh
search|ssh;install.sh / update.sh 共享函数抽到 scripts/lib.sh,远程
安装器改为先下载到临时文件再执行,下载失败可被检测。

权限:hard-guards 新增 protectedWrites,写入 Pi 凭据/设置、已安装
组合包与扩展目录、pi-ssh 凭据库时一律人工确认(跳过 AutoReview、
yolo 与 Session grant,无 UI 时拒绝);放行 ls、LSP 查询、ctx 只读、
web_search、Chrome 只读与 git 只读命令,skill_manage 改为 ask。

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
云服务部-叶林立
2026-10-20 01:05:31 -07:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent a0ee9389c7
commit 2aa1be0b54
105 changed files with 2337 additions and 1413 deletions
Executable
+145
View File
@@ -0,0 +1,145 @@
#!/bin/sh
set -u
SCRIPT_DIR=$(CDPATH=; cd "$(dirname "$0")" && pwd) || exit 1
say() {
printf '%s\n' "$*"
}
warn() {
printf '错误: %s\n' "$*" >&2
}
usage() {
cat >&2 <<'EOF'
用法:
./configure.sh search [选项] 配置搜索 API key(tavily / exa / keenable)
./configure.sh ssh [选项] 配置 SSH 主机凭据
EOF
}
search_usage() {
cat <<'EOF'
用法:
./configure.sh search
./configure.sh search --tavily KEY --exa KEY --keenable KEY
./configure.sh search --migrate [search.env 路径]
./configure.sh search --list
选项:
--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 参数时不交互,
未指定的 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
}
configure_search() {
SEARCH_AUTH_MJS="$SCRIPT_DIR/scripts/search-auth.mjs"
[ -f "$SEARCH_AUTH_MJS" ] || { warn "找不到 ${SEARCH_AUTH_MJS}"; exit 1; }
SEARCH_AUTH_FILE=
search_mode=interactive
search_did_work=0
run_search_auth() {
if [ -n "$SEARCH_AUTH_FILE" ]; then
node "$SEARCH_AUTH_MJS" --auth "$SEARCH_AUTH_FILE" "$@"
else
node "$SEARCH_AUTH_MJS" "$@"
fi
}
search_has_key() {
run_search_auth --has "$1" >/dev/null 2>&1
}
search_prompt_key() {
prompt_label=$1
prompt_configured=$2
if [ ! -t 0 ]; then
warn "交互模式需要终端;请改用 --tavily/--exa/--keenable 参数。"
return 2
fi
if [ "$prompt_configured" -eq 1 ]; then
printf '%s API key(回车保留现有值): ' "$prompt_label" >&2
else
printf '%s API key(回车跳过): ' "$prompt_label" >&2
fi
stty -echo
IFS= read -r prompt_value || prompt_status=$?
stty echo
printf '\n' >&2
[ "${prompt_status:-0}" -eq 0 ] || return "$prompt_status"
printf '%s\n' "$prompt_value"
}
while [ "$#" -gt 0 ]; do
case $1 in
--tavily|--exa|--keenable)
[ "$#" -ge 2 ] || { warn "$1 缺少 KEY"; exit 2; }
search_mode=noninteractive; search_did_work=1
run_search_auth --set "${1#--}=$2" || exit $?
shift 2 ;;
--migrate)
search_mode=noninteractive; search_did_work=1
if [ "$#" -ge 2 ] && [ "${2#--}" = "$2" ]; then
run_search_auth --migrate "$2" || exit $?
shift 2
else
run_search_auth --migrate || exit $?
shift
fi
say "如确认 auth.json 可用,可手动删除旧 search.env 文件。" ;;
--list) search_mode=noninteractive; search_did_work=1; run_search_auth --list || exit $?; shift ;;
--auth)
[ "$#" -ge 2 ] || { warn "--auth 缺少 FILE"; exit 2; }
SEARCH_AUTH_FILE=$2; shift 2 ;;
-h|--help) search_usage; exit 0 ;;
*) warn "未知参数:$1"; search_usage >&2; exit 2 ;;
esac
done
if [ "$search_mode" = interactive ]; then
for provider in tavily exa keenable; do
case $provider in
tavily) label=Tavily ;;
exa) label=Exa ;;
keenable) label=Keenable ;;
esac
if search_has_key "$provider"; then configured=1; else configured=0; fi
value=$(search_prompt_key "$label" "$configured") || exit $?
if [ -n "$value" ]; then
run_search_auth --set "$provider=$value" || exit $?
search_did_work=1
fi
done
[ "$search_did_work" -eq 1 ] || say "未提供任何 key,auth.json 保持不变。"
fi
}
case ${1:-} in
search)
shift
configure_search "$@"
;;
ssh)
shift
exec node "$SCRIPT_DIR/pi-ssh/scripts/ssh-config-launcher.mjs" "$@"
;;
*)
usage
exit 2
;;
esac