runScript's return shape — deliberately a loose bag of fields (mirrors legacy's own
run_script subs, each of which returns a different ad-hoc hash with no shared shape across the
three real Scripts plugins) rather than one rigid interface every script must conform to.
total/id/error are SourceFinder.pm's own fields; modified/updates are
nHentaiSourceConverter.pm's.
total: number
1 if a match was found, 0 otherwise (urlfinder).
id: string
The matching archive's ID, when total is 1 (urlfinder).
error: PluginError
modified: number
Count of archives this call changed (nhsrcconv).
updates: ScriptArchiveTags[]
This call's intended tag rewrites — the host applies each one (same shape as
ScriptHostArgs.archives) after runScript returns; a plugin never writes storage
directly (nhsrcconv).
categories_to_create: { name: string; archive_ids: string[]; }[]
One static Category to create per discovered subfolder — the host creates each one after
runScript returns, same read-compute-return-write-to-host split as updates
(foldertocat).
delete_old_categories: boolean
Whether to delete every existing static (non-dynamic) category before creating the new ones
— echoes back this call's own customargs[0], since the host applies the deletion itself
(foldertocat).
elapsed_ms: number
Wall-clock milliseconds runScript itself spent walking the directory tree and computing
groupings — reported back for a direct comparison against the equivalent native Rust endpoint
(POST /database/scripts/subfolders-to-categories's own elapsed_ms), which does the
identical job. Not a generic field every script plugin is expected to fill in (foldertocat
only).