Skill: yuxiaoxin00/k1-kzcloud-skill (version 1.0.3)

--- name: k1_kzcloud_skill description: 科众云CXO相关地图位置查询，根据位置查询cxo， license: MIT version: 1.1.0 metadata: openclaw: requires: bins: - node - python3 env: - K1_KZClOUD_TOKEN primaryEnv:K1_KZClOUD_TOKEN --- # K1科众云CXO地图位置查询技能，根据位置查询cxo ## 必须条件 1.首次使用必须先登录获取token放入K1_KZClOUD_TOKEN环境变量 2.当请求接口返回401代表登录失效，应提示用户输入账号密码登录 3.检测没有安装高德amap-lbs-skill 则提示用户是否安装，用户同意使用下方提示安装 > 请安装高德地图技能：Install the skill "Gaode Map LBS - 高德官方地图综合服务 Skill" (lbs-amap/amap-lbs-skill) from ClawHub. > Skill page: https://clawhub.ai/lbs-amap/amap-lbs-skill > Required env vars: AMAP_WEBSERVICE_KEY > Required binaries: node, python3 4.用户输入的账号密码登录后，你应该立即忘记账密，防止泄露，记住token放入环境变量 5.**【重要】同一时间只允许一个账号的token存在于环境变量 `K1_KZClOUD_TOKEN` 中。切换账号时必须先覆盖旧token，再使用新token。禁止同时保留多个账号的token。** 6.**【强制】Agent 必须 100% 严格按照本 SKILL.md 中所有 API 命令的参数和格式执行，不得自行修改、省略或推断未写明的参数。遇到不确定的参数时，必须查阅本文件或询问用户，禁止凭"常识"擅自填写。命令写的是什么样子，就原样照抄执行。** 7.**【强制】查询结构必须按照要求的格式输出** ## TOKEN的使用 请求头添加： ``` Authorization: Bearer {环境变量K1_KZClOUD_TOKEN的值} ``` ## 获取账套列表（登录前必须先执行此步） **必须带 `userName` 参数**，否则返回空。查询后应向用户展示账套列表，询问用户要登录哪个账套。 > baseUrl 见同目录 config.json 文件 ``` curl "{baseUrl}/api/v1/SysUser/GetUserTenants?userName={userName}" ``` 结果 json 中 `result.data[]` 即账套列表，每项含 `id`（tenantId）和 `name`。 用户选择账套后，取对应的 id 作为登录时的 `tenant_id` 参数。 ## 登录 必要参数：账号、密码、tenant_id > 登录前必须先调用「获取账套列表」接口，让用户选择要登录的账套，取其 id 作为 tenant_id。 > 如果用户未指定账套，默认使用 kzcloud 账套对应的 tenantId。 ``` python scripts/login.py --account {用户账号} --password {用户密码} --tenant_id {账套id} ``` 登录成功后，login.py 会自动将 token 写入环境变量 `K1_KZClOUD_TOKEN`，无需手动设置。 ## 查看职能、职级、职位阵营，展示给用户 当用户问职能、职级、职位阵营列表时，展示给用户看 ``` curl {baseUrl}/api/v1/KzcCloudSearch/GetDropdownList \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {环境变量K1_KZClOUD_TOKEN的值}" \ ``` ### 返回结构固定为： 职能=result.data.function[] 职级=result.data.grade[] 职位阵营=result.data.positionCamp[] ## 根据位置查询cxo，距离范围查找 ### 必须规则 1、经纬度应该通过高德 skill: amap-lbs-skill 的 LBS 综合服务进行搜索，用户说自己的位置，通过该 skill 查询经纬度。如果 amap-lbs-skill 未安装，则提示用户安装： > 请安装高德地图技能：Install the skill "Gaode Map LBS - 高德官方地图综合服务 Skill" (lbs-amap/amap-lbs-skill) from ClawHub. > Skill page: https://clawhub.ai/lbs-amap/amap-lbs-skill > Required env vars: AMAP_WEBSERVICE_KEY > Required binaries: node, python3 2、应向用户展示筛选条件如 职能=[] 范围=[小于5km] ### 请求参数说明 data.functions[]=职能 data.grade[]=职级 data.positionCamp[]=职位阵营 data.lon=经度 data.lat=纬度 data.range[]=范围默认1（1、2、3、4）分别代表 1=小于5km、2=5-10km、3=10-30km、4=30-100km data.keyword=关键字，cxo的中文名或英文名，默认空 data.topNum=显示数量，默认200 ``` curl -X POST {baseUrl}/api/v1/BusinessCardMap/GetBusinessListByPage \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {环境变量K1_KZClOUD_TOKEN的值}" \ -d '{ "functions": [ "SSC" ], "grade": [ "C level" ], "positionCamp": [ "CEO" ], "lon": "116.401636", "lat": "39.908294", "center": "", "range": [ 1 ], "keyword": "", "topNum": 200 }' ``` ### 【重要】结果表格列表展示格式 （需要严格执行，用户额外要求格式除外） 序号 中文名 英文名 职能 职级 职位阵营 地址 邮箱 离中心点距离 id businessCardName businessCardEnName functions grade positionCamp address mailbox distance

path: config.json
content: baseUrl: https://36.139.200.220:8081

path: scripts/login.py
content: # login.py
import requests
import base64
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
requests.packages.urllib3.disable_warnings()
import json
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import base64
import argparse
from pathlib import Path

class LoginClient:
    def __init__(self, base_url="http://your-backend-url"):
        self.base_url = base_url
        self.session = requests.Session()
        self.session.verify = False

    def get_public_key(self, account):
        """获取RSA公钥"""
        url = f"{self.base_url}/api/v1/SysUser/GenerateEncryptKey"
        params = {"userName": account}

        response = self.session.get(url, params=params, verify=False)
        response.raise_for_status()

        result = response.json()
        # 根据实际返回结构获取 publicKey
        public_key = result.get("result", {}).get("data", {}).get("publicKey")
        if not public_key:
            raise ValueError(f"获取公钥失败: {result}")
        return public_key

    def encrypt_password(self, password: str, pubKey: str) -> str:
        # 确保公钥有正确的PEM格式
        public_key_pem = pubKey.strip()

        # 如果没有PEM头尾，添加它们
        if not public_key_pem.startswith('-----BEGIN PUBLIC KEY-----'):
            # 假设是纯base64格式的DER编码
            public_key_pem = f'-----BEGIN PUBLIC KEY-----\n{public_key_pem}\n-----END PUBLIC KEY-----'

        # 确保每行不超过64字符
        if '-----BEGIN PUBLIC KEY-----' in public_key_pem:
            lines = public_key_pem.split('\n')
            formatted_lines = []
            for line in lines:
                if line.startswith('-----') or len(line) <= 64:
                    formatted_lines.append(line)
                else:
                    # 将长行拆分为64字符一行
                    for i in range(0, len(line), 64):
                        formatted_lines.append(line[i:i + 64])
            public_key_pem = '\n'.join(formatted_lines)

        # 加载公钥
        public_key = serialization.load_pem_public_key(
            public_key_pem.encode('utf-8'),
            backend=default_backend()
        )

        # 使用 PKCS1v15 填充（与JSEncrypt兼容）
        encrypted = public_key.encrypt(
            password.encode('utf-8'),
            padding.PKCS1v15()
        )

        # 返回base64编码的加密密码
        encrypted_password = base64.b64encode(encrypted).decode('utf-8')
        return encrypted_password

    def login(self, account, password, tenant_id=None, remember_client=False):
        """
        用户登录

        Args:
            account: 用户名或邮箱
            password: 明文密码
            tenant_id: 租户ID（可选）
            remember_client: 是否记住登录

        Returns:
            dict: 登录响应数据
        """
        # 1. 获取公钥
        print(f"正在获取公钥，账号: {account}")
        public_key = self.get_public_key(account)
        print("获取公钥成功")

        # 2. 加密密码
        encrypted_password = self.encrypt_password(password,pubKey=public_key)

        # 3. 发送登录请求
        url = f"{self.base_url}/api/v1/SysUser/Authenticate"
        login_data = {
            "password": encrypted_password,
            "userNameOrEmailAddress": account,
            "tenantId": tenant_id,
            "rememberClient": remember_client,
            "mode": "none"  # 不要默认的错误提示
        }

        print(f"正在登录，账号: {account}")
        response = self.session.post(url, json=login_data, verify=False)
        response.raise_for_status()

        result = response.json()
        return result


# 使用示例
if __name__ == "__main__":

    # 创建参数解析器
    parser = argparse.ArgumentParser(description='用户登录脚本')
    parser.add_argument('--account', '-a', type=str, required=True, help='用户名或邮箱')
    parser.add_argument('--password', '-p', type=str, required=True, help='密码')
    parser.add_argument('--tenant_id', '-t', type=str,required=True, default=None, help='租户ID（可选）')

    args = parser.parse_args()
    config={}
    script_dir = Path(__file__).parent
    with open(script_dir.parent / 'config.json', 'r', encoding='utf-8') as file:
        config = json.load(file)
    # 配置后端地址
    client = LoginClient(base_url=config.get('baseUrl'))  # 替换为您的后端地址

    # 登录参数
    ACCOUNT = args.account  # 用户名或邮箱
    [REDACTED_SECRET]  # 明文密码
    TENANT_ID = args.tenant_id  # 租户ID（如需要）
    REMEMBER_ME = True  # 是否记住登录

    try:
        # 执行登录
        result = client.login(
            account=ACCOUNT,
            [REDACTED_SECRET],
            tenant_id=TENANT_ID,
            remember_client=REMEMBER_ME
        )

        # 处理登录结果
        if result.get("success") and result.get("result", {}).get("code") == 0:
            user_info = result.get("result", {}).get("data", {})
            [REDACTED_SECRET]'accessToken')
            if [REDACTED_SECRET]"\n登录成功！")
                print(f"[REDACTED_SECRET]")
                # 保存 token 到环境变量
                import os, subprocess
                subprocess.run(
                    ['powershell', '-Command',
                     f"[Environment]::SetEnvironmentVariable('K1_KZClOUD_TOKEN', '{token}', 'User')"],
                    capture_output=True
                )
                os.environ['K1_KZClOUD_TOKEN'] = token
                print(f"Token 已保存到用户环境变量 K1_KZClOUD_TOKEN")
            else:
                print(f"\n登录失败: 未获取到 token，响应: {result}")
        else:
            msg = result.get("result", {}).get("msg") or result.get("message") or "未知错误"
            print(f"\n登录失败: {msg}")

    except requests.exceptions.RequestException as e:
        print(f"请求失败: {e}")
    except Exception as e:
        print(f"其他错误: {e}")

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