Skill: keepchen/stardots-backup-skill (version 1.0.1)

# # StarDots Backup 将图像自动备份到 stardots.io 云存储平台，并提供访问链接。 ## 功能特点 - 📤 一键上传图像到 Stardots.io - 📋 查看已上传文件列表 - 🔒 安全的API认证 - 📁 支持多空间管理 - 🌐 跨平台支持 ## 安装方法 1. 在 OpenClaw 市场中搜索 "Stardots Backup" 2. 点击安装 3. 配置你的 Stardots.io API 凭证 ## 配置说明 使用前需要在技能设置中配置以下参数： | 参数 | 必填 | 说明 | |------|------|------| | apiKey | 是 | Stardots.io API密钥 | | apiSecret | 是 | Stardots.io API密钥 | | space | 是 | 目标空间名称 | ## 使用方法 ### 上传图像 上传 /path/to/your/image.jpg 备份 ~/Photos/vacation.png ### 查看文件列表 列出文件 显示所有备份文件 ### 获取帮助 帮助 如何使用备份 ## 使用示例 **用户**: "上传 ~/Documents/photo.jpg" **技能**: "✅ 图像上传成功！ 文件: photo.jpg 大小: 2.5 MB URL: https://stardots.io/..." **用户**: "列出文件" **技能**: "📁 文件列表 (第1页，共15个文件): - vacation.jpg (1.2 MB) [2024-01-15 14:30:22] - ..." ## 注意事项 - 支持的文件格式: JPG, JPEG, PNG, GIF, BMP, SVG, AVIF, WEBP - 文件大小限制取决于你的 Stardots.io 套餐 - API凭证会加密存储，确保安全 ## 故障排除 **上传失败: 文件不存在** - 确保文件路径正确 - 使用绝对路径或相对于当前工作目录的路径 **认证失败** - 检查API密钥是否正确 - 确认API密钥有足够权限 ## 隐私说明 本技能仅在上传文件时访问指定路径的文件，不会收集任何个人信息。所有API凭证均加密存储。 ## 限制 - 速率限制：每分钟 300 次请求 - 文件大小：最大可升级到 30MB - 文件名长度：最多 170 个字符 ## 链接 - [Stardots.io](https://stardots.io) - [API 文档](https://stardots.io/en/documentation/openapi) - [ClawHub](https://clawhub.com) ## 许可证 MIT

path: dist/stardots-client.js
content: "use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StardotsClient = void 0;
const crypto_1 = __importDefault(require("crypto"));
const axios_1 = __importDefault(require("axios"));
const form_data_1 = __importDefault(require("form-data"));
const fs_1 = require("fs");
class StardotsClient {
    constructor(config) {
        this.baseURL = 'https://api.stardots.io';
        this.config = config;
        this.client = axios_1.default.create({
            baseURL: this.baseURL,
            timeout: 60000,
        });
    }
    generateNonce(length = 10) {
        const chars = [REDACTED_SECRET];
        let nonce = '';
        for (let i = 0; i < length; i++) {
            nonce += chars.charAt(Math.floor(Math.random() * chars.length));
        }
        return nonce;
    }
    generateSign(timestamp, nonce) {
        const needSignStr = `${timestamp}|${this.config.apiSecret}|${nonce}`;
        return crypto_1.default.createHash('md5').update(needSignStr).digest('hex').toUpperCase();
    }
    getAuthHeaders() {
        const timestamp = Math.floor(Date.now() / 1000);
        const nonce = this.generateNonce();
        const sign = this.generateSign(timestamp, nonce);
        return {
            'x-stardots-timestamp': timestamp.toString(),
            'x-stardots-nonce': nonce,
            'x-stardots-key': this.config.apiKey,
            'x-stardots-sign': sign,
            'x-stardots-extensions': '{"via": "openclaw", "version": "1.0.0"}'
        };
    }
    async uploadImage(imagePath, space, metadata) {
        try {
            const formData = new form_data_1.default();
            formData.append('file', (0, fs_1.createReadStream)(imagePath));
            formData.append('space', space || this.config.space);
            if (metadata) {
                formData.append('metadata', JSON.stringify(metadata));
            }
            const headers = {
                ...this.getAuthHeaders(),
                ...formData.getHeaders(),
            };
            const response = await this.client.put('/openapi/file/upload', formData, { headers });
            if (response.data?.success) {
                return {
                    success: true,
                    url: response.data.data.url,
                    message: '上传成功',
                };
            }
            return {
                success: false,
                url: '',
                message: response.data?.message || '上传失败',
            };
        }
        catch (error) {
            return {
                success: false,
                url: '',
                message: error.response?.data?.message || error.message,
            };
        }
    }
    async listFiles(params) {
        try {
            const response = await this.client.get('/openapi/file/list', {
                headers: this.getAuthHeaders(),
                params: {
                    space: params.space || this.config.space,
                    page: params.page || 1,
                    pageSize: params.pageSize || 20,
                },
            });
            if (response.data?.success) {
                return {
                    files: response.data.data.files || [],
                    total: response.data.data.total || 0,
                    page: params.page || 1,
                    pageSize: params.pageSize || 20,
                };
            }
            throw new Error(response.data?.message || '获取文件列表失败');
        }
        catch (error) {
            throw new Error(`获取文件列表失败: ${error.message}`);
        }
    }
    async deleteFile(fileId, space) {
        try {
            const response = await this.client.delete('/openapi/file/delete', {
                headers: this.getAuthHeaders(),
                data: {
                    fileId,
                    space: space || this.config.space,
                },
            });
            return response.data?.success || false;
        }
        catch (error) {
            return false;
        }
    }
}
exports.StardotsClient = StardotsClient;
//# sourceMappingURL=stardots-client.js.map

path: dist/types.js
content: "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.js.map

path: dist/index.d.ts
content: interface SkillContext {
    userMessage: {
        content: string;
    };
    config: Record<string, any>;
}
interface SkillResult {
    success: boolean;
    message: string;
    data?: any;
    error?: string;
    suggestActions?: string[];
}
export default class StardotsBackupSkill {
    name: string;
    description: string;
    version: string;
    private client;
    private config;
    initialize(config: Record<string, any>): Promise<void>;
    execute(context: SkillContext): Promise<SkillResult>;
    private matchUploadIntent;
    private matchListIntent;
    private handleUpload;
    private handleList;
    private getHelpMessage;
}
export {};
//# sourceMappingURL=index.d.ts.map

path: dist/stardots-client.d.ts
content: import { StardotsConfig, UploadResult, ListResult } from './types';
export declare class StardotsClient {
    private client;
    private config;
    private readonly baseURL;
    constructor(config: StardotsConfig);
    private generateNonce;
    private generateSign;
    private getAuthHeaders;
    uploadImage(imagePath: string, space?: string, metadata?: Record<string, any>): Promise<UploadResult>;
    listFiles(params: {
        page?: number;
        pageSize?: number;
        space?: string;
    }): Promise<ListResult>;
    deleteFile(fileId: string, space?: string): Promise<boolean>;
}
//# sourceMappingURL=stardots-client.d.ts.map

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