JavaScript Runtime

The JavaScript runtime is the quickest way to implement capability methods, and it can power far more than scraping flows. The manifest invoke field points to an async function attached to globalThis, and host capabilities are called through Ting.host.

Entry and Exported Functions

A JavaScript plugin entry is the script named by entry_point. When the backend invokes a capability, it runs globalThis[invoke](args); args is a JSON object and includes _context.

runtime: javascript
entry_point: plugin.js
capabilities:
  - id: metadata.search
    kind: metadata_provider
    invoke: searchMetadata
permissions:
  - type: network_access
    value: api.example.com
async function searchMetadata(args) {
  const response = await fetch(
    "https://api.example.com/search?q=" + encodeURIComponent(args.title)
  );
  const data = await response.json();

  return {
    items: data.items.map((item) => ({
      title: item.title,
      author: item.author,
      narrator: item.narrator,
      cover_url: item.cover,
      description: item.description
    })),
    total: data.total,
    page: args.page ?? 1,
    page_size: args.page_size ?? 20
  };
}

globalThis.searchMetadata = searchMetadata;

Network, npm, and Config

fetch can only access domains declared with network_access in the manifest. npm packages must be listed in npm_dependencies; runtime require can only load declared packages or relative modules inside the plugin directory.

npm_dependencies:
  cheerio: "^1.0.0"
  dayjs: "^1.11.0"

Read configuration from Ting.config. Sensitive fields saved by clients or the admin UI should be marked with x-encrypted, encrypted, format: password, or format: secret in config_schema; see “Plugin Configuration: config_schema” in this guide for the full pattern.

Invoking Capabilities and HostGateway

The JavaScript runtime provides Ting.host.getContext() and Ting.host.invoke(method, params). HostGateway validates plugin permissions and the current user context for every call.

const cheerio = Ting.npm.require("cheerio");

async function openAssistant(args) {
  const context = Ting.host.getContext();
  const recent = await Ting.host.invoke("progress.recent", { limit: 10 });
  const cached = await Ting.host.invoke("cache.get", { key: "assistant-state" });

  return {
    context,
    recent,
    state: cached ?? { collapsed: false }
  };
}

globalThis.openAssistant = openAssistant;

Good JS Capability Examples

  • metadata_provider: call a website or API and normalize title, author, narrator, cover, and description fields.
  • plugin_store: fetch a remote plugin index and return { plugins: [...] }.
  • tool_provider: expose lightweight tools for assistants, other plugins, or clients.
  • ui_extension: handle schema form submissions or web_container capability requests.

Packaging

A JavaScript plugin usually contains plugin.yml, the entry script, optional ui/ static assets, and local modules. Use trpack to validate permissions, dependencies, and asset paths before packaging.

trpack validate my-plugin
trpack build my-plugin --output dist/my-plugin.tr
trpack verify dist/my-plugin.tr