- bookshelf: Servarr v1 fork on the radarr template (4 tools). - unifi: portainer-shaped tooled app (7 tools, 9 ports), settings-merged config. - fail2ban: host-level security module mirroring firewall (service + restart-on, no container); ban actions preserved as source ufw/iptables and FLAGGED to be rewritten nftables-native before it actually bans. - marrytts: manifest-only plain container (no tools), like resolv-conf. All typecheck against the built @novox/mesh-sdk; service images digest-pinned. Held from merge pending the hq initialization reconciliation. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
70 lines
2.5 KiB
TypeScript
70 lines
2.5 KiB
TypeScript
// bookshelf's tools — ported from the shared hal `arr` sdk (novox/hq ADR 0044), importing
|
|
// bookshelf's own client. They return structured data (not the pre-formatted text hal returned); the
|
|
// mesh serves them through the sdk's tool harness. Bookshelf has no calendar endpoint, so there is
|
|
// no calendar tool — matching hal, which excluded it from its calendar-capable apps.
|
|
|
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
|
import { BookshelfClient } from "../client.js";
|
|
|
|
export function getBookshelfTools(bookshelf: BookshelfClient): ToolDefinition[] {
|
|
return [
|
|
{
|
|
name: "bookshelf_status",
|
|
description: "Bookshelf status overview: version, book count, monitored count, queue size.",
|
|
input: {},
|
|
run: async () => {
|
|
const [status, content, queue] = await Promise.all([
|
|
bookshelf.getStatus(),
|
|
bookshelf.getContent(),
|
|
bookshelf.getQueue(),
|
|
]);
|
|
return {
|
|
app: status.appName,
|
|
version: status.version,
|
|
books: content.length,
|
|
monitored: content.filter((c) => c.monitored).length,
|
|
queue: queue.totalRecords,
|
|
};
|
|
},
|
|
},
|
|
{
|
|
name: "bookshelf_library",
|
|
description: "List books from the Bookshelf library.",
|
|
input: { limit: { type: "number", description: "max items to return (default 50)" } },
|
|
run: async (args) => {
|
|
const items = await bookshelf.getContent(args.limit ? Number(args.limit) : 50);
|
|
return { count: items.length, books: items };
|
|
},
|
|
},
|
|
{
|
|
name: "bookshelf_search",
|
|
description:
|
|
"Search the Bookshelf library for books by title or author (filters existing content, not indexers).",
|
|
input: { query: { type: "string", description: "the search term" } },
|
|
run: async (args) => {
|
|
const query = String(args.query);
|
|
return { query, results: await bookshelf.searchContent(query) };
|
|
},
|
|
},
|
|
{
|
|
name: "bookshelf_queue",
|
|
description: "Show the Bookshelf download queue — what is downloading and how far along.",
|
|
input: {},
|
|
run: async () => {
|
|
const queue = await bookshelf.getQueue();
|
|
return { count: queue.totalRecords, items: queue.items };
|
|
},
|
|
},
|
|
];
|
|
}
|
|
|
|
// The tools exist only when Bookshelf is configured; without a URL and key, bookshelf contributes
|
|
// none rather than failing the whole runtime.
|
|
registerModuleTools("bookshelf", (env) => {
|
|
try {
|
|
return getBookshelfTools(BookshelfClient.fromEnv(env));
|
|
} catch {
|
|
return [];
|
|
}
|
|
});
|