The manifest named /services/jackett/config and /var/lib/mesh/jackett/config.json — host
paths ADR 0112 takes out of definitions. The config dir is now a pathless directory
(${dir:config}) and the runtime's config and route binding live in a placed state dir, as
searxng does.
The image is pinned to v0.24.2627-ls34, the digest ace runs today; the old pin
(v0.24.2517-ls16) was older than the running version.
The runtime reached jackett at a fixed 127.0.0.1:9117; it now uses ${port:9117}, the
machine port the mesh actually assigned.
The tools never loaded: the client needed an API key nobody set. Like sonarr/radarr read
config.xml, it now reads APIKey from Jackett's own ServerConfig.json (the config dir is
already mounted read-only), so no secret goes into an assignment. jackett_indexers called
/api/v2.0/indexers, which is the web UI's endpoint and answers an API key with a redirect;
it now reads the Torznab t=indexers feed, and treats Torznab's 200-with-<error> as a
failure.
Verified: catalogue key tests (MESH_CATALOGUE set, not skipped); a throwaway container of
the pinned image on a fresh 0700 1000:1000 config dir serves its UI; the client discovers
the key from the generated ServerConfig.json, lists 617 indexers through the Torznab feed,
searches via /results, and a wrong key is refused; client.ts typechecks under --strict.
49 lines
1.9 KiB
TypeScript
49 lines
1.9 KiB
TypeScript
// jackett's tools — its own code (novox/hq ADR 0039), importing jackett's client. Jackett has
|
|
// nothing worth watching (an indexer proxy answers queries; it has no timeline of its own), so it
|
|
// is a tools-only module: no events entrypoint, no broker. What is useful is asking it things.
|
|
|
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
|
import { JackettClient } from "../client.js";
|
|
|
|
export function getJackettTools(jackett: JackettClient): ToolDefinition[] {
|
|
return [
|
|
{
|
|
name: "jackett_indexers",
|
|
description: "List the indexers Jackett proxies, with their type and site.",
|
|
input: { all: { type: "boolean", description: "include indexers not yet configured (default false)" } },
|
|
run: async (args) => {
|
|
const indexers = await jackett.getIndexers(!args.all);
|
|
return { count: indexers.length, indexers };
|
|
},
|
|
},
|
|
{
|
|
name: "jackett_search",
|
|
description: "Torznab search across Jackett's indexers, returning normalised torrent results.",
|
|
input: {
|
|
query: { type: "string", description: "the search query" },
|
|
indexer: { type: "string", description: 'an indexer id, or "all" to aggregate (default "all")' },
|
|
limit: { type: "number", description: "max results (default 25)" },
|
|
},
|
|
run: async (args) => {
|
|
const query = String(args.query);
|
|
const results = await jackett.search(
|
|
query,
|
|
args.indexer ? String(args.indexer) : "all",
|
|
args.limit ? Number(args.limit) : 25,
|
|
);
|
|
return { query, count: results.length, results };
|
|
},
|
|
},
|
|
];
|
|
}
|
|
|
|
// Only exposed when Jackett is configured; otherwise jackett contributes no tools rather than
|
|
// failing the whole runtime.
|
|
registerModuleTools("jackett", (env) => {
|
|
try {
|
|
return getJackettTools(JackettClient.fromEnv(env));
|
|
} catch {
|
|
return [];
|
|
}
|
|
});
|