#!/usr/bin/env node const fs = require('fs'); const https = require('https'); const CONFIG_PATH = process.env.HOME + '/.config/vaultwarden/config.json'; let config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); const VAULT_URL = config.url; const CLIENT_ID = config.client_id; const CLIENT_SECRET = config.client_secret; let token = null; let tokenExpiry = null; const deviceId = 'openclaw-cli-' + require('crypto').randomUUID(); async function getToken() { if (token && tokenExpiry && Date.now() < tokenExpiry - 300000) { return token; } const params = new URLSearchParams({ grant_type: 'client_credentials', scope: 'api', client_id: CLIENT_ID, client_secret: CLIENT_SECRET, device_identifier: deviceId, device_type: '14', device_name: 'OpenClaw CLI' }); const response = await new Promise((resolve, reject) => { const url = new URL(VAULT_URL + '/identity/connect/token'); const options = { hostname: url.hostname, port: url.port || 443, path: url.pathname + url.search, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, timeout: 30000 }; const req = https.request(options, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { try { const json = JSON.parse(body); resolve(json); } catch (e) { reject(e); } }); }); req.on('error', reject); req.write(params.toString()); req.end(); }); if (!response.access_token) { throw new Error('认证失败'); } token = response.access_token; tokenExpiry = Date.now() + ((response.expires_in || 7200) - 300) * 1000; return token; } async function getItemByName(name) { const t = await getToken(); const response = await new Promise((resolve, reject) => { const url = new URL(VAULT_URL + '/api/ciphers'); const options = { hostname: url.hostname, port: url.port || 443, path: url.pathname + url.search, method: 'GET', headers: { 'Authorization': `Bearer ${t}` }, timeout: 30000 }; const req = https.request(options, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { try { const json = JSON.parse(body); resolve(json); } catch (e) { reject(e); } }); }); req.on('error', reject); req.end(); }); return (response.data || []).find(item => item.name?.toLowerCase() === name.toLowerCase()); } async function deleteItem(id) { const t = await getToken(); return new Promise((resolve, reject) => { const url = new URL(VAULT_URL + `/api/ciphers/${id}?permanent=true`); const options = { hostname: url.hostname, port: url.port || 443, path: url.pathname + url.search, method: 'DELETE', headers: { 'Authorization': `Bearer ${t}` }, timeout: 30000 }; const req = https.request(options, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { if (res.statusCode >= 200 && res.statusCode < 300) resolve(); else reject(new Error(`HTTP ${res.statusCode}: ${body}`)); }); }); req.on('error', reject); req.end(); }); } async function main() { try { const item = await getItemByName('Test Account'); if (!item) { console.log('❌ 未找到: Test Account'); return; } console.log(`🗑️ 删除 "${item.name}"...`); await deleteItem(item.id); console.log(`✅ 已删除: ${item.name}\n`); } catch (error) { console.error('\n❌ 错误:', error.message); process.exit(1); } } main();