Skill: anbangzhiguo/openclaw-local-storage (version 1.0.0)

--- name: "openclaw-local-storage" description: "使用Node.js实现本地文件存储数据，支持增删改查功能。第一次使用时自动分析数据字段，之后可通过一句话命令进行操作。" --- # OpenClaw 本地存储技能 ## 功能介绍 本技能使用Node.js实现本地文件存储数据，支持以下功能： 1. **自动字段分析**：第一次使用时，根据用户输入的内容自动分析数据结构和字段 2. **增删改查**：支持通过一句话命令进行数据的添加、删除、修改和查询操作 3. **本地文件存储**：数据存储在本地JSON文件中，无需外部数据库 ## 使用方法 ### 第一次使用 当您第一次使用本技能时，系统会要求您输入要存储的内容，以便自动分析数据字段。例如： ``` 存储一条用户信息：{"name": "张三", "age": 25, "email": "[REDACTED_SECRET]"} ``` 系统会自动分析出数据包含name、age、email三个字段，并创建相应的存储结构。 ### 数据操作 之后，您可以通过一句话命令进行数据的增删改查操作： #### 添加数据 ``` 添加一条用户信息：name为李四，age为30，email为[REDACTED_SECRET] ``` #### 查询数据 ``` 查询所有用户信息 ``` ``` 查询name为张三的用户信息 ``` #### 修改数据 ``` 修改name为张三的用户信息，age改为26 ``` #### 删除数据 ``` 删除name为李四的用户信息 ``` ## 技术实现 本技能使用以下技术： - **Node.js**：运行环境 - **JSON文件**：本地存储格式 - **自动字段分析**：根据首次输入的数据结构自动生成字段定义 - **自然语言处理**：解析用户的一句话命令，提取操作类型和参数 ## 注意事项 1. 数据存储在本地JSON文件中，确保文件权限正确 2. 首次使用时，请提供完整的数据结构，以便系统正确分析字段 3. 操作命令应清晰明确，包含操作类型和必要的参数 4. 支持的数据类型包括字符串、数字、布尔值和简单对象 ## 示例 ### 首次使用 用户输入： ``` 存储一条商品信息：{"id": 1, "name": "手机", "price": 3999, "stock": 100} ``` 系统分析出字段：id、name、price、stock ### 添加数据 用户输入： ``` 添加一条商品信息：id为2，name为电脑，price为5999，stock为50 ``` ### 查询数据 用户输入： ``` 查询所有商品信息 ``` ### 修改数据 用户输入： ``` 修改id为1的商品信息，price改为3499 ``` ### 删除数据 用户输入： ``` 删除id为2的商品信息 ```

path: test.js
content: const OpenClawLocalStorage = require('./src/index');

async function test() {
  const storage = new OpenClawLocalStorage();

  // 测试初始化
  console.log('=== 测试初始化 ===');
  let result = await storage.processCommand('存储一条用户信息：{"name": "张三", "age": 25, "email": "[REDACTED_SECRET]"}');
  console.log(result);

  // 测试添加数据
  console.log('\n=== 测试添加数据 ===');
  result = await storage.processCommand('添加一条用户信息：name为李四，age为30，email为[REDACTED_SECRET]');
  console.log(result);

  // 测试查询所有数据
  console.log('\n=== 测试查询所有数据 ===');
  result = await storage.processCommand('查询所有用户信息');
  console.log(result);

  // 测试条件查询
  console.log('\n=== 测试条件查询 ===');
  result = await storage.processCommand('查询name为张三的用户信息');
  console.log(result);

  // 测试修改数据
  console.log('\n=== 测试修改数据 ===');
  result = await storage.processCommand('修改name为张三的用户信息，age改为26');
  console.log(result);

  // 测试删除数据
  console.log('\n=== 测试删除数据 ===');
  result = await storage.processCommand('删除name为李四的用户信息');
  console.log(result);

  // 测试查询所有数据（验证删除结果）
  console.log('\n=== 测试查询所有数据（验证删除结果） ===');
  result = await storage.processCommand('查询所有用户信息');
  console.log(result);
}

test().catch(console.error);

path: data.json
content: name: 张三
age: 26
email: [REDACTED_SECRET]

path: package.json
content: {
  "name": "openclaw-local-storage",
  "version": "1.0.0",
  "description": "OpenClaw本地存储技能，使用Node.js实现本地文件存储数据，支持增删改查功能",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "openclaw",
    "local-storage",
    "nodejs"
  ],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "fs": "^0.0.1-security",
    "path": "^0.12.7"
  }
}

path: src/analyzer.js
content: class Analyzer {
  analyzeFields(data) {
    const fields = {};
    
    // 遍历数据对象的所有属性
    for (const key in data) {
      if (data.hasOwnProperty(key)) {
        const value = data[key];
        // 确定字段类型
        fields[key] = this.getType(value);
      }
    }
    
    return fields;
  }

  getType(value) {
    if (value === null) {
      return 'null';
    } else if (Array.isArray(value)) {
      return 'array';
    } else if (typeof value === 'object') {
      return 'object';
    } else {
      return typeof value;
    }
  }
}

module.exports = Analyzer;

path: src/index.js
content: const fs = require('fs');
const path = require('path');
const Storage = require('./storage');
const Parser = require('./parser');
const Analyzer = require('./analyzer');

class OpenClawLocalStorage {
  constructor() {
    this.storage = new Storage();
    this.parser = new Parser();
    this.analyzer = new Analyzer();
    this.isInitialized = false;
  }

  async processCommand(command) {
    if (!this.isInitialized) {
      return await this.initialize(command);
    }

    const parsed = this.parser.parseCommand(command);
    if (!parsed) {
      return '无法解析命令，请使用正确的格式';
    }

    switch (parsed.action) {
      case 'add':
        return await this.storage.add(parsed.data);
      case 'query':
        return await this.storage.query(parsed.conditions);
      case 'update':
        return await this.storage.update(parsed.conditions, parsed.data);
      case 'delete':
        return await this.storage.delete(parsed.conditions);
      default:
        return '不支持的操作类型';
    }
  }

  async initialize(command) {
    // 提取首次存储的数据
    const dataMatch = command.match(/存储一条.*：(.*)/);
    if (!dataMatch) {
      return '请提供要存储的数据，格式为：存储一条[类型]信息：{"字段1": "值1", "字段2": "值2"}';
    }

    try {
      const data = JSON.parse(dataMatch[1]);
      const fields = this.analyzer.analyzeFields(data);
      
      // 初始化存储
      await this.storage.init(fields);
      await this.storage.add(data);
      
      this.isInitialized = true;
      return `初始化成功！分析出以下字段：${Object.keys(fields).join('、')}`;
    } catch (error) {
      return '数据格式错误，请确保输入的是有效的JSON格式';
    }
  }
}

module.exports = OpenClawLocalStorage;

path: src/parser.js
content: class Parser {
  parseCommand(command) {
    // 分析操作类型
    if (command.includes('添加')) {
      return this.parseAddCommand(command);
    } else if (command.includes('查询')) {
      return this.parseQueryCommand(command);
    } else if (command.includes('修改')) {
      return this.parseUpdateCommand(command);
    } else if (command.includes('删除')) {
      return this.parseDeleteCommand(command);
    }
    return null;
  }

  parseAddCommand(command) {
    // 解析添加命令，格式：添加一条[类型]信息：字段1为值1，字段2为值2
    const dataMatch = command.match(/添加一条.*：(.*)/);
    if (!dataMatch) return null;

    const dataStr = dataMatch[1];
    const data = {};

    // 提取字段和值
    const fieldMatches = dataStr.match(/([^，]+)为([^，]+)/g);
    if (!fieldMatches) return null;

    fieldMatches.forEach(match => {
      const [field, value] = match.split('为');
      // 尝试转换值的类型
      let parsedValue = value;
      if (!isNaN(value) && value.trim() !== '') {
        parsedValue = Number(value);
      } else if (value === 'true') {
        parsedValue = true;
      } else if (value === 'false') {
        parsedValue = false;
      }
      data[field.trim()] = parsedValue;
    });

    return {
      action: 'add',
      data
    };
  }

  parseQueryCommand(command) {
    // 解析查询命令，格式：查询所有[类型]信息 或 查询字段为值的[类型]信息
    if (command.includes('所有')) {
      return {
        action: 'query',
        conditions: {}
      };
    }

    // 解析条件查询
    const conditionMatch = command.match(/查询([^的]+)为([^的]+)的/);
    if (conditionMatch) {
      const [, field, value] = conditionMatch;
      // 尝试转换值的类型
      let parsedValue = value;
      if (!isNaN(value) && value.trim() !== '') {
        parsedValue = Number(value);
      } else if (value === 'true') {
        parsedValue = true;
      } else if (value === 'false') {
        parsedValue = false;
      }

      return {
        action: 'query',
        conditions: {
          [field.trim()]: parsedValue
        }
      };
    }

    return null;
  }

  parseUpdateCommand(command) {
    // 解析修改命令，格式：修改字段为值的[类型]信息，字段改为值
    const conditionMatch = command.match(/修改([^的]+)为([^的]+)的.*，(.*)/);
    if (!conditionMatch) return null;

    const [, conditionField, conditionValue, dataStr] = conditionMatch;
    
    // 解析条件
    let parsedConditionValue = conditionValue;
    if (!isNaN(conditionValue) && conditionValue.trim() !== '') {
      parsedConditionValue = Number(conditionValue);
    } else if (conditionValue === 'true') {
      parsedConditionValue = true;
    } else if (conditionValue === 'false') {
      parsedConditionValue = false;
    }

    // 解析要修改的数据
    const data = {};
    const dataMatches = dataStr.match(/([^，]+)改为([^，]+)/g);
    if (!dataMatches) return null;

    dataMatches.forEach(match => {
      const [field, value] = match.split('改为');
      // 尝试转换值的类型
      let parsedValue = value;
      if (!isNaN(value) && value.trim() !== '') {
        parsedValue = Number(value);
      } else if (value === 'true') {
        parsedValue = true;
      } else if (value === 'false') {
        parsedValue = false;
      }
      data[field.trim()] = parsedValue;
    });

    return {
      action: 'update',
      conditions: {
        [conditionField.trim()]: parsedConditionValue
      },
      data
    };
  }

  parseDeleteCommand(command) {
    // 解析删除命令，格式：删除字段为值的[类型]信息
    const conditionMatch = command.match(/删除([^的]+)为([^的]+)的/);
    if (!conditionMatch) return null;

    const [, field, value] = conditionMatch;
    // 尝试转换值的类型
    let parsedValue = value;
    if (!isNaN(value) && value.trim() !== '') {
      parsedValue = Number(value);
    } else if (value === 'true') {
      parsedValue = true;
    } else if (value === 'false') {
      parsedValue = false;
    }

    return {
      action: 'delete',
      conditions: {
        [field.trim()]: parsedValue
      }
    };
  }
}

module.exports = Parser;

path: src/storage.js
content: const fs = require('fs');
const path = require('path');

class Storage {
  constructor() {
    this.dataFile = path.join(__dirname, '../data.json');
    this.fields = {};
  }

  async init(fields) {
    this.fields = fields;
    // 确保数据文件存在
    if (!fs.existsSync(this.dataFile)) {
      await this.saveData([]);
    }
  }

  async add(data) {
    try {
      const currentData = await this.loadData();
      currentData.push(data);
      await this.saveData(currentData);
      return '数据添加成功';
    } catch (error) {
      return '数据添加失败：' + error.message;
    }
  }

  async query(conditions = {}) {
    try {
      const data = await this.loadData();
      if (Object.keys(conditions).length === 0) {
        return JSON.stringify(data, null, 2);
      }

      const filtered = data.filter(item => {
        return Object.entries(conditions).every(([key, value]) => {
          return item[key] === value;
        });
      });

      return JSON.stringify(filtered, null, 2);
    } catch (error) {
      return '查询失败：' + error.message;
    }
  }

  async update(conditions, data) {
    try {
      const currentData = await this.loadData();
      let updated = false;

      const newData = currentData.map(item => {
        const match = Object.entries(conditions).every(([key, value]) => {
          return item[key] === value;
        });

        if (match) {
          updated = true;
          return { ...item, ...data };
        }
        return item;
      });

      if (updated) {
        await this.saveData(newData);
        return '数据更新成功';
      } else {
        return '未找到匹配的数据';
      }
    } catch (error) {
      return '更新失败：' + error.message;
    }
  }

  async delete(conditions) {
    try {
      const currentData = await this.loadData();
      const newData = currentData.filter(item => {
        return !Object.entries(conditions).every(([key, value]) => {
          return item[key] === value;
        });
      });

      if (newData.length < currentData.length) {
        await this.saveData(newData);
        return '数据删除成功';
      } else {
        return '未找到匹配的数据';
      }
    } catch (error) {
      return '删除失败：' + error.message;
    }
  }

  async loadData() {
    if (!fs.existsSync(this.dataFile)) {
      return [];
    }

    const data = fs.readFileSync(this.dataFile, 'utf8');
    return JSON.parse(data);
  }

  async saveData(data) {
    fs.writeFileSync(this.dataFile, JSON.stringify(data, null, 2), 'utf8');
  }
}

module.exports = Storage;

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