deploy_kiro_cli.sh 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. #!/usr/bin/env bash
  2. # =============================================================================
  3. # FranklinWH Toolbox - Kiro CLI 配置部署脚本
  4. # =============================================================================
  5. # 作用:把版本库内的 kiro-cli 配置(agents / steering / skills)部署到
  6. # 后端运行用户的 HOME 下的 .kiro 目录,供后端以 ACP 子进程方式拉起的 kiro-cli
  7. # 加载使用。
  8. #
  9. # 注意:settings/ 由 kiro-cli 自动生成,不纳入部署(不覆盖目标机自身的设置)。
  10. #
  11. # 配置来源(版本库内,权威配置):
  12. # deploy/kiro/
  13. # ├── agents/ Agent 定义(*.json)
  14. # ├── steering/ Steering 规则(*.md)
  15. # └── skills/ 仅自定义 skill(skills.sh 生态的通过命令安装,不入此目录)
  16. #
  17. # 部署目标(与 install_kiro_cli.sh 的 KIRO_HOME 保持一致):
  18. # ${KIRO_HOME}/.kiro 默认 /var/www/fwh-toolbox/server/.kiro
  19. # ├── agents/ ← 拷贝同步
  20. # ├── steering/ ← 拷贝同步
  21. # ├── settings/ ← 不部署(kiro-cli 自动生成)
  22. # └── skills/
  23. # ├── <skills.sh 生态> ← 用 `npx skills add` 命令安装(可控、可更新)
  24. # └── <自定义 skill> ← 直接拷贝(skills.sh 上不存在,无法命令安装)
  25. #
  26. # Skill 分类原则:
  27. # * 来自 skills.sh 生态的 skill → 命令安装。清单外置在 ${KIRO_CONFIG_SRC}/skills-sh.list
  28. # (每行 "<owner>/<repo>@<skill>",# 注释/空行忽略;文件内含安装命令备注)。
  29. # 新增/调整只需改该清单文件,无需改本脚本。
  30. # * 非 skills.sh 的自定义 skill → 直接拷贝。**不再硬编码清单**,脚本扫描
  31. # ${KIRO_CONFIG_SRC}/skills/ 下实际存在的目录逐个拷贝。新增只需把目录放进去。
  32. #
  33. # 前置条件:
  34. # * 目标用户存在,且 kiro-cli 已安装(见 install_kiro_cli.sh)。
  35. # * 命令安装 skill 需要 Node.js / npx(skills CLI 通过 npx 调用)。
  36. # * rsync(可选,用于同步;缺失时回退到 cp)。
  37. #
  38. # 环境变量:
  39. # KIRO_RUN_AS_USER 部署并运行 kiro-cli 的系统用户 (默认当前用户)
  40. # KIRO_HOME 目标用户 HOME(决定 .kiro 位置) (默认 /var/www/fwh-toolbox/server)
  41. # KIRO_CONFIG_SRC 配置来源目录 (默认 <repo>/deploy/kiro)
  42. # KIRO_SKILLS_LIST skills.sh 安装清单文件 (默认 ${KIRO_CONFIG_SRC}/skills-sh.list)
  43. # KIRO_AGENT skills CLI 的目标 agent (默认 kiro-cli)
  44. # KIRO_SKILLS_INSTALL 是否命令安装 skills.sh skill (y/n) (默认 y)
  45. # KIRO_WIKI_DOCS 产品文档知识库目录(替换 __WIKI_DOCS__) (默认当前 Wiki docs 路径)
  46. # KIRO_OFFICECLI_BIN officecli 可执行文件(替换 __OFFICECLI_BIN__)(默认当前 officecli 路径)
  47. # KIRO_DRY_RUN 只打印将执行的动作,不落盘 (y/n) (默认 n)
  48. # =============================================================================
  49. set -euo pipefail
  50. # ---------------------------------------------------------------------------
  51. # 路径与默认值
  52. # ---------------------------------------------------------------------------
  53. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  54. DEPLOY_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" # deploy/
  55. PROJECT_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)" # 仓库根目录
  56. KIRO_RUN_AS_USER="${KIRO_RUN_AS_USER:-$(id -un)}"
  57. KIRO_HOME="${KIRO_HOME:-/var/www/fwh-toolbox/server}"
  58. KIRO_CONFIG_SRC="${KIRO_CONFIG_SRC:-${DEPLOY_DIR}/kiro}"
  59. KIRO_AGENT="${KIRO_AGENT:-kiro-cli}"
  60. KIRO_SKILLS_INSTALL="${KIRO_SKILLS_INSTALL:-y}"
  61. KIRO_DRY_RUN="${KIRO_DRY_RUN:-n}"
  62. # agent.json 占位符替换值(部署时可覆盖;默认取当前环境路径)
  63. # __KIRO_HOME__ → ${KIRO_HOME}(fwh-pyrun、templates 的父路径)
  64. # __WIKI_DOCS__ → ${KIRO_WIKI_DOCS}(产品文档知识库目录)
  65. # __OFFICECLI_BIN__ → ${KIRO_OFFICECLI_BIN}(officecli 可执行文件)
  66. KIRO_WIKI_DOCS="${KIRO_WIKI_DOCS:-/home/jerry-remote/web/FranklinWH-Wiki/client/docs}"
  67. KIRO_OFFICECLI_BIN="${KIRO_OFFICECLI_BIN:-/home/jerry-remote/.local/bin/officecli}"
  68. KIRO_DEST="${KIRO_HOME%/}/.kiro"
  69. # ---------------------------------------------------------------------------
  70. # Skill 来源
  71. # ---------------------------------------------------------------------------
  72. # skills.sh 生态:从清单文件读取(每行 "<owner>/<repo>@<skill>",# 注释/空行忽略)
  73. KIRO_SKILLS_LIST="${KIRO_SKILLS_LIST:-${KIRO_CONFIG_SRC}/skills-sh.list}"
  74. # 自定义 skill:不再硬编码,直接扫描 ${KIRO_CONFIG_SRC}/skills/ 下实际存在的目录进行拷贝
  75. # 拷贝同步的配置子目录(settings 由 kiro-cli 自动生成,不部署)
  76. CONFIG_SUBDIRS=(agents steering)
  77. # ---------------------------------------------------------------------------
  78. # 日志
  79. # ---------------------------------------------------------------------------
  80. c_reset='\033[0m'; c_blue='\033[1;34m'; c_green='\033[1;32m'; c_yellow='\033[1;33m'; c_red='\033[1;31m'
  81. info() { echo -e "${c_blue}[INFO]${c_reset} $*"; }
  82. ok() { echo -e "${c_green}[ OK ]${c_reset} $*"; }
  83. warn() { echo -e "${c_yellow}[WARN]${c_reset} $*"; }
  84. err() { echo -e "${c_red}[FAIL]${c_reset} $*" >&2; }
  85. has() { command -v "$1" >/dev/null 2>&1; }
  86. is_dry() { [[ "${KIRO_DRY_RUN}" == "y" ]]; }
  87. # 解析目标用户 HOME(仅用于日志/一致性校验;实际目标由 KIRO_HOME 决定)
  88. resolve_user_home() {
  89. getent passwd "${KIRO_RUN_AS_USER}" | cut -d: -f6
  90. }
  91. # 以目标用户身份执行命令(设置 HOME=KIRO_HOME,保证 .kiro 落到目标位置)
  92. # dry-run 时只打印。
  93. as_target_user() {
  94. local cmd="$*"
  95. if is_dry; then
  96. echo -e "${c_yellow}[DRY]${c_reset} (as ${KIRO_RUN_AS_USER}, HOME=${KIRO_HOME}) ${cmd}"
  97. return 0
  98. fi
  99. if [[ "${KIRO_RUN_AS_USER}" == "$(id -un)" ]]; then
  100. HOME="${KIRO_HOME}" bash -lc "${cmd}"
  101. else
  102. sudo -u "${KIRO_RUN_AS_USER}" -H env HOME="${KIRO_HOME}" bash -lc "${cmd}"
  103. fi
  104. }
  105. # 以目标用户身份 / 或 sudo 执行文件系统操作(run_fs "<shell>")
  106. run_fs() {
  107. local cmd="$*"
  108. if is_dry; then
  109. echo -e "${c_yellow}[DRY]${c_reset} ${cmd}"
  110. return 0
  111. fi
  112. bash -lc "${cmd}"
  113. }
  114. # 检查并(尽力)安装沙箱运行 Python 所需依赖:bwrap、python3。
  115. # 自动安装需要 sudo 与受支持的包管理器(apt/dnf/yum/zypper/pacman)。
  116. # 失败时给出告警但不中断部署(与 npx 缺失的处理一致)。
  117. ensure_sandbox_deps() {
  118. local missing=()
  119. has bwrap || missing+=("bwrap")
  120. has python3 || missing+=("python3")
  121. if [[ ${#missing[@]} -eq 0 ]]; then
  122. ok "沙箱依赖已就绪:bwrap、python3"
  123. return 0
  124. fi
  125. warn "缺少沙箱依赖:${missing[*]},尝试自动安装 ..."
  126. # 将逻辑包名映射到各发行版的实际包名
  127. local pkgs_apt=() pkgs_rpm=() pkgs_pacman=()
  128. local m
  129. for m in "${missing[@]}"; do
  130. case "$m" in
  131. bwrap) pkgs_apt+=("bubblewrap"); pkgs_rpm+=("bubblewrap"); pkgs_pacman+=("bubblewrap") ;;
  132. python3) pkgs_apt+=("python3"); pkgs_rpm+=("python3"); pkgs_pacman+=("python") ;;
  133. esac
  134. done
  135. if is_dry; then
  136. echo -e "${c_yellow}[DRY]${c_reset} 将安装:${missing[*]}"
  137. return 0
  138. fi
  139. if has apt-get; then
  140. run_fs "sudo apt-get update -y && sudo apt-get install -y ${pkgs_apt[*]}" || true
  141. elif has dnf; then
  142. run_fs "sudo dnf install -y ${pkgs_rpm[*]}" || true
  143. elif has yum; then
  144. run_fs "sudo yum install -y ${pkgs_rpm[*]}" || true
  145. elif has zypper; then
  146. run_fs "sudo zypper install -y ${pkgs_rpm[*]}" || true
  147. elif has pacman; then
  148. run_fs "sudo pacman -Sy --noconfirm ${pkgs_pacman[*]}" || true
  149. else
  150. err "未识别的包管理器,无法自动安装:${missing[*]}"
  151. err "请手动安装后重试(bwrap 对应包名通常为 bubblewrap)。"
  152. return 1
  153. fi
  154. # 复检
  155. local still=()
  156. has bwrap || still+=("bwrap")
  157. has python3 || still+=("python3")
  158. if [[ ${#still[@]} -gt 0 ]]; then
  159. err "以下依赖安装后仍不可用:${still[*]}。Python3 沙箱能力将不可用。"
  160. return 1
  161. fi
  162. ok "沙箱依赖安装完成:${missing[*]}"
  163. }
  164. # ---------------------------------------------------------------------------
  165. # 0. 前置检查
  166. # ---------------------------------------------------------------------------
  167. ensure_prereqs() {
  168. if [[ ! -d "${KIRO_CONFIG_SRC}" ]]; then
  169. err "配置来源不存在:${KIRO_CONFIG_SRC}"
  170. return 1
  171. fi
  172. if ! getent passwd "${KIRO_RUN_AS_USER}" >/dev/null; then
  173. err "系统用户不存在:${KIRO_RUN_AS_USER}"
  174. return 1
  175. fi
  176. if [[ "${KIRO_SKILLS_INSTALL}" == "y" ]] && ! has npx; then
  177. warn "未找到 npx(Node.js)。skills.sh skill 的命令安装将被跳过。"
  178. warn "如需命令安装,请先安装 Node.js(提供 npx),或设 KIRO_SKILLS_INSTALL=n。"
  179. fi
  180. # Python3 沙箱依赖(bwrap + python3),供 fwh-toolbox-agent 的 fwh-pyrun 使用
  181. ensure_sandbox_deps || warn "沙箱依赖未完全就绪,fwh-pyrun 可能无法运行。"
  182. }
  183. # ---------------------------------------------------------------------------
  184. # 1. 准备目标目录(属主为目标用户)
  185. # ---------------------------------------------------------------------------
  186. prepare_dest() {
  187. info "准备目标目录:${KIRO_DEST}(属主 ${KIRO_RUN_AS_USER})"
  188. run_fs "sudo mkdir -p '${KIRO_DEST}/skills'"
  189. run_fs "sudo chown -R '${KIRO_RUN_AS_USER}' '${KIRO_HOME%/}/.kiro'"
  190. }
  191. # ---------------------------------------------------------------------------
  192. # 2. 同步配置子目录(agents / settings / steering)
  193. # ---------------------------------------------------------------------------
  194. sync_config_dirs() {
  195. local sub src
  196. for sub in "${CONFIG_SUBDIRS[@]}"; do
  197. src="${KIRO_CONFIG_SRC}/${sub}"
  198. if [[ ! -d "${src}" ]]; then
  199. warn "跳过(源不存在):${src}"
  200. continue
  201. fi
  202. info "同步配置:${sub}/ → ${KIRO_DEST}/${sub}/"
  203. run_fs "sudo mkdir -p '${KIRO_DEST}/${sub}'"
  204. if has rsync; then
  205. run_fs "sudo rsync -a --delete '${src}/' '${KIRO_DEST}/${sub}/'"
  206. else
  207. run_fs "sudo rm -rf '${KIRO_DEST}/${sub}'/* && sudo cp -rf '${src}/.' '${KIRO_DEST}/${sub}/'"
  208. fi
  209. done
  210. run_fs "sudo chown -R '${KIRO_RUN_AS_USER}' '${KIRO_HOME%/}/.kiro'"
  211. ok "配置目录同步完成"
  212. }
  213. # ---------------------------------------------------------------------------
  214. # 3. 命令安装 skills.sh 生态的 skill(从清单文件读取)
  215. # ---------------------------------------------------------------------------
  216. # 解析清单文件:去掉 # 注释与空行,输出每行 "<owner>/<repo>@<skill>"
  217. read_skills_list() {
  218. [[ -f "${KIRO_SKILLS_LIST}" ]] || return 0
  219. sed -E 's/#.*$//; s/[[:space:]]+$//; s/^[[:space:]]+//' "${KIRO_SKILLS_LIST}" \
  220. | grep -vE '^[[:space:]]*$'
  221. }
  222. install_skills_sh() {
  223. if [[ "${KIRO_SKILLS_INSTALL}" != "y" ]]; then
  224. info "跳过 skills.sh skill 命令安装(KIRO_SKILLS_INSTALL=${KIRO_SKILLS_INSTALL})"
  225. return 0
  226. fi
  227. if [[ ! -f "${KIRO_SKILLS_LIST}" ]]; then
  228. warn "未找到 skills.sh 清单文件:${KIRO_SKILLS_LIST},跳过命令安装"
  229. return 0
  230. fi
  231. if ! has npx; then
  232. warn "无 npx,跳过 skills.sh skill 命令安装"
  233. return 0
  234. fi
  235. info "从清单安装 skills.sh skill:${KIRO_SKILLS_LIST}"
  236. local entry repo skill count=0
  237. while IFS= read -r entry; do
  238. [[ -z "${entry}" ]] && continue
  239. if [[ "${entry}" != *"@"* ]]; then
  240. warn "清单行格式不符(应为 owner/repo@skill),跳过:${entry}"
  241. continue
  242. fi
  243. repo="${entry%@*}"
  244. skill="${entry##*@}"
  245. info "命令安装 skill:${repo} --skill ${skill} → agent ${KIRO_AGENT}"
  246. # 以目标用户身份、HOME=KIRO_HOME 运行,skill 落到 ${KIRO_DEST}/skills
  247. as_target_user "cd '${KIRO_HOME}' && npx --yes skills add '${repo}' --skill '${skill}' --agent '${KIRO_AGENT}' --yes" \
  248. || warn "skill 安装失败(可稍后重试):${entry}"
  249. count=$((count+1))
  250. done < <(read_skills_list)
  251. ok "skills.sh skill 命令安装完成(共 ${count} 个)"
  252. }
  253. # ---------------------------------------------------------------------------
  254. # 4. 拷贝自定义 skill(扫描 ${KIRO_CONFIG_SRC}/skills/ 实际存在的目录)
  255. # ---------------------------------------------------------------------------
  256. copy_custom_skills() {
  257. local skills_src="${KIRO_CONFIG_SRC}/skills"
  258. if [[ ! -d "${skills_src}" ]]; then
  259. info "无自定义 skill 目录(${skills_src}),跳过拷贝"
  260. return 0
  261. fi
  262. local dir name dest count=0
  263. for dir in "${skills_src}"/*/; do
  264. [[ -d "${dir}" ]] || continue
  265. name="$(basename "${dir}")"
  266. dest="${KIRO_DEST}/skills/${name}"
  267. info "拷贝自定义 skill:${name} → ${dest}"
  268. if has rsync; then
  269. run_fs "sudo rsync -a --delete '${skills_src}/${name}/' '${dest}/'"
  270. else
  271. run_fs "sudo rm -rf '${dest}' && sudo mkdir -p '${dest}' && sudo cp -rf '${skills_src}/${name}/.' '${dest}/'"
  272. fi
  273. count=$((count+1))
  274. done
  275. run_fs "sudo chown -R '${KIRO_RUN_AS_USER}' '${KIRO_DEST}/skills'"
  276. ok "自定义 skill 拷贝完成(共 ${count} 个)"
  277. }
  278. # ---------------------------------------------------------------------------
  279. # 渲染占位符(部署后按目标环境替换 agents 与 skills 中的路径)
  280. # ---------------------------------------------------------------------------
  281. # 版本库中的 agents/*.json 与 skills/**/*.md 用占位符表示环境相关路径,
  282. # 部署到目标后在此统一替换为实值:
  283. # __KIRO_HOME__ → ${KIRO_HOME} (fwh-pyrun、templates 的父路径)
  284. # __WIKI_DOCS__ → ${KIRO_WIKI_DOCS} (产品文档知识库目录)
  285. # __OFFICECLI_BIN__ → ${KIRO_OFFICECLI_BIN}(officecli 可执行文件)
  286. # 注意:需在 agents / skills 全部拷贝到目标之后调用。
  287. render_placeholders() {
  288. info "替换占位符:__KIRO_HOME__=${KIRO_HOME%/} __WIKI_DOCS__=${KIRO_WIKI_DOCS} __OFFICECLI_BIN__=${KIRO_OFFICECLI_BIN}"
  289. # 用 # 作为 sed 分隔符(路径含 /);当前替换值不含 #
  290. local sed_expr="s#__KIRO_HOME__#${KIRO_HOME%/}#g; s#__WIKI_DOCS__#${KIRO_WIKI_DOCS%/}#g; s#__OFFICECLI_BIN__#${KIRO_OFFICECLI_BIN}#g"
  291. # agents/*.json
  292. if [[ -d "${KIRO_DEST}/agents" ]]; then
  293. run_fs "sudo find '${KIRO_DEST}/agents' -maxdepth 1 -type f -name '*.json' -exec sed -i -E '${sed_expr}' {} +"
  294. fi
  295. # skills/**/*.md
  296. if [[ -d "${KIRO_DEST}/skills" ]]; then
  297. run_fs "sudo find '${KIRO_DEST}/skills' -type f -name '*.md' -exec sed -i -E '${sed_expr}' {} +"
  298. fi
  299. run_fs "sudo chown -R '${KIRO_RUN_AS_USER}' '${KIRO_HOME%/}/.kiro'"
  300. ok "占位符替换完成(agents + skills)"
  301. }
  302. # ---------------------------------------------------------------------------
  303. # 4.2 部署 PPT 公司模板(随部署路径走)
  304. # ---------------------------------------------------------------------------
  305. # 源:${KIRO_CONFIG_SRC}/templates/*
  306. # 目标:${KIRO_HOME}/templates/(agent.json 中以 __KIRO_HOME__/templates 引用)
  307. install_templates() {
  308. local src="${KIRO_CONFIG_SRC}/templates"
  309. local dest="${KIRO_HOME%/}/templates"
  310. if [[ ! -d "${src}" ]]; then
  311. info "无模板目录(${src}),跳过模板部署"
  312. return 0
  313. fi
  314. info "部署模板:${src}/ → ${dest}/"
  315. run_fs "sudo mkdir -p '${dest}'"
  316. if has rsync; then
  317. run_fs "sudo rsync -a '${src}/' '${dest}/'"
  318. else
  319. run_fs "sudo cp -rf '${src}/.' '${dest}/'"
  320. fi
  321. run_fs "sudo chown -R '${KIRO_RUN_AS_USER}' '${dest}'"
  322. ok "模板部署完成:${dest}"
  323. }
  324. # ---------------------------------------------------------------------------
  325. # 4.5 部署 fwh-pyrun 包装脚本(Python3 沙箱执行器,供 fwh-toolbox-agent 调用)
  326. # ---------------------------------------------------------------------------
  327. # 源:${KIRO_CONFIG_SRC}/bin/fwh-pyrun
  328. # 目标:${KIRO_HOME}/bin/fwh-pyrun(须与 agent.json 中 allowedCommands 的路径一致)
  329. # 同时确保沙箱目录 /tmp/fwh-toolbox-agent 存在(属主为运行用户)。
  330. install_pyrun() {
  331. local src="${KIRO_CONFIG_SRC}/bin/fwh-pyrun"
  332. local dest_dir="${KIRO_HOME%/}/bin"
  333. local dest="${dest_dir}/fwh-pyrun"
  334. if [[ ! -f "${src}" ]]; then
  335. warn "未找到 fwh-pyrun 源(${src}),跳过部署(Python3 沙箱能力不可用)"
  336. return 0
  337. fi
  338. info "部署 fwh-pyrun:${src} → ${dest}"
  339. run_fs "sudo mkdir -p '${dest_dir}'"
  340. run_fs "sudo cp -f '${src}' '${dest}'"
  341. run_fs "sudo chmod 0755 '${dest}'"
  342. run_fs "sudo chown '${KIRO_RUN_AS_USER}' '${dest}'"
  343. # 沙箱可写目录(唯一持久写入点),属主为运行用户
  344. run_fs "sudo mkdir -p /tmp/fwh-toolbox-agent && sudo chown '${KIRO_RUN_AS_USER}' /tmp/fwh-toolbox-agent"
  345. ok "fwh-pyrun 部署完成:${dest}"
  346. }
  347. # ---------------------------------------------------------------------------
  348. # 5. 校验
  349. # ---------------------------------------------------------------------------
  350. verify() {
  351. info "校验部署结果 ..."
  352. if is_dry; then
  353. info "(dry-run) 跳过实际校验"
  354. return 0
  355. fi
  356. local sub
  357. for sub in "${CONFIG_SUBDIRS[@]}" skills; do
  358. if [[ -d "${KIRO_DEST}/${sub}" ]]; then
  359. ok "存在:${KIRO_DEST}/${sub}"
  360. else
  361. warn "缺失:${KIRO_DEST}/${sub}"
  362. fi
  363. done
  364. local name dir
  365. for dir in "${KIRO_CONFIG_SRC}/skills"/*/; do
  366. [[ -d "${dir}" ]] || continue
  367. name="$(basename "${dir}")"
  368. [[ -f "${KIRO_DEST}/skills/${name}/SKILL.md" ]] \
  369. && ok "自定义 skill 就绪:${name}" \
  370. || warn "自定义 skill 缺 SKILL.md:${name}"
  371. done
  372. if has npx && [[ "${KIRO_SKILLS_INSTALL}" == "y" ]]; then
  373. info "已安装 skill(项目级,HOME=${KIRO_HOME}):"
  374. as_target_user "cd '${KIRO_HOME}' && npx --yes skills list 2>/dev/null" || true
  375. fi
  376. # fwh-pyrun 沙箱执行器校验
  377. local pyrun="${KIRO_HOME%/}/bin/fwh-pyrun"
  378. if [[ -x "${pyrun}" ]]; then
  379. ok "fwh-pyrun 就绪:${pyrun}"
  380. else
  381. warn "fwh-pyrun 缺失或不可执行:${pyrun}(Python3 沙箱能力不可用)"
  382. fi
  383. has bwrap && ok "bwrap 可用:$(command -v bwrap)" || warn "bwrap 不可用(Python3 沙箱能力不可用)"
  384. has python3 && ok "python3 可用:$(command -v python3)" || warn "python3 不可用(Python3 沙箱能力不可用)"
  385. # 模板校验
  386. if [[ -d "${KIRO_HOME%/}/templates" ]]; then
  387. ok "模板目录就绪:${KIRO_HOME%/}/templates"
  388. else
  389. warn "模板目录缺失:${KIRO_HOME%/}/templates"
  390. fi
  391. # 占位符残留校验(替换后不应再出现 __XXX__)
  392. if grep -rlE '__(KIRO_HOME|WIKI_DOCS|OFFICECLI_BIN)__' "${KIRO_DEST}/agents" "${KIRO_DEST}/skills" >/dev/null 2>&1; then
  393. warn "agents/skills 中仍存在未替换的占位符,请检查 render_placeholders"
  394. else
  395. ok "占位符已全部替换(agents + skills)"
  396. fi
  397. }
  398. # ---------------------------------------------------------------------------
  399. # main
  400. # ---------------------------------------------------------------------------
  401. main() {
  402. info "== 部署 Kiro CLI 配置 =="
  403. info "运行用户 = ${KIRO_RUN_AS_USER}(passwd HOME=$(resolve_user_home))"
  404. info "配置来源 = ${KIRO_CONFIG_SRC}"
  405. info "部署目标 = ${KIRO_DEST}"
  406. info "目标 agent = ${KIRO_AGENT}"
  407. info "命令安装 = ${KIRO_SKILLS_INSTALL} dry-run = ${KIRO_DRY_RUN}"
  408. if [[ "$(resolve_user_home)" != "${KIRO_HOME%/}" ]]; then
  409. warn "KIRO_HOME(${KIRO_HOME}) 与该用户 passwd 家目录($(resolve_user_home)) 不一致;"
  410. warn "请确认 config.env 的 KIRO_HOME 与后端拉起 kiro-cli 时的 HOME 一致。"
  411. fi
  412. ensure_prereqs
  413. prepare_dest
  414. sync_config_dirs
  415. install_skills_sh
  416. copy_custom_skills
  417. install_templates
  418. install_pyrun
  419. render_placeholders
  420. verify
  421. echo
  422. echo -e "${c_green}Kiro CLI 配置部署结束${c_reset}"
  423. cat <<EOF
  424. 目标目录 : ${KIRO_DEST}
  425. 已同步 : ${CONFIG_SUBDIRS[*]}
  426. 命令安装源 : ${KIRO_SKILLS_LIST}
  427. 拷贝源 : ${KIRO_CONFIG_SRC}/skills/(扫描实际目录)
  428. 模板目录 : ${KIRO_HOME%/}/templates(公司 PPT 模板等)
  429. 沙箱执行器 : ${KIRO_HOME%/}/bin/fwh-pyrun(bwrap 隔离 Python3)
  430. 占位符替换 : __KIRO_HOME__=${KIRO_HOME%/} __WIKI_DOCS__=${KIRO_WIKI_DOCS} __OFFICECLI_BIN__=${KIRO_OFFICECLI_BIN}
  431. EOF
  432. }
  433. main "$@"