Files
crm-ai/libredesk/legacy/scripts/register-tools.js
T
2026-10-23 10:35:28 +08:00

106 lines
4.1 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
'use strict';
/**
* 把 tools 服务的 manifest 注册到 LibreDesk(POST/PUT /api/v1/ai/tools)。
*
* 前置:
* 1. LibreDesk 已启动
* 2. .env 里配置 LIBREDESK_API_KEY / LIBREDESK_API_SECRET(系统用户或具备 ai:manage 的 agent)
* 3. tools 服务已启动(bash scripts/start-tools.sh)
*
* 权限不足(403/401)时,回退到 Admin → AI → Tools 手工创建(字段见 docs/libredesk-setup.md)。
*/
const fs = require('fs');
const path = require('path');
const root = path.resolve(__dirname, '..');
function parseEnv(file) {
const out = {};
for (const line of fs.readFileSync(file, 'utf8').split('\n')) {
const t = line.trim();
if (!t || t.startsWith('#')) continue;
const eq = t.indexOf('=');
if (eq < 0) continue;
let v = t.slice(eq + 1).trim();
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
out[t.slice(0, eq).trim()] = v;
}
return out;
}
async function main() {
const envPath = path.join(root, '.env');
if (!fs.existsSync(envPath)) throw new Error('.env not found — run scripts/bootstrap.sh first');
const env = parseEnv(envPath);
const base = (env.LIBREDESK_BASE_URL || 'http://localhost:9000').replace(/\/+$/, '');
const apiKey = env.LIBREDESK_API_KEY;
const apiSecret = env.LIBREDESK_API_SECRET;
const toolsPort = env.TOOLS_PORT || '4010';
const toolsKey = env.TOOLS_SHARED_KEY;
if (!apiKey || !apiSecret) throw new Error('LIBREDESK_API_KEY / LIBREDESK_API_SECRET 未配置');
if (!toolsKey) throw new Error('TOOLS_SHARED_KEY 未配置');
const authHeaders = {
Authorization: `token ${apiKey}:${apiSecret}`,
'Content-Type': 'application/json',
};
// 1. 本机拉取 manifest(TOOLS_PUBLIC_BASE_URL 是容器视角,宿主机要用 127.0.0.1)
const manifestRes = await fetch(`http://127.0.0.1:${toolsPort}/tools/manifest`, { headers: { 'X-Tools-Key': toolsKey } });
if (!manifestRes.ok) throw new Error(`tools manifest 不可用(HTTP ${manifestRes.status}),先启动 bash scripts/start-tools.sh`);
const manifest = await manifestRes.json();
// 2. 现有工具
const listRes = await fetch(`${base}/api/v1/ai/tools`, { headers: authHeaders });
const listText = await listRes.text();
if (!listRes.ok) throw new Error(`读取 AI tools 失败(HTTP ${listRes.status}):${listText.slice(0, 300)}`);
const listJson = JSON.parse(listText);
const existing = Array.isArray(listJson) ? listJson : (listJson.data || []);
const byName = new Map(existing.map((t) => [t.name, t]));
// 3. 逐个创建/更新
const results = [];
for (const tool of manifest.tools) {
const body = {
name: tool.name,
description: tool.description,
url: tool.url,
method: 'POST',
auth: { headers: [{ key: 'X-Tools-Key', value: toolsKey }] },
parameters: tool.parameters,
enabled: true,
copilot_enabled: true,
generate_reply_enabled: true,
requires_agent_approval: false,
requires_verification: Boolean(tool.requires_verification),
};
const current = byName.get(tool.name);
const url = current ? `${base}/api/v1/ai/tools/${current.id}` : `${base}/api/v1/ai/tools`;
const res = await fetch(url, { method: current ? 'PUT' : 'POST', headers: authHeaders, body: JSON.stringify(body) });
const text = await res.text();
if (!res.ok) {
results.push({ name: tool.name, ok: false, status: res.status, body: text.slice(0, 300) });
} else {
results.push({ name: tool.name, ok: true, action: current ? 'updated' : 'created' });
}
}
for (const r of results) {
if (r.ok) console.log(` ✓ ${r.name} (${r.action})`);
else console.error(` ✗ ${r.name} → HTTP ${r.status}: ${r.body}`);
}
if (results.some((r) => !r.ok)) {
console.error('\n部分工具注册失败。常见原因:API key 权限不足(需要 ai:manage)、LibreDesk 版本字段不匹配。');
console.error('回退方案:按 docs/libredesk-setup.md 在 Admin → AI → Tools 手工创建。');
process.exitCode = 1;
}
}
main().catch((err) => {
console.error('[register-tools]', err.message);
process.exit(1);
});