Skill: cinience/alicloud-compute-swas-open (version 1.0.2)

--- name: alicloud-compute-swas-open description: Manage Alibaba Cloud Simple Application Server (SWAS OpenAPI 2020-06-01) resources end-to-end. Use for querying instances, starting/stopping/rebooting, executing commands (cloud assistant), managing disks/snapshots/images, firewall rules/templates, key pairs, tags, monitoring, and lightweight database operations. version: 1.0.0 --- Category: service # Simple Application Server (SWAS-OPEN 2020-06-01) Use SWAS-OPEN OpenAPI to manage full SAS resources: instances, disks, snapshots, images, key pairs, firewall, Cloud Assistant, monitoring, tags, and lightweight databases. ## Prerequisites - Prepare AccessKey with least-privilege RAM user/role. - Choose correct region and matching endpoint (public/VPC).`ALICLOUD_REGION_ID` can be used as default region; if unset choose the most reasonable region, ask user if unclear. - This OpenAPI uses RPC signing; prefer Python SDK or OpenAPI Explorer instead of manual signing. ## SDK Priority 1) Python SDK (preferred) 2) OpenAPI Explorer 3) Other SDKs ### Python SDK quick query (instance ID / IP / plan) Virtual environment is recommended (avoid PEP 668 system install restrictions). ```bash python3 -m venv .venv . .venv/bin/activate python -m pip install alibabacloud_swas_open20200601 alibabacloud_tea_openapi alibabacloud_credentials ``` ```python import os from alibabacloud_swas_open20200601.client import Client as SwasClient from alibabacloud_swas_open20200601 import models as swas_models from alibabacloud_tea_openapi import models as open_api_models def create_client(region_id: str) -> SwasClient: config = open_api_models.Config( region_id=region_id, endpoint=f"swas.{region_id}.aliyuncs.com", ) ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") if ak and sk: config.access_key_id = ak config.access_key_secret = sk return SwasClient(config) def list_regions(): client = create_client("cn-hangzhou") resp = client.list_regions(swas_models.ListRegionsRequest()) return [r.region_id for r in resp.body.regions] def list_instances(region_id: str): client = create_client(region_id) resp = client.list_instances(swas_models.ListInstancesRequest(region_id=region_id)) return resp.body.instances def main(): for region_id in list_regions(): for inst in list_instances(region_id): ip = getattr(inst, "public_ip_address", None) or getattr(inst, "inner_ip_address", None) spec = getattr(inst, "plan_name", None) or getattr(inst, "plan_id", None) print(inst.instance_id, ip or "-", spec or "-", region_id) if __name__ == "__main__": main() ``` ### Python SDK scripts (recommended for inventory and summary) - All-region instance inventory (TSV/JSON):`scripts/list_instances_all_regions.py` - Count instances by plan:`scripts/summary_instances_by_plan.py` - Count instances by status:`scripts/summary_instances_by_status.py` - Fix SSH key-based access (custom port supported):`scripts/fix_ssh_access.py` - Get current SSH port of an instance:`scripts/get_ssh_port.py` ## CLI Notes - `aliyun` CLI may not expose `swas-open` as product name; prefer Python SDK. If CLI is mandatory, generate request examples in OpenAPI Explorer first, then migrate to CLI. ## Workflow 1) Confirm resource type and region (instance/disk/snapshot/image/firewall/command/database/tag). 2) Identify API group and operation in `references/api_overview.md`. 3) Choose invocation method (Python SDK / OpenAPI Explorer / other SDK). 4) After mutations, verify state/results with query APIs. ## Common Operation Map - Instance query/start/stop/reboot:`ListInstances`、`StartInstance(s)`、`StopInstance(s)`、`RebootInstance(s)` - Command execution:`RunCommand` or `CreateCommand` + `InvokeCommand`; use `DescribeInvocations`/`DescribeInvocationResult` - Firewall:`ListFirewallRules`/`CreateFirewallRule(s)`/`ModifyFirewallRule`/`EnableFirewallRule`/`DisableFirewallRule` - Snapshot/disk/image:`CreateSnapshot`、`ResetDisk`、`CreateCustomImage` etc. ## Cloud Assistant Execution Notes - Target instance must be in Running state. - Cloud Assistant agent must be installed (use `InstallCloudAssistant`). - For PowerShell commands, ensure required modules are available on Windows instances. - After execution, use `DescribeInvocations` or `DescribeInvocationResult` to fetch status and outputs. See `references/command-assistant.md` for details. ## Clarifying questions (ask when uncertain) 1. What is the target region? Is VPC endpoint required? 2. What are target instance IDs? Are they currently Running? 3. What command/script type/timeout is needed? Linux or Windows? 4. Do you need batch execution or scheduled execution? ## Output Policy If you need to save results or responses, write to: `output/compute-swas-open/` ## Validation ```bash mkdir -p output/alicloud-compute-swas-open for f in skills/compute/swas/alicloud-compute-swas-open/scripts/*.py; do python3 -m py_compile "$f" done echo "py_compile_ok" > output/alicloud-compute-swas-open/validate.txt ``` Pass criteria: command exits 0 and `output/alicloud-compute-swas-open/validate.txt` is generated. ## Output And Evidence - Save artifacts, command outputs, and API response summaries under [REDACTED_SECRET]. - Include key parameters (region/resource id/time range) in evidence files for reproducibility. ## Prerequisites - Configure least-privilege Alibaba Cloud credentials before execution. - Prefer environment variables: `ALICLOUD_ACCESS_KEY_ID`, `ALICLOUD_ACCESS_KEY_SECRET`, optional `ALICLOUD_REGION_ID`. - If region is unclear, ask the user before running mutating operations. ## Workflow 1) Confirm user intent, region, identifiers, and whether the operation is read-only or mutating. 2) Run one minimal read-only query first to verify connectivity and permissions. 3) Execute the target operation with explicit parameters and bounded scope. 4) Verify results and save output/evidence files. ## References - API overview and operation groups:`references/api_overview.md` - Endpoints and integration:`references/endpoints.md` - Cloud Assistant highlights:`references/command-assistant.md` - Official source list:`references/sources.md`

path: agents/openai.yaml
content: interface:
  display_name: "Alibaba Cloud Compute SWAS Open"
  short_description: "Simple app server operations workflows"
  default_prompt: "Use $alicloud-compute-swas-open to complete this compute/swas task on Alibaba Cloud."


path: references/api_overview.md
content: API 概览（SWAS-OPEN 2020-06-01）
================================================

来源：阿里云帮助文档「API概览」。用于快速定位接口分组与操作名。详情与参数以原文档为准。

目录
----
- 实例
- 密钥对
- 防火墙模板
- 防火墙
- 快照
- 磁盘
- 自定义镜像
- 命令助手
- 轻量数据库服务
- 标签
- 其他资源

实例
----
- CreateInstances: 创建实例
- StartInstance / StartInstances: 启动实例（单/批量）
- ListInstanceStatus: 批量获取实例状态
- StopInstance / StopInstances: 停止实例（单/批量）
- UpdateInstanceAttribute: 修改实例部分信息（名称/密码）
- ListInstances: 获取实例列表
- LoginInstance: 远程登录实例（Workbench）
- DescribeInstanceVncUrl: 获取实例 VNC 连接地址
- ModifyInstanceVncPassword: 修改实例 VNC 密码
- ListInstancePlansModification: 获取可升级套餐列表
- ListInstancesTrafficPackages: 获取流量包使用情况
- DescribeInstancePasswordsSetting: 查询是否设置过密码
- InstallCloudMonitorAgent: 安装云监控插件
- DescribeCloudMonitorAgentStatuses: 查询云监控插件状态
- DescribeMonitorData: 获取实例监控数据
- DescribeSecurityAgentStatus: 查询安全中心 Agent 状态
- RebootInstance / RebootInstances: 重启实例（单/批量）
- UpgradeInstance: 升级实例套餐
- RenewInstance: 续费实例
- ResetSystem: 重置系统

密钥对
------
- CreateInstanceKeyPair / CreateKeyPair: 创建密钥对
- UploadInstanceKeyPair / ImportKeyPair: 导入密钥对
- DescribeInstanceKeyPair: 查询实例密钥对信息
- ListKeyPairs: 查询密钥对列表
- AttachKeyPair / DetachKeyPair: 绑定/解绑密钥对
- DeleteInstanceKeyPair / DeleteKeyPairs: 删除密钥对

防火墙模板
----------
- CreateFirewallTemplate: 创建防火墙模板
- DescribeFirewallTemplates: 查询防火墙模板
- CreateFirewallTemplateRules: 创建防火墙模板规则
- ApplyFirewallTemplate: 应用防火墙模板
- ModifyFirewallTemplate: 修改防火墙模板
- DescribeFirewallTemplateApplyResults: 查询模板应用结果
- DescribeFirewallTemplateRulesApplyResult: 查询模板规则应用结果
- DeleteFirewallTemplateRules: 删除防火墙模板规则
- DeleteFirewallTemplates: 删除防火墙模板

防火墙
------
- DeleteFirewallRules / DeleteFirewallRule: 批量/单条删除防火墙规则
- CreateFirewallRule / CreateFirewallRules: 创建防火墙规则（单/批量）
- ListFirewallRules: 查询实例防火墙规则
- ModifyFirewallRule: 修改防火墙规则
- EnableFirewallRule / DisableFirewallRule: 启用/禁用防火墙规则

快照
----
- CreateSnapshot: 创建快照
- ListSnapshots: 获取快照
- UpdateSnapshotAttribute: 修改快照备注
- DeleteSnapshot / DeleteSnapshots: 删除快照（单/批量）

磁盘
----
- UpdateDiskAttribute: 修改数据盘备注
- ListDisks: 查询磁盘信息
- ResetDisk: 回滚磁盘

自定义镜像
----------
- CreateCustomImage: 创建自定义镜像
- ListCustomImages: 查询自定义镜像
- ModifyImageShareStatus: 共享/解除共享自定义镜像
- AddCustomImageShareAccount: 跨账号共享自定义镜像
- ListCustomImageShareAccounts: 查看跨账号共享信息
- RemoveCustomImageShareAccount: 取消跨账号共享
- DeleteCustomImage / DeleteCustomImages: 删除自定义镜像（单/批量）

命令助手
--------
- DescribeCloudAssistantAttributes: 查询命令助手信息
- UpdateCommandAttribute: 修改命令
- InvokeCommand: 执行命令（通过已有命令）
- DescribeCommands: 查询命令
- DescribeCommandInvocations: 查询命令执行列表与状态
- DeleteCommand: 删除命令
- CreateCommand: 创建命令
- StartTerminalSession: 开启免密登录会话
- InstallCloudAssistant: 安装云助手
- DescribeCloudAssistantStatus: 查询云助手安装状态
- DescribeInvocationResult: 查询单个命令执行结果
- RunCommand: 执行命令（直接下发）
- DescribeInvocations: 查看命令详细信息

轻量数据库服务
--------------
- ModifyDatabaseInstanceParameter: 修改数据库参数
- StopDatabaseInstance / StartDatabaseInstance: 停止/启动数据库实例
- ModifyDatabaseInstanceDescription: 修改数据库实例描述
- DescribeDatabaseSlowLogRecords: 查询慢日志明细
- DescribeDatabaseErrorLogs: 查询错误日志
- DescribeDatabaseInstanceMetricData: 查询数据库监控
- DescribeDatabaseInstanceParameters: 查看数据库参数
- DescribeDatabaseInstances: 查看数据库实例信息
- AllocatePublicConnection: 申请公网访问地址
- RestartDatabaseInstance: 重启数据库实例
- ResetDatabaseAccountPassword: 重置数据库账号密码
- ReleasePublicConnection: 释放外网访问地址

标签
----
- TagResources: 绑定标签
- ListTagResources: 查询标签列表
- UntagResources: 解绑并删除标签

其他资源
--------
- ListRegions: 查询可用地域列表
- ListImages: 获取镜像列表
- ListPlans: 获取套餐信息


path: references/command-assistant.md
content: 命令助手（Command/Invoke/Run）
=============================

来源：阿里云帮助文档 `InvokeCommand` 等接口说明。

关键点
------
- 目标实例必须处于 Running 状态。
- 实例需安装云助手 Agent（可用 `InstallCloudAssistant` 安装）。
- Windows 实例执行 PowerShell 需保证 PowerShell 模块可用。

常用接口
--------
- CreateCommand: 创建命令模板
- InvokeCommand: 在实例上执行已创建命令
- RunCommand: 直接下发命令执行（无需先创建命令）
- DescribeInvocations / DescribeInvocationResult: 查询执行结果与状态

常见参数提示
------------
- CommandType: `RunShellScript`（Linux）或 `RunPowerShellScript`（Windows）
- InstanceIds: 目标实例 ID 列表（JSON 数组）
- Timeout: 命令超时时间
- WorkingDir: 运行目录（可选）

最佳实践
--------
- 批量执行时优先使用 `InvokeCommand` 并轮询 `DescribeInvocations` 获取状态。
- 需要可复用/可审计时先 `CreateCommand`，否则直接 `RunCommand` 更快捷。


path: references/endpoints.md
content: 接入点（Endpoints）
==================

来源：阿里云帮助文档「服务接入点」。

说明
----
- 公网接入点格式：`swas.<region-id>.aliyuncs.com`
- VPC 接入点格式：`swas-vpc.<region-id>.aliyuncs.com`
- 选错 Region 或接入点会导致调用失败。

示例
----
- 青岛（公）：`swas.cn-qingdao.aliyuncs.com`
- 青岛（VPC）：`swas-vpc.cn-qingdao.aliyuncs.com`

地域与公网接入点（节选）
----------------------
- 华东1（杭州）：`swas.cn-hangzhou.aliyuncs.com`
- 华东2（上海）：`swas.cn-shanghai.aliyuncs.com`
- 华北1（青岛）：`swas.cn-qingdao.aliyuncs.com`
- 华北2（北京）：`swas.cn-beijing.aliyuncs.com`
- 华北3（张家口）：`swas.cn-zhangjiakou.aliyuncs.com`
- 华北5（呼和浩特）：`swas.cn-huhehaote.aliyuncs.com`
- 华北6（乌兰察布）：`swas.cn-wulanchabu.aliyuncs.com`
- 华南1（深圳）：`swas.cn-shenzhen.aliyuncs.com`
- 华南2（河源）：`swas.cn-heyuan.aliyuncs.com`
- 华南3（广州）：`swas.cn-guangzhou.aliyuncs.com`
- 西南1（成都）：`swas.cn-chengdu.aliyuncs.com`
- 西南2（重庆）：`swas.cn-chongqing.aliyuncs.com`
- 华中1（武汉-本地地域）：`swas.cn-wuhan-lr.aliyuncs.com`
- 中国（香港）：`swas.cn-hongkong.aliyuncs.com`
- 亚太东南1（新加坡）：`swas.ap-southeast-1.aliyuncs.com`
- 亚太东南2（悉尼）：`swas.ap-southeast-2.aliyuncs.com`
- 亚太东南3（吉隆坡）：`swas.ap-southeast-3.aliyuncs.com`
- 亚太东南5（雅加达）：`swas.ap-southeast-5.aliyuncs.com`
- 亚太东南6（马尼拉）：`swas.ap-southeast-6.aliyuncs.com`
- 亚太东北1（日本）：`swas.ap-northeast-1.aliyuncs.com`
- 亚太东北2（韩国）：`swas.ap-northeast-2.aliyuncs.com`
- 亚太南部1（孟买）：`swas.ap-south-1.aliyuncs.com`
- 美国（弗吉尼亚）：`swas.us-east-1.aliyuncs.com`
- 美国（硅谷）：`swas.us-west-1.aliyuncs.com`
- 英国（伦敦）：`swas.eu-west-1.aliyuncs.com`
- 中东东部1（迪拜）：`swas.me-east-1.aliyuncs.com`
- 欧洲中部1（法兰克福）：`swas.eu-central-1.aliyuncs.com`
- 俄罗斯（莫斯科）：`swas.ru-central-1.aliyuncs.com`

更多地域与 VPC 接入点以官方文档为准。


path: references/sources.md
content: 官方文档来源（用于后续更新）
============================

- https://help.aliyun.com/zh/simple-application-server/developer-reference/api-swas-open-2020-06-01-invokecommand
- https://help.aliyun.com/zh/simple-application-server/developer-reference/api-swas-open-2020-06-01-overview
- https://help.aliyun.com/zh/simple-application-server/developer-reference/service-endpoints


path: scripts/fix_ssh_access.py
content: #!/usr/bin/env python3
"""Fix SSH access for SWAS instances.

- Ensures authorized_keys contains the provided public key
- Ensures PermitRootLogin and PubkeyAuthentication are enabled
- Restarts ssh service
- Optionally sets Port in sshd_config

This script uses SWAS RunCommand to execute on the instance.
"""

from __future__ import annotations

import argparse
import os
from alibabacloud_swas_open20200601.client import Client as SwasClient
from alibabacloud_swas_open20200601 import models as swas_models
from alibabacloud_tea_openapi import models as open_api_models


def create_client(region_id: str) -> SwasClient:
    config = open_api_models.Config(
        region_id=region_id,
        endpoint=f"swas.{region_id}.aliyuncs.com",
    )
    ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
    sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    [REDACTED_SECRET]"ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
    if ak and sk:
        config.access_key_id = ak
        config.access_key_secret = sk
        if [REDACTED_SECRET] = token
    return SwasClient(config)


def build_script(pub_key: str, user: str, port: str | None) -> str:
    port_block = ""
    if port:
        port_block = f"""
if ! grep -q '^Port' $SSHD_CONFIG; then
  echo 'Port {port}' >> $SSHD_CONFIG
else
  sed -i 's/^Port.*/Port {port}/' $SSHD_CONFIG
fi
"""

    return f"""#!/bin/bash
set -e
USER_NAME="{user}"
HOME_DIR=$(getent passwd "$USER_NAME" | cut -d: -f6)
if [ -z "$HOME_DIR" ]; then
  echo "User $USER_NAME not found"
  exit 1
fi

mkdir -p "$HOME_DIR/.ssh"
chmod 700 "$HOME_DIR/.ssh"
if ! grep -qF '{pub_key}' "$HOME_DIR/.ssh/authorized_keys" 2>/dev/null; then
  echo '{pub_key}' >> "$HOME_DIR/.ssh/authorized_keys"
fi
chmod 600 "$HOME_DIR/.ssh/authorized_keys"
chown -R "$USER_NAME":"$USER_NAME" "$HOME_DIR/.ssh"

SSHD_CONFIG=/etc/ssh/sshd_config
if ! grep -q '^PermitRootLogin' $SSHD_CONFIG; then
  echo 'PermitRootLogin yes' >> $SSHD_CONFIG
else
  sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' $SSHD_CONFIG
fi
if ! grep -q '^PubkeyAuthentication' $SSHD_CONFIG; then
  echo 'PubkeyAuthentication yes' >> $SSHD_CONFIG
else
  sed -i 's/^PubkeyAuthentication.*/PubkeyAuthentication yes/' $SSHD_CONFIG
fi
{port_block}

if systemctl list-unit-files | grep -q '^ssh\\.service'; then
  systemctl restart ssh
elif systemctl list-unit-files | grep -q '^sshd\\.service'; then
  systemctl restart sshd
elif command -v service >/dev/null 2>&1; then
  service ssh restart || service sshd restart
elif [ -x /etc/init.d/ssh ]; then
  /etc/init.d/ssh restart
elif [ -x /etc/init.d/sshd ]; then
  /etc/init.d/sshd restart
else
  echo "No ssh service found"
  exit 1
fi
"""


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--region", required=True)
    parser.add_argument("--instance-id", required=True)
    parser.add_argument("--user", default="root")
    parser.add_argument("--port", help="Set SSH port in sshd_config")
    parser.add_argument("--pubkey", default="~/.ssh/id_ed25519.pub")
    args = parser.parse_args()

    pubkey_path = os.path.expanduser(args.pubkey)
    with open(pubkey_path, "r", encoding="utf-8") as f:
        pub_key = f.read().strip()

    script = build_script(pub_key, args.user, args.port)
    client = create_client(args.region)
    resp = client.run_command(swas_models.RunCommandRequest(
        region_id=args.region,
        instance_id=args.instance_id,
        name="fix-ssh-access",
        type="RunShellScript",
        command_content=script,
    ))
    print("InvokeId:", resp.body.invoke_id)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: scripts/get_ssh_port.py
content: #!/usr/bin/env python3
"""Get SSH port from sshd_config via SWAS RunCommand."""

from __future__ import annotations

import argparse
import os
from alibabacloud_swas_open20200601.client import Client as SwasClient
from alibabacloud_swas_open20200601 import models as swas_models
from alibabacloud_tea_openapi import models as open_api_models


def create_client(region_id: str) -> SwasClient:
    config = open_api_models.Config(
        region_id=region_id,
        endpoint=f"swas.{region_id}.aliyuncs.com",
    )
    ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
    sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    [REDACTED_SECRET]"ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
    if ak and sk:
        config.access_key_id = ak
        config.access_key_secret = sk
        if [REDACTED_SECRET] = token
    return SwasClient(config)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--region", required=True)
    parser.add_argument("--instance-id", required=True)
    args = parser.parse_args()

    script = """#!/bin/bash
set -e
PORT=$(grep -E '^Port ' /etc/ssh/sshd_config | tail -n 1 | awk '{print $2}')
if [ -z "$PORT" ]; then
  PORT=22
fi
echo $PORT
"""

    client = create_client(args.region)
    resp = client.run_command(swas_models.RunCommandRequest(
        region_id=args.region,
        instance_id=args.instance_id,
        name="get-ssh-port",
        type="RunShellScript",
        command_content=script,
    ))
    print("InvokeId:", resp.body.invoke_id)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: scripts/list_instances_all_regions.py
content: #!/usr/bin/env python3
"""List SWAS instances across all regions.

Outputs TSV by default. Use --json for JSON output.
"""

from __future__ import annotations

import argparse
import json
import os
import sys

from alibabacloud_swas_open20200601.client import Client as SwasClient
from alibabacloud_swas_open20200601 import models as swas_models
from alibabacloud_tea_openapi import models as open_api_models


def create_client(region_id: str) -> SwasClient:
    config = open_api_models.Config(
        region_id=region_id,
        endpoint=f"swas.{region_id}.aliyuncs.com",
    )
    ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
    sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    [REDACTED_SECRET]"ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
    if ak and sk:
        config.access_key_id = ak
        config.access_key_secret = sk
        if [REDACTED_SECRET] = token
    return SwasClient(config)


def list_regions() -> list[str]:
    client = create_client("cn-hangzhou")
    resp = client.list_regions(swas_models.ListRegionsRequest())
    return [r.region_id for r in resp.body.regions]


def list_instances(region_id: str):
    client = create_client(region_id)
    resp = client.list_instances(swas_models.ListInstancesRequest(region_id=region_id))
    return resp.body.instances or []


def to_record(region_id: str, inst) -> dict:
    return {
        "region_id": region_id,
        "instance_id": inst.instance_id,
        "instance_name": getattr(inst, "instance_name", None),
        "status": getattr(inst, "status", None),
        "public_ip": getattr(inst, "public_ip_address", None),
        "inner_ip": getattr(inst, "inner_ip_address", None),
        "plan_id": getattr(inst, "plan_id", None),
        "plan_name": getattr(inst, "plan_name", None),
        "cpu": getattr(inst, "cpu", None),
        "memory_gib": getattr(inst, "memory", None),
        "zone_id": getattr(inst, "zone_id", None),
    }


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--json", action="store_true", help="Output JSON array")
    parser.add_argument("--output", help="Write output to file")
    args = parser.parse_args()

    records = []
    failed_regions: list[tuple[str, str]] = []
    for region_id in list_regions():
        try:
            instances = list_instances(region_id)
        except Exception as exc:
            failed_regions.append((region_id, str(exc)))
            print(f"Warning: failed to query region {region_id}: {exc}", file=sys.stderr)
            continue
        for inst in instances:
            records.append(to_record(region_id, inst))

    if args.json:
        output = json.dumps(records, ensure_ascii=False, indent=2)
    else:
        lines = [
            "region_id\tinstance_id\tinstance_name\tstatus\tpublic_ip\tinner_ip\tplan_name\tplan_id\tcpu\tmemory_gib\tzone_id"
        ]
        for r in records:
            lines.append(
                "\t".join(
                    [
                        str(r.get("region_id") or ""),
                        str(r.get("instance_id") or ""),
                        str(r.get("instance_name") or ""),
                        str(r.get("status") or ""),
                        str(r.get("public_ip") or ""),
                        str(r.get("inner_ip") or ""),
                        str(r.get("plan_name") or ""),
                        str(r.get("plan_id") or ""),
                        str(r.get("cpu") or ""),
                        str(r.get("memory_gib") or ""),
                        str(r.get("zone_id") or ""),
                    ]
                )
            )
        output = "\n".join(lines)

    if args.output:
        with open(args.output, "w", encoding="utf-8") as f:
            f.write(output)
    else:
        print(output)

    if failed_regions:
        print("\nFailed regions:", file=sys.stderr)
        for region_id, err in failed_regions:
            print(f"- {region_id}: {err}", file=sys.stderr)

    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: scripts/summary_instances_by_plan.py
content: #!/usr/bin/env python3
"""Summarize SWAS instances by plan across all regions."""

from __future__ import annotations

import argparse
import os
from collections import Counter

from alibabacloud_swas_open20200601.client import Client as SwasClient
from alibabacloud_swas_open20200601 import models as swas_models
from alibabacloud_tea_openapi import models as open_api_models


def create_client(region_id: str) -> SwasClient:
    config = open_api_models.Config(
        region_id=region_id,
        endpoint=f"swas.{region_id}.aliyuncs.com",
    )
    ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
    sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    [REDACTED_SECRET]"ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
    if ak and sk:
        config.access_key_id = ak
        config.access_key_secret = sk
        if [REDACTED_SECRET] = token
    return SwasClient(config)


def list_regions() -> list[str]:
    client = create_client("cn-hangzhou")
    resp = client.list_regions(swas_models.ListRegionsRequest())
    return [r.region_id for r in resp.body.regions]


def list_instances(region_id: str):
    client = create_client(region_id)
    resp = client.list_instances(swas_models.ListInstancesRequest(region_id=region_id))
    return resp.body.instances or []


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--output", help="Write output to file")
    args = parser.parse_args()

    counter: Counter[str] = Counter()
    for region_id in list_regions():
        for inst in list_instances(region_id):
            plan = getattr(inst, "plan_name", None) or getattr(inst, "plan_id", None) or "(unknown)"
            counter[plan] += 1

    lines = ["plan\tcount"]
    for plan, cnt in sorted(counter.items()):
        lines.append(f"{plan}\t{cnt}")

    output = "\n".join(lines) if len(lines) > 1 else "No SWAS instances found."
    if args.output:
        with open(args.output, "w", encoding="utf-8") as f:
            f.write(output)
    else:
        print(output)

    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: scripts/summary_instances_by_status.py
content: #!/usr/bin/env python3
"""Summarize SWAS instances by status across all regions."""

from __future__ import annotations

import argparse
import os
from collections import Counter

from alibabacloud_swas_open20200601.client import Client as SwasClient
from alibabacloud_swas_open20200601 import models as swas_models
from alibabacloud_tea_openapi import models as open_api_models


def create_client(region_id: str) -> SwasClient:
    config = open_api_models.Config(
        region_id=region_id,
        endpoint=f"swas.{region_id}.aliyuncs.com",
    )
    ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
    sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    [REDACTED_SECRET]"ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
    if ak and sk:
        config.access_key_id = ak
        config.access_key_secret = sk
        if [REDACTED_SECRET] = token
    return SwasClient(config)


def list_regions() -> list[str]:
    client = create_client("cn-hangzhou")
    resp = client.list_regions(swas_models.ListRegionsRequest())
    return [r.region_id for r in resp.body.regions]


def list_instances(region_id: str):
    client = create_client(region_id)
    resp = client.list_instances(swas_models.ListInstancesRequest(region_id=region_id))
    return resp.body.instances or []


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--output", help="Write output to file")
    args = parser.parse_args()

    counter: Counter[str] = Counter()
    for region_id in list_regions():
        for inst in list_instances(region_id):
            status = getattr(inst, "status", None) or "(unknown)"
            counter[status] += 1

    lines = ["status\tcount"]
    for status, cnt in sorted(counter.items()):
        lines.append(f"{status}\t{cnt}")

    output = "\n".join(lines) if len(lines) > 1 else "No SWAS instances found."
    if args.output:
        with open(args.output, "w", encoding="utf-8") as f:
            f.write(output)
    else:
        print(output)

    return 0


if __name__ == "__main__":
    raise SystemExit(main())


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)