106 lines
4.1 KiB
JavaScript
Executable File
106 lines
4.1 KiB
JavaScript
Executable File
#!/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);
|
||
});
|