Skill: leo-jiqimao/aliyun-ecs-skill (version 1.0.0)

--- name: aliyun-ecs-skill description: Manage Alibaba Cloud ECS (Elastic Compute Service) — query instances, monitoring, firewall, snapshots, remote execution. Use when user asks about ECS, 阿里云服务器, or Alibaba Cloud. NOT for Tencent Cloud or other cloud providers. metadata: { "openclaw": { "emoji": "☁️", "requires": {}, "install": [ { "id": "node-aliyun-sdk", "kind": "node", "package": "@alicloud/openapi-client", "label": "Install AliCloud SDK", }, ], }, } --- # Aliyun ECS 云服务器运维 管理阿里云ECS（弹性计算服务）实例。 ## 首次使用 — 自动设置 当用户首次要求管理阿里云服务器时，按以下流程操作： ### 步骤 1：检查当前状态 ```bash {baseDir}/scripts/setup.sh --check-only ``` 如果输出显示一切 OK（SDK 已安装、config 已配置、ECS 已就绪），跳到「调用格式」。 ### 步骤 2：如果未配置，引导用户提供密钥 告诉用户： > 我需要你的阿里云 API 密钥来连接 ECS 服务器。请提供： > 1. **AccessKey ID** — 阿里云 API 密钥 ID > 2. **AccessKey Secret** — 阿里云 API 密钥 Secret > > 你可以在 [阿里云控制台 > 访问控制 > AccessKey管理](https://ram.console.aliyun.com/manage/ak) 获取。 > > ⚠️ 建议使用子账号，只授予ECS相关权限（ecs:*） ### 步骤 3：用户提供密钥后，运行自动设置 ```bash {baseDir}/scripts/setup.sh --access-key-id "<用户提供的AccessKeyId>" --access-key-secret "<用户提供的AccessKeySecret>" ``` 脚本会自动： - 检查并安装阿里云SDK（如未安装） - 创建 `~/.aliyun/config.json` 配置文件 - 写入 ECS 配置和密钥 - 验证连接 设置完成后即可开始使用。 ## 调用格式 所有命令使用以下格式： ``` aliyun-ecs <command> [options] ``` 或直接使用 Node.js 脚本： ```bash node {baseDir}/src/index.js <command> [options] ``` ## 工具总览 | 类别 | 说明 | |------|------| | 地域查询 | 获取可用地域列表 | | 实例管理 | 查询、启动、停止、重启实例 | | 监控与告警 | 获取多指标监控数据 | | 快照管理 | 创建、删除、回滚快照 | | 安全组 | 规则增删改查 | | 远程命令 | 在实例上执行命令 | ## 常用操作 ### 获取地域列表 ```bash aliyun-ecs regions ``` ### 实例管理 ```bash # 查询实例列表 aliyun-ecs list --region cn-hangzhou # 查询指定实例 aliyun-ecs info --region cn-hangzhou --id i-xxxxxxxxxx # 启动实例 aliyun-ecs start --region cn-hangzhou --id i-xxxxxxxxxx # 停止实例 aliyun-ecs stop --region cn-hangzhou --id i-xxxxxxxxxx # 重启实例 aliyun-ecs restart --region cn-hangzhou --id i-xxxxxxxxxx ``` ### 监控与告警 ```bash # 获取监控数据（CPU、内存、网络） aliyun-ecs monitor --region cn-hangzhou --id i-xxxxxxxxxx --metrics CPUUtilization,MemoryUtilization # 支持的监控指标: # CPUUtilization - CPU使用率 # MemoryUtilization - 内存使用率 # InternetInRate - 公网入带宽 # InternetOutRate - 公网出带宽 # DiskReadIOPS - 磁盘读IOPS # DiskWriteIOPS - 磁盘写IOPS ``` ### 快照管理 ```bash # 创建快照 aliyun-ecs snapshot create --region cn-hangzhou --id i-xxxxxxxxxx --name "backup-20260312" # 列出快照 aliyun-ecs snapshot list --region cn-hangzhou --id i-xxxxxxxxxx # 删除快照 aliyun-ecs snapshot delete --region cn-hangzhou --snapshot-id s-xxxxxxxxxx # 回滚快照 aliyun-ecs snapshot rollback --region cn-hangzhou --id i-xxxxxxxxxx --snapshot-id s-xxxxxxxxxx ``` ### 安全组（防火墙） ```bash # 查询安全组规则 aliyun-ecs security-group list --region cn-hangzhou --group-id sg-xxxxxxxxxx # 添加安全组规则（开放80端口） aliyun-ecs security-group add --region cn-hangzhou --group-id sg-xxxxxxxxxx --port 80 --protocol tcp # 删除安全组规则 aliyun-ecs security-group remove --region cn-hangzhou --group-id sg-xxxxxxxxxx --port 80 --protocol tcp ``` ### 远程命令执行 (云助手) ```bash # 在 Linux 实例上执行命令 aliyun-ecs exec --region cn-hangzhou --id i-xxxxxxxxxx --command "uptime && df -h && free -m" # 在 Windows 实例上执行命令 aliyun-ecs exec --region cn-hangzhou --id i-xxxxxxxxxx --command "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10" --windows ``` ## 使用规范 1. **Region 参数规则**: 除 `regions` 外，所有操作都**必须**传入 `--region` 参数 2. **首次使用流程**: 先调用 `regions` 获取地域列表 → 再调用 `list` 获取实例列表 → 记住 InstanceId 和 Region 供后续使用 3. **危险操作前先确认**: 安全组修改、实例停止/重启、快照回滚等，先向用户确认 4. **错误处理**: 如果调用失败，先用 `setup.sh --check-only` 诊断问题 5. **建议子账号**: 为了安全，建议用户使用子账号AccessKey，只授予ECS相关权限

path: README.md
content: # Aliyun ECS Skill for OpenClaw

阿里云ECS（弹性计算服务）管理技能，让你通过OpenClaw用自然语言管理阿里云服务器。

## 功能特性

- ✅ **实例管理** - 查询、启动、停止、重启ECS实例
- ✅ **监控查询** - 查看CPU、内存、网络等监控指标
- ✅ **快照管理** - 创建、查看、回滚磁盘快照
- ✅ **安全组** - 管理防火墙规则
- 🔄 **远程命令** - 通过云助手在实例上执行命令（开发中）

## 安装

```bash
# 进入skill目录
cd ~/.openclaw/skills/aliyun-ecs-skill

# 安装依赖
npm install

# 配置阿里云密钥
./scripts/setup.sh --access-key-id YOUR_ACCESS_KEY_ID --access-key-secret YOUR_ACCESS_KEY_SECRET
```

## 使用方法

### 查询地域列表
```bash
aliyun-ecs regions
```

### 查询实例列表
```bash
aliyun-ecs list --region cn-hangzhou
```

### 启动/停止/重启实例
```bash
aliyun-ecs start --region cn-hangzhou --id i-bp67acfmxazb4p****
aliyun-ecs stop --region cn-hangzhou --id i-bp67acfmxazb4p****
aliyun-ecs restart --region cn-hangzhou --id i-bp67acfmxazb4p****
```

### 创建快照
```bash
aliyun-ecs snapshot create --region cn-hangzhou --disk-id d-bp67acfmxazb4p**** --name "backup-20260312"
```

### 管理安全组
```bash
# 查看安全组列表
aliyun-ecs security-group list --region cn-hangzhou

# 添加规则（开放80端口）
aliyun-ecs security-group add --region cn-hangzhou --group-id sg-bp67acfmxazb4p**** --port 80

# 删除规则
aliyun-ecs security-group remove --region cn-hangzhou --group-id sg-bp67acfmxazb4p**** --port 80
```

## 与腾讯云Lighthouse的区别

| 对比项 | 阿里云ECS | 腾讯云Lighthouse |
|--------|-----------|------------------|
| 定位 | 企业级弹性计算 | 轻量应用服务器 |
| 目标用户 | 中大型企业、开发者 | 个人开发者、小企业 |
| 计费方式 | 包年包月/按量付费 | 套餐包（更便宜） |
| 功能丰富度 | 更丰富（SLB、VPC等） | 简洁够用 |
| 市场占有率 | ~35-40%（第一） | ~15-18%（第三） |

## 典型使用场景

### 场景1: 快速部署测试环境
```
"帮我创建一台杭州区域的2核4G服务器"
→ 选择镜像 → 配置安全组 → 启动实例
```

### 场景2: 大促前扩容
```
"双11快到了，给所有生产服务器做快照备份"
→ 批量创建快照
```

### 场景3: 安全加固
```
"检查所有服务器的安全组，只开放必要的端口"
→ 列出规则 → 识别风险 → 清理多余规则
```

## 配置说明

配置文件位于 `~/.aliyun/config.json`：

```json
{
  "accessKeyId": "YOUR_ACCESS_KEY_ID",
  "accessKeySecret": "YOUR_ACCESS_KEY_SECRET",
  "defaultRegion": "cn-hangzhou",
  "endpoint": "ecs.aliyuncs.com"
}
```

**安全建议**: 使用RAM子账号，只授予ECS相关权限（`ecs:*`）

## 开发计划

- [x] 基础实例管理
- [x] 快照管理
- [x] 安全组管理
- [ ] 监控告警
- [ ] 远程命令执行（云助手）
- [ ] 自动伸缩

## 贡献

欢迎提交PR！请确保：
1. 代码通过ESLint检查
2. 添加必要的测试
3. 更新文档

## 许可

MIT

## 作者

Leo & AI Agent

---

**关联**: 本项目与 [tencentcloud-lighthouse-skill](https://clawhub.ai/skills/tencentcloud-lighthouse-skill) 形成互补，共同覆盖中国主流云服务商。


path: _meta.json
content: ownerId: leo-and-ai-agent
slug: aliyun-ecs-skill
version: 1.0.0

path: package.json
content: {
  "name": "aliyun-ecs-skill",
  "version": "1.0.0",
  "description": "Aliyun ECS management skill for OpenClaw",
  "main": "src/index.js",
  "bin": {
    "aliyun-ecs": "./src/index.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "openclaw",
    "aliyun",
    "ecs",
    "cloud",
    "skill"
  ],
  "author": "Leo & AI Agent",
  "license": "MIT",
  "dependencies": {
    "@alicloud/openapi-client": "^0.4.10",
    "@alicloud/ecs20140526": "^7.0.0"
  }
}


path: scripts/setup.sh
content: #!/bin/bash
# setup.sh — 阿里云ECS Skill 自动设置脚本

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_DIR="$HOME/.aliyun"
CONFIG_FILE="$CONFIG_DIR/config.json"

# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 检查Node.js
node_check() {
    if ! command -v node &> /dev/null; then
        echo -e "${RED}✗ Node.js 未安装${NC}"
        return 1
    fi
    
    NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1)
    if [ "$NODE_VERSION" -lt 16 ]; then
        echo -e "${RED}✗ Node.js 版本过低: $(node --version)，需要 >= 16${NC}"
        return 1
    fi
    
    echo -e "${GREEN}✓ Node.js $(node --version)${NC}"
    return 0
}

# 检查阿里云SDK
sdk_check() {
    if npm list -g @alicloud/openapi-client &> /dev/null; then
        echo -e "${GREEN}✓ 阿里云SDK已安装${NC}"
        return 0
    fi
    
    if npm list @alicloud/openapi-client &> /dev/null 2>&1; then
        echo -e "${GREEN}✓ 阿里云SDK已安装（本地）${NC}"
        return 0
    fi
    
    echo -e "${YELLOW}⚠ 阿里云SDK未安装${NC}"
    return 1
}

# 安装阿里云SDK
sdk_install() {
    echo "正在安装阿里云SDK..."
    cd "$SCRIPT_DIR/.."
    
    # 创建package.json如果不存在
    if [ ! -f package.json ]; then
        cat > package.json << 'EOF'
{
  "name": "aliyun-ecs-skill",
  "version": "1.0.0",
  "description": "Aliyun ECS management skill for OpenClaw",
  "main": "src/index.js",
  "dependencies": {
    "@alicloud/openapi-client": "^0.4.10",
    "@alicloud/ecs20140526": "^7.0.0"
  }
}
EOF
    fi
    
    npm install
    echo -e "${GREEN}✓ 阿里云SDK安装完成${NC}"
}

# 检查配置文件
config_check() {
    if [ -f "$CONFIG_FILE" ]; then
        if grep -q "accessKeyId" "$CONFIG_FILE" 2>/dev/null; then
            echo -e "${GREEN}✓ 配置文件已存在${NC}"
            return 0
        fi
    fi
    
    echo -e "${YELLOW}⚠ 配置文件不存在或无效${NC}"
    return 1
}

# 创建配置文件
config_create() {
    local access_key_id="$1"
    local access_key_secret="$2"
    
    mkdir -p "$CONFIG_DIR"
    
    cat > "$CONFIG_FILE" << EOF
{
  "accessKeyId": "$access_key_id",
  "accessKeySecret": "$access_key_secret",
  "defaultRegion": "cn-hangzhou",
  "endpoint": "ecs.aliyuncs.com"
}
EOF
    
    chmod 600 "$CONFIG_FILE"
    echo -e "${GREEN}✓ 配置文件创建完成 ($CONFIG_FILE)${NC}"
}

# 验证连接
test_connection() {
    echo "正在验证阿里云连接..."
    
    cd "$SCRIPT_DIR/.."
    
    # 使用实际的阿里云API调用来验证连接
    node -e "
    const { default: OpenApi, \$OpenApi } = require('@alicloud/openapi-client');
    const Ecs20140526 = require('@alicloud/ecs20140526');
    const fs = require('fs');
    
    const configPath = '$CONFIG_FILE';
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
    
    async function test() {
        try {
            const clientConfig = new \$OpenApi({
                [REDACTED_SECRET],
                [REDACTED_SECRET],
            });
            clientConfig.endpoint = 'ecs.aliyuncs.com';
            
            const client = new Ecs20140526(clientConfig);
            const response = await client.describeRegions(new Ecs20140526.DescribeRegionsRequest({}));
            
            if (response.body && response.body.regions && response.body.regions.region) {
                console.log('Connection test passed');
                console.log('Available regions:', response.body.regions.region.length);
                return true;
            }
            throw new Error('Invalid response');
        } catch (error) {
            console.error('Connection failed:', error.message);
            process.exit(1);
        }
    }
    
    test();
    " 2>&1
    
    if [ $? -eq 0 ]; then
        echo -e "${GREEN}✓ 连接验证成功${NC}"
        return 0
    else
        echo -e "${RED}✗ 连接验证失败，请检查密钥和网络${NC}"
        return 1
    fi
}

# 显示帮助
show_help() {
    cat << EOF
阿里云ECS Skill 设置脚本

用法:
  $0 [选项]

选项:
  --check-only          仅检查环境，不修改
  --access-key-id ID    阿里云AccessKey ID
  --access-key-secret S 阿里云AccessKey Secret
  --help                显示此帮助

示例:
  $0 --check-only
  $0 --access-key-id LTAIxxxxx --access-key-secret xxxxx
EOF
}

# 主函数
main() {
    local check_only=false
    local access_key_id=""
    local access_key_secret=""
    
    # 解析参数
    while [[ $# -gt 0 ]]; do
        case $1 in
            --check-only)
                check_only=true
                shift
                ;;
            --access-key-id)
                access_key_id="$2"
                shift 2
                ;;
            --access-key-secret)
                access_key_secret="$2"
                shift 2
                ;;
            --help)
                show_help
                exit 0
                ;;
            *)
                echo "未知选项: $1"
                show_help
                exit 1
                ;;
        esac
    done
    
    echo "=== 阿里云ECS Skill 环境检查 ==="
    echo ""
    
    # 检查Node.js
    local node_ok=false
    if node_check; then
        node_ok=true
    fi
    
    # 检查SDK
    local sdk_ok=false
    if sdk_check; then
        sdk_ok=true
    elif [ "$check_only" = false ] && [ -n "$access_key_id" ]; then
        sdk_install
        sdk_ok=true
    fi
    
    # 检查配置
    local config_ok=false
    if config_check; then
        config_ok=true
    elif [ "$check_only" = false ] && [ -n "$access_key_id" ] && [ -n "$access_key_secret" ]; then
        config_create "$access_key_id" "$access_key_secret"
        config_ok=true
    fi
    
    echo ""
    echo "=== 检查汇总 ==="
    
    if [ "$node_ok" = true ] && [ "$sdk_ok" = true ] && [ "$config_ok" = true ]; then
        echo -e "${GREEN}✓ 所有检查通过，环境已就绪${NC}"
        
        if [ "$check_only" = false ]; then
            test_connection
        fi
        
        exit 0
    else
        echo -e "${YELLOW}⚠ 环境未完全就绪${NC}"
        
        if [ "$check_only" = true ]; then
            exit 1
        fi
        
        if [ -z "$access_key_id" ] || [ -z "$access_key_secret" ]; then
            echo ""
            echo "请提供阿里云AccessKey以完成设置:"
            echo "  $0 --access-key-id YOUR_ID --access-key-secret YOUR_SECRET"
        fi
        
        exit 1
    fi
}

main "$@"


path: src/api/ecs.js
content: const { default: OpenApi, $OpenApi } = require('@alicloud/openapi-client');
const Ecs20140526 = require('@alicloud/ecs20140526');
const fs = require('fs');
const path = require('path');

// 加载配置
function loadConfig() {
    const configPath = path.join(process.env.HOME, '.aliyun', 'config.json');
    if (!fs.existsSync(configPath)) {
        throw new Error('配置文件不存在，请先运行 setup.sh 进行配置');
    }
    return JSON.parse(fs.readFileSync(configPath, 'utf8'));
}

// 创建ECS客户端
function createClient(regionId) {
    const config = loadConfig();
    
    const clientConfig = new $OpenApi({
        [REDACTED_SECRET],
        [REDACTED_SECRET],
    });
    
    clientConfig.endpoint = `ecs.${regionId}.aliyuncs.com`;
    
    return new Ecs20140526(clientConfig);
}

// 查询地域列表
async function describeRegions() {
    const config = loadConfig();
    const clientConfig = new $OpenApi({
        [REDACTED_SECRET],
        [REDACTED_SECRET],
    });
    clientConfig.endpoint = 'ecs.aliyuncs.com';
    
    const client = new Ecs20140526(clientConfig);
    const response = await client.describeRegions(new Ecs20140526.DescribeRegionsRequest({}));
    
    return response.body.regions.region.map(r => ({
        regionId: r.regionId,
        regionEndpoint: r.regionEndpoint,
        localName: r.localName
    }));
}

// 查询实例列表
async function describeInstances(regionId, options = {}) {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.DescribeInstancesRequest({
        regionId: regionId,
        pageSize: options.pageSize || 20,
        pageNumber: options.pageNumber || 1,
    });
    
    if (options.instanceIds) {
        request.instanceIds = options.instanceIds;
    }
    
    const response = await client.describeInstances(request);
    
    if (!response.body.instances || !response.body.instances.instance) {
        return [];
    }
    
    return response.body.instances.instance.map(inst => ({
        instanceId: inst.instanceId,
        instanceName: inst.instanceName,
        status: inst.status,
        regionId: inst.regionId,
        zoneId: inst.zoneId,
        instanceType: inst.instanceType,
        cpu: inst.cpu,
        memory: inst.memory,
        osName: inst.osName,
        osType: inst.osType,
        publicIpAddress: inst.publicIpAddress?.ipAddress || [],
        privateIpAddress: inst.vpcAttributes?.privateIpAddress?.ipAddress || [],
        creationTime: inst.creationTime,
        expiredTime: inst.expiredTime,
        networkType: inst.networkType,
        internetChargeType: inst.internetChargeType,
    }));
}

// 启动实例
async function startInstance(regionId, instanceId) {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.StartInstanceRequest({
        instanceId: instanceId,
    });
    
    const response = await client.startInstance(request);
    return {
        requestId: response.body.requestId,
        success: true,
    };
}

// 停止实例
async function stopInstance(regionId, instanceId, forceStop = false) {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.StopInstanceRequest({
        instanceId: instanceId,
        forceStop: forceStop,
    });
    
    const response = await client.stopInstance(request);
    return {
        requestId: response.body.requestId,
        success: true,
    };
}

// 重启实例
async function rebootInstance(regionId, instanceId, forceStop = false) {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.RebootInstanceRequest({
        instanceId: instanceId,
        forceStop: forceStop,
    });
    
    const response = await client.rebootInstance(request);
    return {
        requestId: response.body.requestId,
        success: true,
    };
}

// 获取监控数据
async function describeInstanceMonitorData(regionId, instanceId, period = 60, startTime, endTime) {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.DescribeInstanceMonitorDataRequest({
        regionId: regionId,
        instanceId: instanceId,
        period: period,
    });
    
    if (startTime) request.startTime = startTime;
    if (endTime) request.endTime = endTime;
    
    const response = await client.describeInstanceMonitorData(request);
    
    if (!response.body.monitorData || !response.body.monitorData.instanceMonitorData) {
        return [];
    }
    
    return response.body.monitorData.instanceMonitorData.map(data => ({
        timestamp: data.timeStamp,
        cpu: data.CPU,
        memory: data.memory,
        internetIn: data.internetIn,
        internetOut: data.internetOut,
        intranetIn: data.intranetIn,
        intranetOut: data.intranetOut,
    }));
}

// 创建快照
async function createSnapshot(regionId, diskId, snapshotName, description = '') {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.CreateSnapshotRequest({
        diskId: diskId,
        snapshotName: snapshotName,
        description: description,
    });
    
    const response = await client.createSnapshot(request);
    return {
        requestId: response.body.requestId,
        snapshotId: response.body.snapshotId,
        success: true,
    };
}

// 查询快照
async function describeSnapshots(regionId, instanceId, diskId) {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.DescribeSnapshotsRequest({
        regionId: regionId,
    });
    
    if (instanceId) request.instanceId = instanceId;
    if (diskId) request.diskIds = JSON.stringify([diskId]);
    
    const response = await client.describeSnapshots(request);
    
    if (!response.body.snapshots || !response.body.snapshots.snapshot) {
        return [];
    }
    
    return response.body.snapshots.snapshot.map(snap => ({
        snapshotId: snap.snapshotId,
        snapshotName: snap.snapshotName,
        description: snap.description,
        status: snap.status,
        progress: snap.progress,
        creationTime: snap.creationTime,
        sourceDiskId: snap.sourceDiskId,
        sourceDiskType: snap.sourceDiskType,
    }));
}

// 回滚快照
async function resetDisk(regionId, diskId, snapshotId) {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.ResetDiskRequest({
        diskId: diskId,
        snapshotId: snapshotId,
    });
    
    const response = await client.resetDisk(request);
    return {
        requestId: response.body.requestId,
        success: true,
    };
}

// 查询安全组
async function describeSecurityGroups(regionId, options = {}) {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.DescribeSecurityGroupsRequest({
        regionId: regionId,
    });
    
    if (options.securityGroupId) {
        request.securityGroupIds = options.securityGroupId;
    }
    
    const response = await client.describeSecurityGroups(request);
    
    if (!response.body.securityGroups || !response.body.securityGroups.securityGroup) {
        return [];
    }
    
    return response.body.securityGroups.securityGroup.map(sg => ({
        securityGroupId: sg.securityGroupId,
        securityGroupName: sg.securityGroupName,
        description: sg.description,
        vpcId: sg.vpcId,
        creationTime: sg.creationTime,
    }));
}

// 查询安全组规则
async function describeSecurityGroupAttribute(regionId, securityGroupId, direction = 'ingress') {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.DescribeSecurityGroupAttributeRequest({
        regionId: regionId,
        securityGroupId: securityGroupId,
        direction: direction,
    });
    
    const response = await client.describeSecurityGroupAttribute(request);
    
    const rules = response.body.permissions?.permission;
    
    if (!rules) return [];
    
    return rules.map(rule => ({
        ipProtocol: rule.ipProtocol,
        portRange: rule.portRange,
        sourceCidrIp: rule.sourceCidrIp,
        destCidrIp: rule.destCidrIp,
        policy: rule.policy,
        description: rule.description,
    }));
}

// 授权安全组规则
async function authorizeSecurityGroup(regionId, securityGroupId, ipProtocol, portRange, sourceCidrIp = '0.0.0.0/0', description = '') {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.AuthorizeSecurityGroupRequest({
        regionId: regionId,
        securityGroupId: securityGroupId,
        ipProtocol: ipProtocol,
        portRange: portRange,
        sourceCidrIp: sourceCidrIp,
        description: description,
        policy: 'accept',
    });
    
    const response = await client.authorizeSecurityGroup(request);
    return {
        requestId: response.body.requestId,
        success: true,
    };
}

// 撤销安全组规则
async function revokeSecurityGroup(regionId, securityGroupId, ipProtocol, portRange, sourceCidrIp = '0.0.0.0/0') {
    const client = createClient(regionId);
    
    const request = new Ecs20140526.RevokeSecurityGroupRequest({
        regionId: regionId,
        securityGroupId: securityGroupId,
        ipProtocol: ipProtocol,
        portRange: portRange,
        sourceCidrIp: sourceCidrIp,
    });
    
    const response = await client.revokeSecurityGroup(request);
    return {
        requestId: response.body.requestId,
        success: true,
    };
}

module.exports = {
    describeRegions,
    describeInstances,
    startInstance,
    stopInstance,
    rebootInstance,
    describeInstanceMonitorData,
    createSnapshot,
    describeSnapshots,
    resetDisk,
    describeSecurityGroups,
    describeSecurityGroupAttribute,
    authorizeSecurityGroup,
    revokeSecurityGroup,
};


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