Files
my-pi/install.sh
T
云服务部-叶林立andDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 2aa1be0b54 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>
2026-10-20 01:05:31 -07:00

411 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
set -u
SCRIPT_DIR=$(CDPATH=; cd "$(dirname "$0")" && pwd) || exit 1
MY_PI_LIB=$SCRIPT_DIR/scripts/lib.sh
if [ ! -f "$MY_PI_LIB" ]; then
printf '错误: 找不到共享辅助脚本:%s\n' "$MY_PI_LIB" >&2
exit 1
fi
# shellcheck source=scripts/lib.sh
. "$MY_PI_LIB"
PACKAGE_SOURCE=${PI_PACKAGE_SOURCE:-git:git@bitbucket.org:siakitem/my-pi.git}
POWERLEVEL10K_PRESET_FILE=$SCRIPT_DIR/config/p10k.zsh
CODEGRAPH_INSTALL_URL=https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh
KITTY_INSTALL_URL=https://sw.kovidgoyal.net/kitty/installer.sh
OH_MY_ZSH_INSTALL_URL=https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
POWERLEVEL10K_REPOSITORY=https://github.com/romkatv/powerlevel10k.git
ZSH_AUTOSUGGESTIONS_REPOSITORY=https://github.com/zsh-users/zsh-autosuggestions.git
ZSH_SYNTAX_HIGHLIGHTING_REPOSITORY=https://github.com/zsh-users/zsh-syntax-highlighting.git
install_failed=0
ask_yes_no() {
prompt=$1
printf '%s [y/N] ' "$prompt"
if ! IFS= read -r answer; then
answer=n
fi
case $answer in
y|Y|yes|YES|Yes) return 0 ;;
*) return 1 ;;
esac
}
ask_to_install() {
ask_yes_no "未检测到 $1。是否现在安装?"
}
ask_to_enable() {
ask_yes_no "尚未启用 $1。是否现在安装所需文件并启用?"
}
require_brew() {
if command -v brew >/dev/null 2>&1; then
return 0
fi
warn "自动安装该依赖需要 Homebrew,但当前 PATH 中没有 brew。"
warn "请先安装 Homebrew(https://brew.sh/),然后重新运行本脚本。"
return 1
}
record_install_failure() {
warn "$1 安装失败;其余依赖仍会继续检查。"
install_failed=1
}
java_major_version() {
java_version=$(java -version 2>&1 | awk -F '"' 'NR == 1 { print $2 }')
case $java_version in
1.*) java_version=${java_version#1.} ;;
esac
java_major=${java_version%%.*}
case $java_major in
''|*[!0-9]*) return 1 ;;
*) printf '%s\n' "$java_major" ;;
esac
}
install_codegraph() {
if ! command -v curl >/dev/null 2>&1; then
warn "安装 CodeGraph 需要 curl。"
return 1
fi
codegraph_installer=$(mktemp "${TMPDIR:-/tmp}/my-pi-codegraph.XXXXXX") || return 1
if ! curl -fsSL "$CODEGRAPH_INSTALL_URL" -o "$codegraph_installer" || \
! sh "$codegraph_installer"; then
rm -f "$codegraph_installer"
return 1
fi
rm -f "$codegraph_installer"
# 官方安装器默认使用 ~/.local/bin;让本次检查立即看到新命令。
if [ -d "$HOME/.local/bin" ]; then
PATH="$HOME/.local/bin:$PATH"
export PATH
fi
command -v codegraph >/dev/null 2>&1
}
install_kotlin_lsp() {
require_brew || return 1
HOMEBREW_NO_ASK=1 brew install JetBrains/utils/kotlin-lsp || return 1
command -v kotlin-lsp >/dev/null 2>&1
}
install_java_21() {
require_brew || return 1
HOMEBREW_NO_ASK=1 brew install openjdk@21 || return 1
java_home=$(brew --prefix openjdk@21 2>/dev/null) || return 1
PATH="$java_home/bin:$PATH"
export PATH
installed_java_major=$(java_major_version || true)
if [ -z "$installed_java_major" ] || [ "$installed_java_major" -lt 21 ]; then
return 1
fi
say "已在本次安装流程中使用 $java_home/bin。若新终端仍找不到 Java,请把该目录加入 PATH。"
}
install_jdtls() {
require_brew || return 1
HOMEBREW_NO_ASK=1 brew install jdtls || return 1
command -v jdtls >/dev/null 2>&1
}
install_kitty() {
case $(uname -s) in
Darwin)
require_brew || return 1
HOMEBREW_NO_ASK=1 brew install --cask kitty || return 1
;;
*)
if ! command -v curl >/dev/null 2>&1; then
warn "安装 Kitty 需要 curl。"
return 1
fi
installer_file=$(mktemp "${TMPDIR:-/tmp}/my-pi-kitty.XXXXXX") || return 1
if ! curl -fsSL "$KITTY_INSTALL_URL" -o "$installer_file" || \
! sh "$installer_file"; then
rm -f "$installer_file"
return 1
fi
rm -f "$installer_file"
;;
esac
detect_kitty
}
kitty_solarized_dark_configured() {
resolve_kitty_config_dir
[ -f "$kitty_config_dir/current-theme.conf" ] || return 1
[ -f "$kitty_config_dir/kitty.conf" ] || return 1
grep -F "Solarized Dark" "$kitty_config_dir/current-theme.conf" >/dev/null 2>&1 || return 1
grep -E '^[[:space:]]*include[[:space:]]+([^#[:space:]]*/)?current-theme\.conf([[:space:]]*(#.*)?)?$' \
"$kitty_config_dir/kitty.conf" >/dev/null 2>&1
}
configure_kitty_solarized_dark() {
"$kitty_bin" +kitten themes --reload-in=none "Solarized Dark"
}
oh_my_zsh_installed() {
resolve_zsh_paths
[ -f "$oh_my_zsh_dir/oh-my-zsh.sh" ]
}
install_oh_my_zsh() {
if ! command -v curl >/dev/null 2>&1; then
warn "安装 Oh My Zsh 需要 curl。"
return 1
fi
if ! command -v git >/dev/null 2>&1; then
warn "安装 Oh My Zsh 需要 git。"
return 1
fi
resolve_zsh_paths
oh_my_zsh_installer=$(curl -fsSL "$OH_MY_ZSH_INSTALL_URL") || return 1
RUNZSH=no CHSH=no KEEP_ZSHRC=yes ZSH="$oh_my_zsh_dir" \
sh -c "$oh_my_zsh_installer" "" --unattended --keep-zshrc || return 1
oh_my_zsh_installed
}
prepare_zshrc() {
resolve_zsh_paths
zshrc_dir=${zshrc_file%/*}
[ "$zshrc_dir" = "$zshrc_file" ] && zshrc_dir=.
mkdir -p "$zshrc_dir" || return 1
[ -f "$zshrc_file" ] || : > "$zshrc_file" || return 1
}
zshrc_has_oh_my_zsh_loader() {
[ -f "$zshrc_file" ] || return 1
grep -E '^[[:space:]]*(source|\.)[[:space:]].*oh-my-zsh\.sh' "$zshrc_file" >/dev/null 2>&1
}
ensure_oh_my_zsh_loader() {
prepare_zshrc || return 1
if zshrc_has_oh_my_zsh_loader; then
return 0
fi
replace_zsh_managed_block "oh-my-zsh-loader" "append" "" \
"export ZSH=\"$oh_my_zsh_dir\"" \
"source \"\$ZSH/oh-my-zsh.sh\""
}
set_powerlevel10k_theme() {
prepare_zshrc || return 1
replace_zsh_managed_block "powerlevel10k-theme" "before" \
"$MY_PI_ZSH_LOADER_ANCHOR" \
'ZSH_THEME="powerlevel10k/powerlevel10k"'
}
p10k_config_sourced() {
my_pi_managed_block_exists "$zshrc_file" "powerlevel10k-config"
}
ensure_p10k_config_source() {
prepare_zshrc || return 1
replace_zsh_managed_block "powerlevel10k-config" "append" "" \
"[[ ! -f \"$p10k_managed_config_file\" ]] || source \"$p10k_managed_config_file\""
}
configure_powerlevel10k() {
resolve_zsh_paths
powerlevel10k_dir=$zsh_custom_dir/themes/powerlevel10k
if [ ! -f "$powerlevel10k_dir/powerlevel10k.zsh-theme" ]; then
if ! command -v git >/dev/null 2>&1; then
warn "安装 Powerlevel10k 需要 git。"
return 1
fi
mkdir -p "$zsh_custom_dir/themes" || return 1
git clone --depth=1 "$POWERLEVEL10K_REPOSITORY" "$powerlevel10k_dir" || return 1
fi
my_pi_sync_managed_file "$POWERLEVEL10K_PRESET_FILE" "$p10k_managed_config_file" || return 1
set_powerlevel10k_theme || return 1
ensure_oh_my_zsh_loader || return 1
ensure_p10k_config_source || return 1
say "已应用仓库内置的 Powerlevel10k Rainbow/ASCII 受管配置。"
}
zsh_plugin_enabled() {
plugin=$1
[ -f "$zshrc_file" ] || return 1
awk -v target="$plugin" '
{
line = $0
sub(/[[:space:]]*#.*/, "", line)
if (line ~ /^[[:space:]]*plugins[[:space:]]*[+]?=/) active = 1
if (active) {
gsub(/[()=+]/, " ", line)
count = split(line, fields, /[[:space:]]+/)
for (i = 1; i <= count; i++) if (fields[i] == target) found = 1
if ($0 ~ /\)/) active = 0
}
}
END { exit(found ? 0 : 1) }
' "$zshrc_file"
}
add_zsh_plugin() {
plugin=$1
prepare_zshrc || return 1
ensure_oh_my_zsh_loader || return 1
replace_zsh_managed_block "zsh-plugin-$plugin" "before" \
"$MY_PI_ZSH_LOADER_ANCHOR" \
"plugins+=($plugin)"
}
install_and_enable_zsh_plugin() {
plugin=$1
repository=${2:-}
if [ -n "$repository" ] && [ ! -d "$zsh_custom_dir/plugins/$plugin" ]; then
if ! command -v git >/dev/null 2>&1; then
warn "安装 $plugin 需要 git。"
return 1
fi
mkdir -p "$zsh_custom_dir/plugins" || return 1
git clone --depth=1 "$repository" "$zsh_custom_dir/plugins/$plugin" || return 1
fi
add_zsh_plugin "$plugin"
}
check_zsh_plugin() {
plugin=$1
description=$2
repository=${3:-}
if zsh_plugin_enabled "$plugin"; then
say "✓ $plugin 已启用"
elif ask_to_enable "$description"; then
install_and_enable_zsh_plugin "$plugin" "$repository" || record_install_failure "$plugin"
else
say "- 已跳过 ${plugin}。"
fi
}
if ! command -v pi >/dev/null 2>&1; then
warn "找不到 pi,无法安装组合包。请先安装 Pi 并确保 pi 位于 PATH。"
exit 127
fi
say "正在安装 Pi 组合包:$PACKAGE_SOURCE"
if ! pi install "$PACKAGE_SOURCE"; then
warn "pi install 执行失败,已停止后续依赖检查。"
exit 1
fi
say "Pi 组合包安装完成。"
say "开始检查终端环境和机器级 CLI 依赖。"
kitty_ok=0
if detect_kitty; then
kitty_ok=1
say "✓ Kitty 已安装(${kitty_bin})"
elif ask_to_install "Kitty 终端"; then
if install_kitty; then
kitty_ok=1
say "✓ Kitty 安装完成"
else
record_install_failure "Kitty"
fi
else
say "- 已跳过 Kitty;不会检查 Solarized Dark 主题。"
fi
if [ "$kitty_ok" -eq 1 ]; then
if kitty_solarized_dark_configured; then
say "✓ Kitty 已配置 Solarized Dark"
elif ask_yes_no "Kitty 尚未配置 Solarized Dark。是否现在配置?"; then
configure_kitty_solarized_dark || record_install_failure "Kitty Solarized Dark 主题"
else
say "- 已跳过 Kitty Solarized Dark 主题。"
fi
fi
oh_my_zsh_ok=0
if oh_my_zsh_installed; then
oh_my_zsh_ok=1
say "✓ Oh My Zsh 已安装(${oh_my_zsh_dir})"
elif ask_to_install "Oh My Zsh"; then
if install_oh_my_zsh; then
oh_my_zsh_ok=1
say "✓ Oh My Zsh 安装完成"
else
record_install_failure "Oh My Zsh"
fi
else
say "- 已跳过 Oh My Zsh;不会检查 Powerlevel10k 和 Zsh 插件。"
fi
if [ "$oh_my_zsh_ok" -eq 1 ]; then
resolve_zsh_paths
powerlevel10k_dir=$zsh_custom_dir/themes/powerlevel10k
if [ -f "$powerlevel10k_dir/powerlevel10k.zsh-theme" ] && \
my_pi_managed_block_exists "$zshrc_file" "powerlevel10k-theme" && \
[ -f "$p10k_managed_config_file" ] && \
cmp -s "$POWERLEVEL10K_PRESET_FILE" "$p10k_managed_config_file" && p10k_config_sourced; then
say "✓ Powerlevel10k 已安装并配置"
elif ask_yes_no "Powerlevel10k 尚未完整配置。是否安装并应用仓库内置的 Rainbow/ASCII 配置?"; then
configure_powerlevel10k || record_install_failure "Powerlevel10k"
else
say "- 已跳过 Powerlevel10k。"
fi
check_zsh_plugin "git" "git 插件" ""
check_zsh_plugin "zsh-autosuggestions" "zsh-autosuggestions(命令自动建议)" \
"$ZSH_AUTOSUGGESTIONS_REPOSITORY"
check_zsh_plugin "zsh-syntax-highlighting" "zsh-syntax-highlighting(语法高亮)" \
"$ZSH_SYNTAX_HIGHLIGHTING_REPOSITORY"
fi
if command -v codegraph >/dev/null 2>&1; then
say "✓ codegraph 已安装"
elif ask_to_install "CodeGraph(提供 codegraph_explore)"; then
install_codegraph || record_install_failure "CodeGraph"
else
say "- 已跳过 CodeGraph;codegraph_explore 将不可用。"
fi
if command -v kotlin-lsp >/dev/null 2>&1; then
say "✓ kotlin-lsp 已安装"
elif ask_to_install "kotlin-lsp(Kotlin LSP)"; then
install_kotlin_lsp || record_install_failure "kotlin-lsp"
else
say "- 已跳过 kotlin-lsp;Kotlin LSP 功能将不可用。"
fi
java_ok=0
if command -v java >/dev/null 2>&1; then
java_major=$(java_major_version || true)
if [ -n "$java_major" ] && [ "$java_major" -ge 21 ]; then
java_ok=1
say "✓ Java $java_major 已安装(满足 Java 21+)"
fi
fi
if [ "$java_ok" -eq 0 ]; then
if ask_to_install "Java 21+(JDT LS 运行时)"; then
if install_java_21; then
java_ok=1
else
record_install_failure "Java 21+"
fi
else
say "- 已跳过 Java 21+;JDT LS 可能无法启动。"
fi
fi
if command -v jdtls >/dev/null 2>&1; then
say "✓ jdtls 已安装"
elif ask_to_install "jdtls(Java LSP)"; then
install_jdtls || record_install_failure "jdtls"
else
say "- 已跳过 jdtls;Java LSP 功能将不可用。"
fi
say "终端环境与依赖检查完成。RTK 命令改写默认关闭,因此未检查可选的 rtk CLI。"
exit "$install_failed"