Skip to main content
Version: 0.0.50

Host API Reference

Plugin code receives a global shisho: ShishoHostAPI. Calls are synchronous. There is no Node.js runtime, native fetch, Promise-based host API, or timer API. Use shisho.http.fetch for HTTP and shisho.sleep for blocking retry delays.

The complete TypeScript contract is in host-api.d.ts. Install it with npm install --save-dev @shisho/plugin-sdk.

Capability Summary

APIManifest Capability
dataDir, log, config, sleep, url, xml, html, yamlNone
httphttpAccess with an allowed domain
fs, archiveSandboxed paths supplied by the hook; broad access needs fileAccess
ffmpegffmpegAccess
shellshellAccess with the command in commands

The installation dialog shows capabilities supplied by the repository publisher. Shisho does not currently verify that this preview matches the downloaded plugin manifest, so administrators must still trust the publisher and review the plugin source. Plugin authors should declare only what the plugin needs in both places.

Persistent Data Directory

readonly shisho.dataDir: string;

dataDir is a persistent directory dedicated to the plugin and created when first accessed. It is always readable and writable through shisho.fs. Use it for plugin-managed caches or state that should survive restarts and updates. Do not use it as a substitute for Shisho configuration.

Logging

shisho.log.debug(msg: string): void;
shisho.log.info(msg: string): void;
shisho.log.warn(msg: string): void;
shisho.log.error(msg: string): void;

Messages are tagged with the plugin scope and ID. Keep credentials, tokens, book content, and other sensitive values out of logs.

Configuration

shisho.config.get(key: string): string | undefined;
shisho.config.getAll(): Record<string, string>;

Values come from the plugin's administrator-managed configSchema. Even boolean and numeric form values are exposed as strings, so parse them explicitly:

var maxResults = Number(shisho.config.get("maxResults") || "10");
var includeImages = shisho.config.get("includeImages") === "true";

A missing value returns undefined. Secret fields are still available to plugin code; masking applies only when values are displayed again.

Sleep

shisho.sleep(ms: number): void;

Blocks synchronously for a finite, non-negative number of milliseconds. 0 returns immediately. Missing, negative, NaN, or infinite values throw. Use it for retry backoff:

for (var attempt = 0; attempt < 3; attempt++) {
var response = shisho.http.fetch(url);
if (response.status !== 429) return response;
shisho.sleep(1000 * Math.pow(2, attempt));
}

A hook cancellation interrupts the wait.

HTTP

Requires httpAccess:

{
"capabilities": {
"httpAccess": {
"domains": ["api.example.com", "*.covers.example.com"]
}
}
}

Exact domains match only themselves. *.example.com matches the base domain and any depth of subdomain. Matching is case-insensitive. Standard HTTP and HTTPS ports are accepted; a non-standard port must be included in the manifest entry. Redirect destinations are checked against the same list.

Fetch

interface FetchOptions {
method?: string; // default GET
headers?: Record<string, string>;
body?: string;
}

shisho.http.fetch(url: string, options?: FetchOptions): FetchResponse;

Only http and https URLs are supported. The full response body is available through any response reader:

interface FetchResponse {
ok: boolean; // true for 2xx
status: number;
statusText: string;
headers: Record<string, string>; // lowercase keys
text(): string;
arrayBuffer(): ArrayBuffer;
json(): unknown;
}

json() throws for invalid JSON. HTTP error statuses return a response normally, so check ok or status yourself.

var response = shisho.http.fetch("https://api.example.com/books", {
method: "POST",
headers: {
"Authorization": "Bearer " + shisho.config.get("apiKey"),
"Content-Type": "application/json",
},
body: JSON.stringify({ query: "Dune" }),
});
if (!response.ok) {
throw new Error("Lookup failed with HTTP " + response.status);
}
var body = response.json();

A metadata result's coverUrl is also checked against the plugin's allowed HTTP domains when Shisho downloads it.

URL Utilities

Encode and Decode

shisho.url.encodeURIComponent(str: string): string;
shisho.url.decodeURIComponent(str: string): string;

These helpers use query-style escaping, so spaces encode as + and + decodes as a space.

Build Query Parameters

shisho.url.searchParams(params: Record<string, unknown>): string;

Keys are sorted for deterministic output. Array values produce repeated keys. null and undefined values are skipped.

shisho.url.searchParams({ q: "space opera", page: 2 });
// "page=2&q=space+opera"

shisho.url.searchParams({ tag: ["fiction", "audio"] });
// "tag=fiction&tag=audio"

Parse a URL

interface ParsedURL {
href: string;
protocol: string;
host: string;
hostname: string;
port: string;
pathname: string;
search: string;
hash: string;
username: string;
password: string;
query: Record<string, string | string[]>;
}

shisho.url.parse(url: string): ParsedURL;

protocol excludes the colon. search and hash include their leading punctuation when present. Repeated query keys become arrays.

Filesystem

shisho.fs.readFile(path: string): ArrayBuffer;
shisho.fs.readTextFile(path: string): string;
shisho.fs.writeFile(path: string, data: ArrayBuffer): void;
shisho.fs.writeTextFile(path: string, content: string): void;
shisho.fs.exists(path: string): boolean;
shisho.fs.mkdir(path: string): void;
shisho.fs.listDir(path: string): string[];
shisho.fs.tempDir(): string;

readTextFile and writeTextFile use UTF-8 text. mkdir creates parents. listDir returns entry names, not full paths. tempDir is created on first use and cleaned up after the hook returns.

Path Access

PathReadWrite
Plugin artifact directoryYesYes
shisho.dataDirYesYes
shisho.fs.tempDir()YesYes
Input converter sourcePath and targetDirYesYes
File parser filePathYesYes
Output generator sourcePath and destPathYesYes
Metadata enricher target filePathYesNo
Other pathsWith fileAccess: read or readwriteWith fileAccess: readwrite

An enricher's scoped target access covers exactly that file, not siblings. Declare broad read access to inspect sidecars or neighboring assets. Access violations and failed file operations throw.

ZIP Archives

Archive methods use the same filesystem path checks as shisho.fs:

shisho.archive.extractZip(archivePath: string, destDir: string): void;
shisho.archive.createZip(srcDir: string, destPath: string): void;
shisho.archive.readZipEntry(
archivePath: string,
entryPath: string,
): ArrayBuffer;
shisho.archive.listZipEntries(archivePath: string): string[];

extractZip needs read access to the archive and write access to the destination. createZip needs read access to the source and write access to the destination. Entry paths use the names stored in the ZIP.

XML

interface XMLElement {
tag: string;
namespace: string;
text: string;
attributes: Record<string, string>;
children: XMLElement[];
}

shisho.xml.parse(content: string): XMLElement;
shisho.xml.querySelector(
doc: XMLElement,
selector: string,
namespaces?: Record<string, string>,
): XMLElement | null;
shisho.xml.querySelectorAll(
doc: XMLElement,
selector: string,
namespaces?: Record<string, string>,
): XMLElement[];

XML selectors match a local tag name such as title, or a namespace-qualified form such as dc|title with a prefix map. Queries search the supplied element and its descendants.

var root = shisho.xml.parse(xmlText);
var title = shisho.xml.querySelector(root, "dc|title", {
dc: "http://purl.org/dc/elements/1.1/",
});
var value = title ? title.text.trim() : "";

text is direct character data for that element. Walk children when descendant text also matters.

HTML

interface HtmlElement {
tag: string;
attributes: Record<string, string>;
text: string;
innerHTML: string;
children: HtmlElement[];
}

shisho.html.parse(html: string): HtmlElement;
shisho.html.querySelector(
doc: HtmlElement,
selector: string,
): HtmlElement | null;
shisho.html.querySelectorAll(
doc: HtmlElement,
selector: string,
): HtmlElement[];

HTML queries support CSS selectors and can start from the parsed document or a previous query result. text includes descendant text; innerHTML preserves the element's inner markup.

var document = shisho.html.parse(response.text());
var node = shisho.html.querySelector(
document,
'script[type="application/ld+json"]',
);
var jsonLd = node ? JSON.parse(node.text) : null;

Use this API instead of regular expressions for HTML scraping.

YAML

shisho.yaml.parse(content: string): unknown;
shisho.yaml.stringify(value: unknown): string;

No capability is required. Parsing returns plain JavaScript objects, arrays, primitives, or null; invalid YAML throws. Mapping keys are exposed as strings. Stringification returns YAML text.

var value = shisho.yaml.parse("title: My Book\npages: 100\n");
var output = shisho.yaml.stringify({ title: value.title, valid: true });

FFmpeg

All methods require ffmpegAccess.

Transcode

interface TranscodeResult {
exitCode: number;
stdout: string;
stderr: string;
}

shisho.ffmpeg.transcode(args: string[]): TranscodeResult;

Arguments are passed directly to FFmpeg. Network protocols are not enabled. A process that runs but exits unsuccessfully returns a nonzero exitCode; a failure to start throws.

var result = shisho.ffmpeg.transcode([
"-i", inputPath,
"-c:a", "aac",
outputPath,
]);
if (result.exitCode !== 0) throw new Error(result.stderr);

Probe

interface ProbeResult {
format: ProbeFormat;
streams: ProbeStream[];
chapters: ProbeChapter[];
stderr: string;
parseError: string;
}

shisho.ffmpeg.probe(args: string[]): ProbeResult;

The host adds JSON output and format, stream, and chapter requests. Common format values include filename, duration, size, bitrate, and tags. Stream entries include codec type plus optional video, audio, timing, disposition, and tag properties. Chapter entries include ID, time base, start, end, and optional tags. Check parseError before relying on parsed fields. See the SDK declaration for the full property list.

Version

interface VersionResult {
version: string;
configuration: string[];
libraries: Record<string, string>;
}

shisho.ffmpeg.version(): VersionResult;

Use this to check whether a required codec or build option is available before starting work.

Shell Commands

interface ExecResult {
exitCode: number;
stdout: string;
stderr: string;
}

shisho.shell.exec(command: string, args: string[]): ExecResult;

Requires the exact command in shellAccess.commands:

{
"capabilities": {
"shellAccess": {
"commands": ["example-convert"]
}
}
}

The command receives the argument array directly. No command shell interprets pipes, redirects, substitutions, or quoting. A nonzero command exit returns an ExecResult; a disallowed command or failure to start throws.

var result = shisho.shell.exec("example-convert", [inputPath, outputPath]);
if (result.exitCode !== 0) throw new Error(result.stderr);

Prefer a narrower host API such as ffmpeg, archive, or an in-memory parser when it can perform the same task.