99 lines
4.1 KiB
TypeScript
99 lines
4.1 KiB
TypeScript
// qbittorrent's tools — living in the module (novox/hq ADR 0039), importing qbittorrent's own
|
|
// client. They return structured data; the mesh serves them through the sdk's tool harness.
|
|
|
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
|
import { QbittorrentClient } from "../client.js";
|
|
|
|
export function getQbittorrentTools(qb: QbittorrentClient): ToolDefinition[] {
|
|
return [
|
|
{
|
|
name: "qbittorrent_status",
|
|
description: "qBittorrent status: version, global transfer rates, and how many torrents are active.",
|
|
input: {},
|
|
run: async () => {
|
|
const [version, transfer, torrents] = await Promise.all([
|
|
qb.getVersion(),
|
|
qb.getTransferInfo(),
|
|
qb.getTorrents(),
|
|
]);
|
|
const downloading = torrents.filter((t) => t.progress < 1).length;
|
|
return { version, transfer, torrents: torrents.length, downloading, seeding: torrents.length - downloading };
|
|
},
|
|
},
|
|
{
|
|
name: "qbittorrent_torrents",
|
|
description: "List torrents — name, state, progress and speed. Optional filter narrows the set.",
|
|
input: {
|
|
filter: { type: "string", description: "one of all|downloading|seeding|completed|paused|active|inactive|stalled" },
|
|
},
|
|
run: async (args) => {
|
|
const items = await qb.getTorrents(args.filter ? String(args.filter) : undefined);
|
|
return { count: items.length, torrents: items };
|
|
},
|
|
},
|
|
{
|
|
name: "qbittorrent_add",
|
|
description: "Add a torrent by magnet link or .torrent URL, optionally into a category.",
|
|
input: {
|
|
url: { type: "string", description: "magnet link or http(s) URL to a .torrent" },
|
|
category: { type: "string", description: "category name (determines save directory)" },
|
|
savepath: { type: "string", description: "explicit save path (overrides the category default)" },
|
|
paused: { type: "boolean", description: "add in paused state (default false)" },
|
|
},
|
|
run: async (args) => {
|
|
await qb.add(
|
|
String(args.url),
|
|
args.category ? String(args.category) : "",
|
|
args.savepath ? String(args.savepath) : "",
|
|
args.paused === true || args.paused === "true",
|
|
);
|
|
return { added: String(args.url), category: args.category ? String(args.category) : null };
|
|
},
|
|
},
|
|
{
|
|
name: "qbittorrent_pause",
|
|
description: "Pause torrents — a pipe-separated hash list, or 'all' (the default).",
|
|
input: { hashes: { type: "string", description: "pipe-separated torrent hashes, or 'all' (default)" } },
|
|
run: async (args) => {
|
|
const hashes = args.hashes ? String(args.hashes) : "all";
|
|
await qb.pause(hashes);
|
|
return { paused: hashes };
|
|
},
|
|
},
|
|
{
|
|
name: "qbittorrent_resume",
|
|
description: "Resume torrents — a pipe-separated hash list, or 'all' (the default).",
|
|
input: { hashes: { type: "string", description: "pipe-separated torrent hashes, or 'all' (default)" } },
|
|
run: async (args) => {
|
|
const hashes = args.hashes ? String(args.hashes) : "all";
|
|
await qb.resume(hashes);
|
|
return { resumed: hashes };
|
|
},
|
|
},
|
|
{
|
|
name: "qbittorrent_delete",
|
|
description: "Remove torrents by hash, optionally deleting their files on disk.",
|
|
input: {
|
|
hashes: { type: "string", description: "pipe-separated torrent hashes, or 'all'" },
|
|
deleteFiles: { type: "boolean", description: "also delete downloaded files (default false)" },
|
|
},
|
|
run: async (args) => {
|
|
const hashes = String(args.hashes);
|
|
const deleteFiles = args.deleteFiles === true || args.deleteFiles === "true";
|
|
await qb.delete(hashes, deleteFiles);
|
|
return { deleted: hashes, deleteFiles };
|
|
},
|
|
},
|
|
];
|
|
}
|
|
|
|
// The tools exist only when qBittorrent is configured; without a URL and password, qbittorrent
|
|
// contributes none rather than failing the whole runtime.
|
|
registerModuleTools("qbittorrent", (env) => {
|
|
try {
|
|
return getQbittorrentTools(QbittorrentClient.fromEnv(env));
|
|
} catch {
|
|
return [];
|
|
}
|
|
});
|