Files
jerapah-flow/AGENTS.md
T
nsrb 3f0774813d refactor(config): remove legacy config reference handling and streamline YAML management
- Deleted legacy config reference handling functions and related migration scripts to simplify the codebase.
- Updated documentation to reflect the removal of legacy YAML paths and configurations.
- Adjusted existing YAML management processes to ensure compatibility with the new mustache-style syntax.
- Removed tests related to legacy config references, focusing on current functionality and ensuring clarity in the testing suite.
2026-08-30 15:37:58 +07:00

5.7 KiB
Raw Blame History

JerapahFlow plugins

This file tells agents how to add a user plugin. Do not put personal or site-specific scripts in packages/server/scripts/ (core, read-only). Do not put them in examples/plugins/ (shipped examples only).

Workflows (instance data)

Live workflows are not product source. They live under packages/server/data/workflows/<owner>/ (gitignored; override with JFLOW_WORKFLOWS_DIR).

Kind In git? Path
Live / personal YAML No packages/server/data/workflows/<owner>/
Example presets Yes examples/workflows/*.yaml (copy into editor only; runner does not load them)

Do not add personal YAML under packages/server/, examples/workflows/, or packages/server/data/workflows/. Prefer owner local. Example presets must use core scripts only (no plugin/… that requires install).

Where things live

Kind YAML script Editable Path
Core fetch-http.js No packages/server/scripts/
User plugin plugin/<id> Yes plugins/<id>/
Example source install → plugin/<id> After install examples/plugins/<id>/

Runtime load path is repo-root plugins/ (PLUGINS_DIR in packages/server/paths.js). Override with JFLOW_PLUGINS_DIR only in tests.

Plugins are outside the pnpm workspace (packages/*). Do not add plugins/* to pnpm-workspace.yaml.

When to create a plugin

Create a user plugin when the script is:

  • Site-specific (LAN IPs, personal modems, private APIs)
  • A fork of a core script the user wants to edit
  • Anything that should stay in git but not ship as core

Use native fetch (not $axios) when the URL is RFC1918 / WG (10.x, 192.168.x, …). $axios is screened and blocks those hosts.

Create a new plugin

  1. Pick an id: lowercase letters, numbers, hyphens; max 64 chars; ^[a-z0-9]+(?:-[a-z0-9]+)*$.
  2. Folder name must equal manifest id. Example: plugins/joplin-api.
  3. Id must not collide with a core script basename (fetch-http, ntfy, …).
  4. Create three files (see below). Prefer main: "script.js".
  5. Point workflows at plugin/<id>.
  6. Restart the runner (pnpm dev / drain-restart under pnpm dev:pm2) so the plugin is picked up.

Copy the layout from plugins/joplin-api, plugins/send-sms, or examples/plugins/get-current-time.

plugins/<id>/jerapah-plugin.json

{
  "id": "my-plugin",
  "name": "My plugin",
  "version": "0.1.0",
  "jerapah": ">=0.1.0 <1.0.0",
  "main": "script.js",
  "description": "One-line description"
}
  • version must be semver (0.1.0).
  • jerapah must match the app (0.1.0 in root package.json). Use ">=0.1.0 <1.0.0" unless you know otherwise.
  • main is a relative path; no .., not absolute.

plugins/<id>/package.json

{
  "name": "jflow-plugin-my-plugin",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "description": "JerapahFlow plugin: …"
}

Add dependencies only if the script require()s extra npm packages. Then:

pnpm install --dir plugins/<id> --ignore-scripts --prefer-offline

plugins/*/node_modules/ is gitignored. Host-allowlisted modules (axios, jsonata, …) come from the server; extra deps resolve from the plugin directory.

plugins/<id>/script.js

Must export default a function. The sandbox rewrites ESM import/export default to CJS.

function passContext(ctx) {
  if (ctx?.context != null && typeof ctx.context === "object" && !Array.isArray(ctx.context)) {
    return { ...ctx.context };
  }
  return {};
}

async function myPlugin(ctx) {
  const output = { ok: true };
  return { output, context: { ...passContext(ctx), ...output } };
}

myPlugin.meta = {
  description: "What this step does",
  previewConfigKey: "url",
  tags: ["HTTP"],
  config: {},
  input: {},
  output: { ok: { type: "boolean" } },
  context: { ok: { type: "boolean" } },
  example: { data: {}, config: {} },
};

export default myPlugin;

Return { output, context?, skipRemaining? }. Do not return ctx. Mutations of ctx.data / ctx.context are discarded unless returned.

Field Meaning
ctx.data Step input (trigger payload, previous output, or DAG needs)
ctx.context Run clipboard (plain object)
ctx.config YAML config (mustache refs like {{ secrets.name }} already resolved)
output Next step’s data
context Next clipboard. Omit to keep incoming

fn.meta must be JSON-serializable (UI + dry-run). Include config / input / output field schemas and an example.

Sandbox globals (do not import these)

Injected: log (pino), console, fetch, require, $axios, $kv, $fingerprint, $secrets, $vars, $responses, $workflows.

  • Use log.info({ … }, "my-plugin: …") — log is not an import.
  • require("axios") is the screened $axios (RFC1918 blocked). Prefer fetch for LAN.
  • Plugin require("some-npm-dep") uses the plugin’s node_modules.

Workflow YAML

scripts:
  - name: Notify to channel
    script: plugin/my-plugin
    config:
      url: http://10.8.0.6:3030/notes
      token: "{{ secrets.joplin_api_token }}"

Optional name is the display title in the editor and graph (falls back to the script filename). Canonical ref is plugin/<id> (.js suffix is optional).

Do not

  • Add user plugins under packages/server/scripts/ or examples/plugins/.
  • Use an id that matches a core script file (ntfy, jsonata, …).
  • Mismatch folder name and jerapah-plugin.json id (plugin is disabled).
  • Commit plugins/.staging-* or plugins/*/node_modules/.
  • Put secrets in script.js; use YAML {{ secrets.name }} / {{ vars.name }} (quote if the value starts with {).