返回技能市场
开发运维 安全

ECS代码一键部署

@aliyun/alibabacloud-ecs-code-deploy

基于 aliyun appmanager 一键部署项目到阿里云 ECS,覆盖环境预检、询价、部署、日志验证全流程。支持 App/AI Agent,新建或已有实例,自动处理分组冲突、zip 解压、余额不足,重复部署免初始化。

云Skills门户 热度 234v1.0.3

通过 aliyun appmanager 部署到阿里云 ECS

概述

aliyun appmanager 是一款对 Agent 友好的 CLI 工具,可将应用(App)和 AI Agents 一键部署到阿里云 ECS。它支持非交互模式(--non-interactive)、结构化 JSON 输出(--output json)和流式 NDJSON 响应。

默认行为:用户调用 /alibabacloud-ecs-code-deploy 时,如果未指定项目路径或 URL,则将当前工作目录中的项目部署到阿里云 ECS。如果用户提供 git URL,则先将其克隆到当前目录,然后 cd 进入克隆后的目录并继续部署。

执行顺序:Agent 必须遵循本文档底部“完整部署工作流”一节所规定的正确执行顺序。下面各任务部分按主题组织,仅供参考,其编号并不表示执行顺序。

---

强制要求:开始前创建待办事项列表

执行任何步骤之前,Agent 必须创建包含以下所有项目的待办事项列表。不得遗漏任何项目。在创建待办事项列表之前,不得开始部署。

Todo list (Deploy to Alibaba Cloud ECS):
  [ ] 0. Resolve $SKILL_DIR (cross-platform path — MUST run first; see "Step 0" below)
  [ ] 1. Environment pre-check (MUST run deploy_toolkit.py check; manual commands FORBIDDEN as replacement)
  [ ] 2. Obtain project (clone git URL here if needed; skip for local projects)
  ── Check whether .appmanager/config.yaml already exists (repeat-deploy shortcut) ──
  │  Exists + new ECS (no instanceId)      → skip 3-5, start from 5.5 (price check)
  │  Exists + existing ECS (has instanceId) → skip 3-5.5, jump to 6 (deploy)
  │  Does not exist                        → proceed normally from 3
  ───────────────────────────────────────────────────────────────────────────────
  [ ] 3. Read project (README.md -> quick-deploy method) + identify type (agent / app)
  [ ] 4. Ask user for deployment config (region + new ECS / existing ECS)
  [ ] 5. Init + generate scripts (appmanager init -> write start/stop scripts to config.yaml)
  [ ] 5.5. Pre-deploy price check + risk warning (MUST run deploy_toolkit.py price; confirm price / OSS billing / existing-ECS impact / group overwrite item by item)
  [ ] 6. Deploy (MUST run deploy_toolkit.py deploy; manual deploy command FORBIDDEN as replacement)
  [ ] 7. Verify (MUST run deploy_toolkit.py verify; manual status command FORBIDDEN as replacement)
  [ ] 8. Output final result (console link + cost reminder + management commands)
⛔ 脚本优先规则:步骤 1、5、6、7 在 $SKILL_DIR/scripts/deploy_toolkit.py 设有专用工具包脚本(其中 $SKILL_DIR 将在下面的步骤 0中解析——适用于 Qoder、Claude Code 及任何其他平台)。Agent 必须直接运行对应子命令,并将其作为该步骤的第一个且唯一操作——禁止在运行脚本之前执行手动 CLI 命令(例如 aliyun version、版本检查、凭证检查),也不得用这些命令代替脚本。该脚本已在内部处理所有检查。仅当脚本文件本身不存在时,才允许将手动命令作为备用方案。
❌ 错误(步骤 1):运行 aliyun version → 检查版本 → 运行 ~/.aliyun/appmanager-venv/bin/python ... → 检查版本 → 然后再运行 deploy_toolkit.py check
✅ 正确(步骤 1):运行 python3 "$SKILL_DIR/scripts/deploy_toolkit.py" check → 如果出现 exit 1,修复其报告的问题 → 如果脚本文件缺失,此时才回退到手动检查
第 6 项没有商量余地。跳过日志验证并直接输出“部署成功”的 Agent 并未正确完成此 skill。如果 deploy_toolkit.py verify 以 1 退出(失败),Agent 必须先修复问题并重新部署,然后才能继续执行第 7 项。

---

步骤 0(强制要求):解析 $SKILL_DIR——跨平台路径

工具包脚本位于 <skill-root>/scripts/deploy_toolkit.py。不同平台会将 skills 安装到不同位置(Qoder/Claude Code/千问/...)。Agent 必须在会话开始时仅解析一次 skill 根目录的绝对路径,并在下文每个出现 $SKILL_DIR 的位置复用该路径。严禁硬编码任何平台特定路径。

有关完整的 10 个候选位置检测算法、export + test -f 验证代码片段,以及模式 A / 模式 B / ⛔ 反模式使用规则,请参阅 [references/skill-dir-resolution.md](references/skill-dir-resolution.md)。

快速回顾(详细信息请阅读参考文档):

  • 模式 A(持久 shell):先执行 export SKILL_DIR="/abs/path",之后再执行 python3 "$SKILL_DIR/scripts/deploy_toolkit.py" <sub>
  • 模式 B(每次调用都使用新的 shell):内联绝对路径——python3 "/abs/path/scripts/deploy_toolkit.py" <sub>
  • 反模式SKILL_DIR=/path python3 "$SKILL_DIR/..."——外层 shell 会在前缀赋值之前展开 $SKILL_DIR,从而生成 /scripts/deploy_toolkit.py 并出现 ENOENT。如果看到 python3: can't open file '/scripts/deploy_toolkit.py',请改用模式 A 或 B。

---

任务 1:安装阿里云 CLI

主要操作:运行 python3 "$SKILL_DIR/scripts/deploy_toolkit.py" check——该脚本会一次性检查 CLI 版本、appmanager-cli 版本和凭证。仅当脚本文件缺失时,才使用 [references/init-and-credentials.md](references/init-and-credentials.md) 中的备用方案。

必须 — 处理未满足的环境前置条件:当 check 因 aliyun CLI 缺失或版本低于 3.3.19(或 appmanager-cli 缺失/版本过旧)而以 1 退出时,Agent 不得静默停止工作流。必须遵循以下流程:
1. 未安装 CLI无需询问用户,直接自动安装 — 执行安装命令(请参见下方代码片段,或参见 [references/init-and-credentials.md](references/init-and-credentials.md) 以选择正确的架构),然后重新运行 deploy_toolkit.py check 进行确认。
2. CLI 已安装必须先询问用户 — 展示检测到的版本、所需版本和升级命令,并征得明确同意(例如,“aliyun CLI 3.3.4 已安装,但未达到 appmanager 要求的版本(>= 3.3.19);是否同意升级(覆盖安装到 /usr/local/bin,需要 sudo)?”)。不得擅自视为同意;不得粘贴凭证。
- 用户同意后 — 执行安装/升级命令,然后重新运行 deploy_toolkit.py check 进行确认。
- 用户拒绝后 — 以用户拒绝为由停止流程。不得继续使用旧版本(部署无论如何都会失败)。
工具包的 check 输出中已针对每个可修复问题包含一行 → AGENT: DO NOT stop. ASK user ... — 必须逐字遵循(“未安装”情况除外,该情况会自动处理)。
必须 — 升级方式优先级:由 brew 管理 → brew upgrade(不得再次覆盖 /usr/local/bin/);可使用 sudo → 覆盖 /usr/local/bin/;无 sudo → ~/bin/ + 持久化 PATH(Unix)。Scoop/Choco → %USERPROFILE%\bin + 用户级 PATH(Windows)。完整优先级规则 → [references/init-and-credentials.md](references/init-and-credentials.md) § “CLI 安装/升级方式优先级”。安装/升级后,必须始终重新运行 deploy_toolkit.py check 进行确认;若 which -a aliyun 仍将旧二进制文件显示在首位,请修正 PATH 顺序 — 不得重复执行相同的覆盖操作。

可观测性(强制要求):User-Agent 注入

⛔ 每次 aliyun CLI 调用都必须携带此 skill 的 User-Agent,以便所有 API 请求都可在云端日志中追踪。Agent 必须在会话开始时(紧接步骤 0 之后)且在调用任何 aliyun / deploy_toolkit.py 之前,仅导出一次 UA 环境变量。

完整 UA 模板:

AlibabaCloud-Agent-Skills/alibabacloud-ecs-code-deploy/{session-id}

session-id 统一规则:

  • 每个 agent 会话仅生成一次:SESSION_ID=$(uuidgen | tr '[:upper:]' '[:lower:]') — 使用小写 UUID(任何由小写十六进制字符组成且长度 ≥ 16 个字符的随机令牌均等效)。
  • 在会话内的所有 CLI 调用中复用同一个 session-id — 禁止为每条命令重新生成,否则一次部署会分散为无法追踪的多个片段。
  • 按照步骤 0 的规则,以一致方式持久化:模式 A 使用 export SESSION_ID=...(持久化命令行环境),或内联字面值(每次调用均使用新的命令行环境)。

注入方式(现代方式 — 环境变量):

export ALIBABA_CLOUD_USER_AGENT="AlibabaCloud-Agent-Skills/alibabacloud-ecs-code-deploy/${SESSION_ID}"
⛔ 旧版 aliyun configure ai-mode enable / set-user-agent 机制已标记为 DEPRECATED — 不得使用。对于同一个 skill 令牌,不得将 ALIBABA_CLOUD_USER_AGENT 与 ai-mode 或按命令设置的环境变量前缀结合使用:CLI 会叠加这些来源并生成重复的 User-Agent。每个会话仅设置一次环境变量,此后保持不变。

---

任务 2:配置凭证

deploy_toolkit.py check 已包含凭证验证。仅当凭证缺失或无效时,才参见 [references/init-and-credentials.md](references/init-and-credentials.md) 中的默认凭证链修复流程。

⛔ 优先使用 OAuth 而非 AK:OAuth 更安全、更方便(无需长期存储密钥)。Agent 必须将 OAuth 作为首选项。用户同意后,Agent 直接运行登录命令 — 但必须先警告用户,该命令会打开浏览器授权页面,并且会一直阻塞,直到用户在浏览器中完成授权 — 然后执行 aliyun configure --mode OAuth --profile oauth 并使用 aliyun sts get-caller-identity --profile oauth 进行验证。
⛔ SA-2.12 — 禁止在聊天中收集 AK/SK:如果用户倾向于使用 AK 认证,请提供一个带占位符且可直接运行的命令模板 — aliyun configure set --profile default --mode AK --access-key-id <your-access-key-id> --access-key-secret <your-access-key-secret> --region cn-hangzhou — 并要求用户自行填入自己的 AK/SK,然后在自己的终端中运行。Agent 禁止要求用户将 AccessKey/密钥/STS 令牌值粘贴到聊天中,禁止在工具调用参数或脚本中放入原始 AK/SK,并且禁止回显凭证值。默认凭证链(ECS RAM 角色 / 环境变量 / 已有的 ~/.aliyun/config.json)也受支持。完整修复流程 → [references/init-and-credentials.md](references/init-and-credentials.md)。
严重禁令:严禁单独运行 appmanageraliyun appmanager login

---

任务 3:初始化项目

步骤 1(强制要求):首先读取 README.md

关键顺序规则:在扫描任何项目文件之前,Agent 必须先读取项目根目录中的 README.md(或 README)。这始终是任务 3 中的第一个操作。

要从 README 提取的内容:

  • 快速开始 / 部署命令(例如 pip install -r requirements.txt && python main.pynpm install && npm start
  • 官方构建/运行命令、Docker 部署方法、端口号、必需的环境变量

强制要求:向用户展示 README 中的方法并遵循决策树

步骤 A:向用户列出 README 提供的内容。

步骤 B:按优先级选择方法:

| 优先级 | 方法类型 | 操作 | |----------|------------|--------| | 1 (HIGHEST) | 原生 CLI / 包管理器安装npm install -gpip installgo install) | 直接使用 | | 2 | 原生构建 + 运行pip install && python main.pynpm install && npm start) | 使用,并安装运行时 | | 3 | 基于脚本的部署bash deploy.sh) | 必须确认支持非交互模式 | | 4 (LOWEST) | Docker / docker-compose | 仅当不存在更高优先级的方法时使用;检查能否从中国访问 |

步骤 C:根据场景执行:

  • README 提供原生方法(优先级 1/2) → 直接将其用作启动脚本的核心。不得忽略 README 而从头构建。
  • README 仅提供 Docker 方法 → 检查镜像可访问性(参见 [references/script-templates.md](references/script-templates.md) 中的“Docker 镜像可访问性检查”)。向用户警示中国镜像源风险。
  • README 没有部署信息或不存在 → Agent 独立扫描项目文件(仅允许在此情况下执行)。
为什么要先读取 README? 大多数项目都记录了确切的构建/运行命令。仅根据文件自动检测容易出错。

---

步骤 2:确定项目类型

| 条件 | 类型 | |-----------|------| | 项目依赖于 agentscope | agent | | 其他所有情况(langchain、mcp、autogen、Web 服务、工具等) | app |

确定 --name

使用项目目录名称(全小写,使用连字符)。告知用户:Default app name uses the directory name <name>

确定 --region 和 ECS 目标(必须询问用户)

Agent 必须在一条消息中同时询问以下两个问题:

1. 您希望部署到哪个地域?
- 上海 (cn-shanghai) / 杭州 (cn-hangzhou) / 北京 (cn-beijing) / 深圳 (cn-shenzhen) / 广州 (cn-guangzhou) / 成都 (cn-chengdu) / 南京 (cn-nanjing) / 香港 (cn-hongkong)
2. 新建 ECS 还是已有 ECS?
- 新建 ECS(自动创建实例,按量付费)
- 已有 ECS(从下方列表中选择,或手动提供实例 ID)

不得使用“华东 1”/“华北 2”等基于可用区的标签。不得添加说明。只能使用城市名称。

已有 ECS — 列出用户在所选地域中最近创建的 10 个实例(强制要求)

当用户选择已有 ECS时,Agent 必须首先查询该账号在所选地域中最近创建的 10 个实例(插件模式:aliyun ecs describe-instances),并以编号列表形式展示以供选择 — 不得强迫用户凭记忆回想实例 ID。还必须始终允许手动输入实例 ID(例如 i-bp1xxxxxxxx)作为备用方案。查询时必须传入与部署相同的 --profile(即同一账号),否则可能返回零个实例或不正确的实例。完整命令(jq 管道、无 jq 时的备用方案、零实例处理)→ [references/init-and-credentials.md](references/init-and-credentials.md) §“列出已有 ECS 实例”。所选或输入的 ID 将传递给 appmanager init --ecs existing --instance-id <ID>

⚠️ 地域传递检查(强制要求):所选地域必须原样传递给 appmanager init --region,写入 config.yaml 中的 common.deployment.regionId,且后续每次调用 deploy_toolkit.py {price,deploy,verify} 时都必须附加 --region <REGION_ID>。地域不一致或遗漏会触发 OOS API 返回的 InvalidParameter: DeployRegionId is invalid

确定 --port(仅限 App 类型,可选)

仅当项目实际监听 HTTP 时才指定。后台服务(机器人、工作进程、CLI 工具)应跳过。需要但未知时,默认为 8080。Agent 类型不使用 --port

非交互式初始化

有关所有初始化参数组合,请参阅 [references/init-and-credentials.md](references/init-and-credentials.md)。

将创建 .appmanager/config.yaml。不支持 --overwrite — 如果 .appmanager/ 已存在,请先删除。

---

任务 4:生成部署脚本

对于所有项目类型,Agent 都必须生成部署脚本并将其写入 .appmanager/config.yaml

工作流

  1. 首先读取 README.md — 遵循任务 3 的决策树
  2. 如果 README 中没有部署信息 — 扫描项目结构(见下文“语言检测”)
  3. Docker 可访问性检查 — 如果选择了 Docker 方案(请参阅 [references/script-templates.md](references/script-templates.md))
  4. 生成启动和停止脚本 — 遵循下述规则。启动脚本必须始终包含 zip 解压流程。
  5. 写入 config.yaml — 写入 common.scripts.startcommon.scripts.stop(不得写入顶层 scripts

语言检测、待读取文件和入口点

语言检测规则(标识文件)、每种语言必须读取的文件以及入口点检测顺序 → [references/script-templates.md](references/script-templates.md) §“项目分析”。

通用脚本规则

| 规则 | 要求 | |------|-------------| | ⛔ 强制要求解压 zip | 启动脚本必须执行以下操作:查找 zip 文件 → mkdir -punzip -ocd。若缺少此流程,ECS 上将不存在项目目录 | | 安装运行时 | 必须首先安装语言运行时(ECS 是纯净环境) | | 安装 unzip | command -v unzip &>/dev/null \|\| $PKG_MGR install -y unzip | | 幂等 | 可安全地多次运行 | | ⛔ 日志文件路径固定 | 必须为 /root/app.log/root/app.pid。验证脚本已硬编码这些路径 | | 日志追加 | 始终使用 >>(不得使用 >) | | PID 文件 | 在 nohup ... & 之后执行 echo $! > /root/app.pid | | 后台运行 | nohup ... >> /root/app.log 2>&1 & | | 停止旧进程 | [ -f /root/app.pid ] && kill "$(cat /root/app.pid)" 2>/dev/null \|\| true | | 应用目录 | /root/{app_name} | | 禁止使用 heredoc | 禁止在脚本中使用 << 'EOF',否则会破坏 YAML 格式。请使用 printfpython3 -c | | 强制要求在末尾输出日志 | 必须以 sleep 3 && cat /root/app.log 结尾,以便在验证时捕获输出 | | ⛔ 停止脚本:禁止退出 | 不得包含 exit。部署系统会将停止脚本与启动脚本拼接在一起,而该命令会终止整个进程 |

ECS 实例是纯净的 Linux 环境(通常为阿里云 Linux,基于 RHEL,使用 yum/dnf)。

有关脚本模板、语言安装命令和 config.yaml 写入方法,请参阅 [references/script-templates.md](references/script-templates.md)。

---

任务 4.5:部署前价格检查 + 风险警告

强制要求:部署前,运行 deploy_toolkit.py price。该脚本会输出 价格估算(含 OSS 额外计费提醒),并在适用时输出 风险警告区块。Agent 必须向用户展示所有标记项,并在运行 deploy 之前获得明确确认。
python3 "$SKILL_DIR/scripts/deploy_toolkit.py" price --config .appmanager/config.yaml
  • 退出码为 0 且输出包含 === AGENT_CONFIRM_REQUIRED ===:向用户展示 完整的 价格信息和风险警告,并逐项确认。
  • 退出码为 1:价格查询失败;不得继续部署。

Agent 必须确认最多 3 项(价格 + OSS 费用 / 现有 ECS 风险 / 分组覆盖选项)——详细说明和示例措辞请参阅 [references/deploy-output-and-management.md](references/deploy-output-and-management.md) § "部署前价格检查:确认项"。

在完成所有适用的确认之前,Agent 不得调用 deploy_toolkit.py deploy

---

任务 5:部署

aliyun appmanager <agent|app> deploy --overwrite --output json
部署成功后先停止输出结果status: success 仅表示编排已完成。Agent 必须先执行任务 6 的验证,再输出结果。

处理部署失败

强制失败处理门禁:发生任何部署失败(exit 1、超时或 ReleaseCancelled)后,Agent 必须立即运行 deploy_toolkit.py verify,且必须在任何修复尝试、改用手动命令或输出部分结果之前执行。失败后严禁跳过验证,否则视为 skill 执行失败。
ReleaseCancelled 的含义:它表示 ECS 上的启动脚本执行失败或超时,表示“有人取消了部署”。唯一正确的下一步操作是:运行 deploy_toolkit.py verify -> 读取日志 -> 修复脚本 -> 重新部署。
已知失败模式:在进行临时故障排查之前,请查看 [references/lessons-learned.md](references/lessons-learned.md),了解此前发现的部署失败模式和经验证的修复方案。

失败处理流程:

  1. 运行 deploy_toolkit.py verify 获取 /root/app.log(不得跳过)。
  2. 分析日志以定位根本原因。
  3. 修复脚本并重新部署(最多尝试 3 次)。
  4. 失败 3 次后,停止——报告错误,但仍需输出控制台链接 + 费用提醒 + 删除命令。

---

任务 6:部署后验证(阻塞)

status: Deployed 并不表示应用正在运行。Agent 必须执行验证并对日志进行语义分析。
  1. 运行 deploy_toolkit.py verify(会自动从 config.yaml 读取参数)。
  2. Agent 对日志进行语义分析,以判断应用是否确实已成功启动。
  3. 未运行 -> 诊断 -> 修复 -> 重新部署 + 验证(最多尝试 3 次)。
  4. 只有在已确认运行 / 需要用户手动操作 / 3 次尝试均失败时,Agent 才应输出最终结果。

---

任务 7 和 8:列出、删除、验证和最终输出

以下内容请参阅 [references/deploy-output-and-management.md](references/deploy-output-and-management.md):

  • 列出/删除命令
  • 配置验证
  • 配置模板参考
  • 关键注意事项与常见陷阱
  • 强制要求的部署后输出格式(控制台链接、费用提醒、使用指南)

输出前检查点 — 自检(⛔ 阻塞)

输出结果前,Agent 必须原样打印 Deployment self-check report 模板(请参阅工作流步骤 7.5)。跳过该报告 = skill 执行失败(并非可选摘要)。如果任何一项为 ❌,请在输出步骤 8 前修复。

📘 包含具体输入/输出和边界情况的实操演练(Python Flask 示例):请参阅 [references/tutorial-flask-app.md](references/tutorial-flask-app.md)。

完整部署工作流

⛔ 强制执行规则:Agent 必须严格遵循此顺序。对于指定了脚本的步骤(步骤 1、5、6),Agent 必须运行该脚本——禁止使用单独的命令手动复现脚本逻辑。上述任务章节仅供参考(用于了解脚本内部的工作方式,或在缺少脚本时作为备用方案)。
# 0. Resolve $SKILL_DIR (MANDATORY — see "Step 0" section above for full algorithm)
# → Detect the absolute directory containing THIS SKILL.md (most accurate)
# → Or fall back to platform-specific candidates: ~/.qoder/skills/..., ~/.claude/skills/..., ~/.qwen/skills/..., $SKILLS_HOME/..., etc.
# → Pattern A (persistent shell): export SKILL_DIR=<abs_path> ; verify $SKILL_DIR/scripts/deploy_toolkit.py exists ; reuse $SKILL_DIR everywhere
# → Pattern B (fresh shell per command): inline the absolute path — `python3 "/abs/path/scripts/deploy_toolkit.py" ...`
# → ⛔ NEVER use `SKILL_DIR=/path python3 "$SKILL_DIR/..."` — outer shell expands $SKILL_DIR
#    BEFORE the prefix assignment, producing `/scripts/deploy_toolkit.py` and ENOENT.

# 1. Environment check (MUST use deploy_toolkit.py check — DO NOT run manual commands)
python3 "$SKILL_DIR/scripts/deploy_toolkit.py" check
# ⛔ FORBIDDEN: running `aliyun version`, `~/.aliyun/appmanager-venv/bin/python -c "..."`,
#    credential checks, or ANY manual version-check commands before or instead of this script.
#    The script checks ALL of: CLI version + appmanager-cli version + credentials in one run.
#    Just run the script. Period.
# → If exit 0: all checks passed, proceed to step 2
# → If exit 1, address the issue printed by the script:
#    ⚠️ DO NOT stop silently. For every fixable ❌ line the script prints,
#       Agent MUST follow the flow below:
#       - aliyun CLI NOT installed: AUTO-INSTALL directly (no need to ask user)
#       - aliyun CLI already installed but outdated: ASK user to approve upgrade
#         (covers to /usr/local/bin, needs sudo), then run the install command
#         printed by the script (see Task 1).
#       - appmanager-cli < 1.1.1 or BROKEN venv: ASK user to approve
#         `rm -rf ~/.aliyun/appmanager-venv` (auto-recreates on next aliyun
#         appmanager run).
#         ⚠️ This path is fixed at ~/.aliyun/appmanager-venv (the venv is self-managed
#            by the aliyun CLI). After deletion, the next `aliyun appmanager` run
#            auto-recreates it. The Agent MUST use this exact literal path —
#            NEVER replace it with a variable or build it via concatenation,
#            to avoid accidentally wiping user data.
#       - credentials missing/invalid: present OAuth-first remediation
#         to the user (OAuth / RAM Role / env vars / `aliyun configure` interactive) — NEVER
#         collect AK/SK in chat. See Task 2 + references/init-and-credentials.md.
# → If user refuses any fix: stop with that refusal as the reason — DO NOT
#   continue with a broken environment (deployment will fail anyway).
# → If script file not found: ONLY THEN fall back to manual checks (Task 1 + Task 2)

# 2. Obtain project source (if needed)
# → If git URL provided: clone to CURRENT WORKING DIRECTORY, cd into cloned dir
#    git clone <URL> && cd <cloned_dir>
# → If local path / current directory: skip this step, use directly

# 🔀 REPEAT DEPLOYMENT SHORTCUT — check BEFORE step 3
# → Check if .appmanager/config.yaml already exists in the project directory
# → If YES (config.yaml exists):
#    Read the file and check for common.deployment.instanceId:
#      - instanceId ABSENT (new ECS): skip steps 3-5, jump to step 5.5 (price check)
#      - instanceId PRESENT (existing ECS): skip steps 3-5.5, jump to step 6 (deploy)
#    In both cases, inform user: "Existing .appmanager/config.yaml detected; will reuse the existing config and deploy directly."
# → If NO (config.yaml does NOT exist): proceed normally from step 3

# 3. Read project + identify type (agent or app)
# → READ README.md FIRST — highest priority source for deployment method:
#    - Agent MUST list README's methods to user and select by priority:
#      Native CLI install > Native build+run > Script deploy > Docker
#    - ❌ NEVER ignore README methods and scan project files instead
#    - ❌ NEVER prefer Docker when native methods are available
#    - Docker: ONLY when no native method exists, MUST warn user about China mirror risks
#    - Only if README absent/empty/no deploy info → Agent scans project files independently
# → Only classify as "agent" if project depends on `agentscope`; everything else is "app"
# → Determine --name from directory name, --port from project config/README
# → For Docker: check image accessibility from China (see references/script-templates.md)

# 4. Ask user for deployment region + ECS target (MANDATORY — ask together in one question)
# → Question 1: "Which region do you want to deploy to? Shanghai(cn-shanghai)/Hangzhou(cn-hangzhou)/Beijing(cn-beijing)/Shenzhen(cn-shenzhen)/Guangzhou(cn-guangzhou)..."
# → Question 2: "New ECS or existing ECS?" — if existing, FIRST query the account's 10 most-recently-created
#   ECS instances in the chosen region and present them as a numbered list for selection:
#     aliyun ecs describe-instances --biz-region-id <REGION> --region <REGION> --page-size 100 | jq -r '.Instances.Instance | sort_by(.CreationTime) | reverse | .[:10] | .[] | "..."'  (plugin mode; no `--output json`; JSON is the default)
#   Also always support manual instance ID entry (e.g. i-bp1xxxxxxxx). See Task 3 "Existing ECS" section.
# → NEVER use zone-based labels like "East China 1" / "North China 2" — always use city names
# → NEVER skip the ECS choice and default to creating new
# → ⚠️ Region MUST be propagated verbatim to: appmanager init --region, config.yaml common.deployment.regionId, AND every deploy_toolkit.py --region. Mismatch → InvalidParameter: DeployRegionId from OOS API.

# 5. Init + generate scripts (appmanager init → write start/stop to config.yaml)
# → If .appmanager/ already exists in the CURRENT project directory, ask user before removing.
#   ⚠️ DESTRUCTIVE: `rm -rf .appmanager` deletes the existing deployment config.
#      Required guard before deletion:
#        a. Confirm CWD matches the intended project directory (`pwd` shows expected path)
#        b. Confirm target is the relative path `.appmanager` (NEVER absolute, NEVER with variables)
#        c. Inform the user "About to delete the existing deployment config under ./.appmanager/. This is irreversible." and obtain consent
#      Recommended safer alternative: back up first
#        mv .appmanager .appmanager.bak.$(date +%Y%m%d%H%M%S)
#      Only after explicit user consent: rm -rf ./.appmanager
# → Run: aliyun appmanager init --non-interactive --name <DIR_NAME> --type <app|agent> --region <REGION> [--port <PORT>] [--ecs existing --instance-id <ID>] [--model qwen3.6-plus --api-key "$API_KEY"]
#   (See references/init-and-credentials.md for full flag combinations by type)
# → Then generate start/stop scripts and write to config.yaml:
#   - MUST write to common.scripts.start and common.scripts.stop (NEVER top-level scripts key)
#   - Use python3 yaml library: config['common']['scripts'] = {'start': ..., 'stop': ...}
#   - PRIORITY: README deployment commands → use directly; only auto-generate when README has none
#   - ⛔ MANDATORY: Start script MUST ALWAYS include zip extraction (mkdir + unzip + cd) BEFORE
#     any build/run commands. appmanager uploads zip but does NOT extract it.

# 5.5. Pre-deploy price check + risk warning (MUST run deploy_toolkit.py price — Agent handles user confirmation)
python3 "$SKILL_DIR/scripts/deploy_toolkit.py" price --config .appmanager/config.yaml
# → Script output structure:
#    [Price table] estimate from `appmanager price` (order-billed resources: ECS/EIP/bandwidth) + the trailing 📦 OSS extra-billing reminder
#    [Risk warning] only when detected: [Existing-ECS deployment risk] / [Group overwrite risk] / [Failure-leftover group]
# → Script does NOT ask user for confirmation — that's the Agent's job
# → If exit 0: Agent MUST read the output, present the COMPLETE breakdown to user — including:
#    1) Price estimate + OSS extra-billing reminder (OSS storage ~CNY 0.12/GB/month, public outbound ~CNY 0.50/GB only when cross-region, requests billed per 10k)
#    2) If output contains [Existing-ECS deployment risk] -> ask whether to deploy onto that existing ECS (may impact other apps on it)
#    3) If output contains [Group overwrite risk] -> ask user to choose A (overwrite) or B (new group)
#    Example: "Estimated cost: compute resources CNY X.XXX/hour (~CNY XXX.XX/month); public traffic billed by usage at CNY 0.80/GB;
#             the deployment also incurs minor OSS storage and request fees (intra-region pull is free of public outbound charges).
#             Confirm to continue?"
# → After ALL items confirmed by user: run the matching deploy command per item 3's choice (default overwrite / --force-new-group for new group)
# → If ANY item refused: STOP deployment
# → If exit 1: price query failed, show error to user, do NOT proceed

# 6. Deploy (MUST use deploy_toolkit.py deploy — DO NOT run deploy manually)
python3 "$SKILL_DIR/scripts/deploy_toolkit.py" deploy \
  --type <agent|app> --name <APP_NAME> --group <GROUP_NAME> --region <REGION_ID>
# ⛔ FORBIDDEN: running `aliyun appmanager deploy` directly without this script
# → Handles: group status check → conflict auto-resolve → deploy
# → Exit 0: deploy submitted, proceed to step 7
# → Exit 1: ⛔ MUST run step 7 (verify) IMMEDIATELY to fetch /root/app.log;
#           skipping to step 8, outputting partial results, or running manual
#           commands instead is FORBIDDEN. Then fix script per log and redeploy
#           (max 3 attempts).

# 7. Verify (MUST use deploy_toolkit.py verify — DO NOT check status manually)
python3 "$SKILL_DIR/scripts/deploy_toolkit.py" verify \
  --type <agent|app> --name <APP_NAME> --group <GROUP_NAME> --region <REGION_ID>
# ⛔ FORBIDDEN: running `aliyun appmanager status` + manual log analysis instead of this script
# → Optional: --wait <seconds> for slow-starting apps (default 3s, Java/heavy use 15-30)
# → Dual-path: Cloud Assistant cat /root/app.log (preferred) → deployCommandOutput (fallback)
# → Exit 0: app running, proceed to step 8
# → Exit 1: app failed — fix start script, re-deploy (back to step 6)
# → Exit 2: inconclusive — retry with longer --wait or suggest SSH check

# 7.5. Self-check summary (⛔ BLOCKING — skill fails if omitted)
# MUST print the exact template below to the user — this is a completion criterion, NOT optional.
#
# ---
# ✅ Deployment self-check report:
#   0. Path resolution — SKILL_DIR=___ (script exists ✅)
#   1. Environment pre-check — CLI v___ / appmanager-cli v___ / credentials valid ✅
#   2. Project obtained — (local / cloned) ✅
#   3. Project identified — type: ___ / deploy method source: README.md ✅
#   4. Deployment region — user choice: ___ ✅
#   5. Init + scripts — config.yaml generated; start script: ___ (key command summary) ✅
#   5.5. Pre-deploy price check — user confirmed price (CNY ___/hour, ~CNY ___/month) ✅
#   6. Deploy executed — deploy_toolkit.py deploy exit 0 ✅
#   7. Run verification — deploy_toolkit.py verify exit 0 / log keywords: ___ ✅
# ---
#
# If any item is ❌, fix it BEFORE step 8 — this is for the USER to see, proving the work is properly done.

# 8. Output results (MANDATORY: console link + cost reminder + management commands)
# → See references/deploy-output-and-management.md for full output format
qianwen skills install @aliyun/alibabacloud-ecs-code-deploy