Overview
Two capabilities, one skill:
- Find — the user describes a business outcome (e.g. model gateway, agent solution, knowledge base, Dify). The skill searches the live public catalog, shows matching solution names first, then lists every region for the picked solution. The solution count is whatever the catalog currently holds (never hardcode it — the script reports
total_solutions). - Deploy — after the user explicitly confirms solution + variant + region, the skill checks credentials, downloads the template, and provisions resources via Terraform with confirmation gates.
Architecture: Per-solution ECS/VPC/Subnet/Security Group/EIP pattern defined by the downloaded .tf/.tf.json template. Provider sources normalized to huaweicloud/huaweicloud and hashicorp/kubernetes.
Tool chain: Python 3.8+ (helper scripts) + Terraform 1.5+ (declarative deployment). No KooCLI. The catalog is always fetched fresh from the public OBS sac_intl.json (3 retries, never falls back to stale local data). The script reports the catalog's generated_at timestamp — always relay it to the user.
Dependency: Quality telemetry is collected automatically via skill-quality-cli (installed by scripts/ensure_cli.sh if absent).
Prerequisites
- Python 3.8+, Terraform 1.5+ — see CLI Installation Guide
- Huawei Cloud temporary credentials (AK/SK/SecurityToken) via environment variables — see Credential Gate below
- IAM user with sufficient permissions — see IAM Policies
- skill-quality-cli — ensured by bash scripts/ensure_cli.sh (idempotent, skips if present)
- Upgrade: run skill-quality-cli upgrade manually (no auto-upgrade)
- Disable telemetry report: set SKILL_QUALITY_REPORT=0
Security
- 🚫 Never expose AK/SK/Token in conversation or output
- 🚫 Never ask user to paste AK/SK/Token in chat; prefer env vars or manual tfvars edit
- 🚫 Never read or display
terraform.auto.tfvars.jsonin conversation context (contains credentials) - ✅ Only temporary credentials with SecurityToken, least privilege, shortest TTL
- ✅ Modification ops (
apply,destroy) require explicit user confirmation
Core Commands
⚠️ Mandatory: every hcloud command in this skill MUST be wrapped with
skill-quality-cli run --skill-name sac-intl-find-and-deploy-skill --— bare hcloud calls are strictly forbidden. (No hcloud commands in this skill; Terraform-only.)
Placeholder values (see Parameter Confirmation for per-OS resolution):
| Placeholder | Linux / macOS | Windows PowerShell |
|---|---|---|
<python> |
python3 |
python |
<script_dir> |
./scripts |
./scripts |
<temp_dir> |
/tmp |
$env:TEMP |
<workdir> |
sac-workdir |
sac-workdir |
# 1a. Find (Stage A): concept search -> distinct solution+variant rows (NOT truncated)
<python> <script_dir>/query_catalog.py --keyword "<business term>" --group --out <temp_dir>/sac_solutions.json
# 1b. Find (Stage B): after user picks solution+variant -> list ALL regions for it
<python> <script_dir>/query_catalog.py --solution "<solution_name>" --variant "<variant>" --out <temp_dir>/sac_candidates.json
# 2. Download template
<python> <script_dir>/download_tf_template_file.py --url "<template_url>" --out-dir <temp_dir>/<workdir>
# 3. Normalize providers (reads HW_* env vars; writes terraform.auto.tfvars.json with 600)
<python> <script_dir>/normalize_tf_providers.py <temp_dir>/<workdir> --region "<console_region>"
# 4. List variables for review
<python> <script_dir>/list_tf_variables.py <temp_dir>/<workdir>
# 5. Deploy
terraform -chdir=<temp_dir>/<workdir> init
terraform -chdir=<temp_dir>/<workdir> plan
# ⛔ STOP — Review the plan output above. Do NOT auto-apply.
# Confirm with the user (AskUserQuestion or equivalent) before proceeding.
# Only after explicit user confirmation:
terraform -chdir=<temp_dir>/<workdir> apply
# 6. Verify
terraform -chdir=<temp_dir>/<workdir> state list
terraform -chdir=<temp_dir>/<workdir> output -json
# 7. Cleanup
terraform -chdir=<temp_dir>/<workdir> destroy
# Only after terraform destroy succeeds, remove local credential residue:
rm -f <temp_dir>/<workdir>/terraform.auto.tfvars.json <temp_dir>/<workdir>/*.tfstate <temp_dir>/<workdir>/*.tfstate.backup <temp_dir>/<workdir>/*.tfplan
Workflow
Part 1 — Find
1. Collect input
Business term or desired outcome (e.g. model gateway, agent solution, knowledge base solution, Dify, SQLBot). Region only matters at step 3.
2. Stage A — show matching solutions
Run Core Commands step 1a. The script does concept-aware matching (model gateway→New API+LiteLLM, knowledge base→RAGFlow, agent→Hermes/Dify/OpenClaw/n8n…) over solution names, URLs and solution_describe text, and prints distinct solution | variant | N regions rows with a describe snippet. If nothing matches it prints the FULL catalog — show that to the user.
Freshness announcement (mandatory): the script prints catalog data as of: <generated_at> on stderr and includes catalog_generated_at / total_solutions in its JSON output. When presenting results, always start with a line like:
Below is the latest SAC solution catalog as of {catalog_generated_at} ({total_solutions} solutions in total) for your selection.
If the fetch fails entirely, the script exits with an explicit error — tell the user the fetch failed and retry later; do NOT improvise with old output. Offline debug only: --catalog <local json> override, or --insecure / --ca-bundle <pem> when a self-signed cert / MITM proxy breaks TLS.
3. Stage B — pick solution, then show ALL regions
Wait for the user to pick exactly one solution+variant. Then run Core Commands step 1b: it lists every region for that variant (no truncation, no hidden cuts). Present rows as solution_name | variant | region_label (console_region) with template: and deploy: lines. Wait for the user to explicitly pick one region. Never auto-pick. Mention the deploy_link supports console self-service without credentials.
Part 2 — Deploy
Enter Part 2 only after the user has picked one solution + variant + region.
4. Recap and confirm (deploy gate)
⛔ Before touching any infrastructure, restate the full selection and get explicit confirmation:
About to deploy:
- Solution: {solution_name}
- Version: {variant}
- Region: {region_label} ({console_region})
- Template: {template_url}
Confirm to start deployment?
Proceed only after the user says yes. If anything is off, go back to Part 1.
5. Credential gate
Check whether the three environment variables are set — existence only, never read or print the values:
[ -n "$HW_ACCESS_KEY" ] && [ -n "$HW_SECRET_KEY" ] && [ -n "$HW_SECURITY_TOKEN" ] && echo CREDENTIALS_SET || echo CREDENTIALS_MISSING
If CREDENTIALS_MISSING, tell the user plainly how to configure them — the user runs these in their own terminal environment, never pastes values into the chat:
Linux / macOS (user runs in their own terminal):
export HW_ACCESS_KEY="替换成你的AK"
export HW_SECRET_KEY="替换成你的SK"
export HW_SECURITY_TOKEN="替换成你的STS临时Token"
Windows PowerShell (user runs in their own terminal):
$env:HW_ACCESS_KEY = "替换成你的AK"
$env:HW_SECRET_KEY = "替换成你的SK"
$env:HW_SECURITY_TOKEN = "替换成你的STS临时Token"
Also remind: AK/SK/Token are sensitive credentials, ⚠️ do not paste them into the conversation; it is recommended to use least-privilege temporary credentials (including SecurityToken) that expire after use.
If the agent's shell session cannot see the user's env vars (or the user prefers files), fall back to manual edit: after running Core Commands step 3, tell the user to open <temp_dir>/<workdir>/terraform.auto.tfvars.json in an editor and fill in access_key / secret_key / security_token themselves. ⛔ The skill must never read or display that file — only confirm with the user whether it has been filled.
Block plan/apply until the user confirms credentials are in place.
6. Download and normalize template
Run Core Commands steps 2–3. normalize_tf_providers.py unifies provider sources and writes <workdir>/terraform.auto.tfvars.json (600, sensitive=true). If env vars were absent at this point, the credential fields stay empty — apply the fallback from step 5.
7. Confirm variables
Run Core Commands step 4. User must confirm login passwords, flavors, billing, and region variables. Sensitive names (key/secret/password/token) display as ******** only. Block apply on empty/weak secrets.
8. Deploy, verify, cleanup
⛔ STOP — review terraform plan, confirm with user, then apply. Verify per Verification Method and Acceptance Criteria. On cleanup, destroy first, then delete tfvars/state/plan residue and confirm.
Parameter Confirmation
| Parameter | Required | Default | Constraint |
|---|---|---|---|
keyword (stage A) |
Yes* | — | Business term or name; concept expansion applies (model gateway/agent solution/knowledge base solution…) |
group (stage A) |
Yes* | — | Stage A always uses --group; lists solution+variant rows |
solution (stage B) |
Yes* | — | Exact solution name picked from stage A |
variant (stage B) |
Yes* | — | Variant picked from stage A, e.g. single-node deployment |
limit (stage B) |
No | 500 |
Safety cap; catalog never reaches this — ALL regions listed |
region-filter (stage B) |
No | "" |
Only if the user names a region up front; otherwise list all |
console_region |
Yes | — | Taken from picked region row, e.g. ap-southeast-1 |
template_url |
Yes | — | Taken from picked row; must end .tf / .tf.json |
access_key |
Yes | <NO_DEFAULT> |
From env HW_ACCESS_KEY; never print in chat |
secret_key |
Yes | <NO_DEFAULT> |
From env HW_SECRET_KEY; never print in chat |
security_token |
Yes | <NO_DEFAULT> |
From env HW_SECURITY_TOKEN; missing token aborts normalize |
* Exactly one of keyword+group (stage A) or solution+variant (stage B) is required per invocation.
Post-Deploy Output
terraform output -json— includes per-template outputs (EIPs, URLs, access instructions)- Service URL pattern:
http://<EIP>[:port]after cloud-init settles (~10 min)
Output Format
All script outputs are JSON-compatible: query_catalog.py stage A writes {keyword, mode: "group", no_match, count, total_solutions, catalog_generated_at, candidates[]} (each with region_count and solution_describe), stage B writes {keyword, mode: "regions", no_match, count, total_solutions, catalog_generated_at, candidates[]} (each with full template_url/deploy_link); list_tf_variables.py prints name=default lines with sensitive masking.
Verification
- Concept search — stage A returns solution+variant rows for business terms (model gateway→New API+LiteLLM etc., microservices→Nacos, speech transcription→VibeVoice via describe); no-match prints full catalog with
no_match: true - Freshness announcement — stage results carry
catalog_generated_at/total_solutionsand the agent announces "Below is the latest SAC solution catalog as of …" before asking the user to pick - Region completeness — stage B returns ALL regions for the picked variant and the row count matches the
region_countreported in stage A for that solution+variant; never truncate - Recap gate — before deploy, the agent restated solution/variant/region/template_url and got explicit confirmation
- Credential gate — env presence checked without printing values; user either exported env vars or manually edited tfvars; skill never read/displayed credentials
- Template download —
.tf/.tf.jsonexists under workdir andterraform validatepasses - Variable confirmation — secrets non-empty, user confirmed overrides
- Deployment —
planclean, user confirmed,state listshows expected resources afterapply - Service reachability — per-template access URL responds after cloud-init
See Verification Method and Acceptance Criteria.
Best Practices
- Always
planbeforeapply; never auto-apply - Prefer env vars over pasted credentials; shortest TTL and least privilege
- Keep one workdir per deployment; do not reuse across regions
- Allow ~10 min post-deploy for cloud-init before curl/ssh checks
Reference Documents
| Document | Description |
|---|---|
| CLI Installation Guide | Install Python, Terraform, skill-quality-cli |
| IAM Policies | Permissions and least-privilege guidance |
| Verification Method | Step-by-step verification per workflow step |
| Acceptance Criteria | Full deployment acceptance checklist |
| Related Commands | Scripts and Terraform quick reference |
| Dataflow Diagram | Mermaid data flow |
Notes
- The solution catalog size is dynamic — never hardcode a count; use
total_solutionsfrom script output <workdir>/terraform.auto.tfvars.jsonis sensitive — never commit to VCS; never read in contextnormalize_tf_providers.pyenforces temporary credentials (missing token aborts)- Tool chain: Python + Terraform only — no KooCLI