fyflow-plugin-sdk

中文文档
0.7.0发布于 2026/11/04MITTypeScriptES Module零依赖

fyflow-app插件的类型和definePlugin:节点类型,工人,门类型和资源组

中文使用文档
文档目录12 节
  1. 合同
  2. 插件目录
  3. 对等范围
  4. 图标
  5. 工人
  6. 工人的生命周期
  7. 插件的数据目录
  8. 插件的状态
  9. 这个 disk , cpu 和 gpu 小组
  10. 在工人生活时举行小组活动
  11. 配置文件和秘密
  12. 版本控制

fyflow-插件-sdk

类型和 definePlugin 写作 插件 对于fyflow-app, visual builder和Host为 fyflow-编排 流。插件提供节点类型(与运行其任务的工人),门类型,资源组类型和资源组。

npm install --save-peer fyflow-plugin-sdk
npm install --save-dev fyflow-orchestration   # its types, for type checking; the Host brings its own at run time

合同

插件是一个模块,其默认导出是一个普通对象。 definePlugin 返回参数不变;它只提供类型检查。依赖SDK作为对等体,不要捆绑它:主机在运行时提供它(见下文),并且您自己的副本只会报告它自己的 SDK_VERSION而不是宿主的。

import { definePlugin } from "fyflow-plugin-sdk";

export default definePlugin({
  id: "acme.signer", // stable and unique across Plugins
  version: "1.0.0",
  base: import.meta.url, // what Worker `url`s resolve against
  // Set on the Host's Profiles, once for every Node Type that takes them (see Profiles and secrets).
  profile: {
    schema: {
      type: "object",
      properties: { key: { type: "string", writeOnly: true } }, // a secret
    },
    startup: ["key"], // read once, when a Worker starts
  },
  workers: {
    sign: {
      url: "./sign.worker.js",
      settingsSchema: { type: "object", properties: { field: { type: "string" } } },
    },
  },
  nodeTypes: {
    Sign: {
      worker: "sign",
      inline: true,
      profile: ["key"], // the fields of `profile` its Workers get
      groups: ["acme.signing"],
      icon: "signature", // from the app's icon set, below
      description: "Signs a field with HMAC.",
    },
  },
  groups: { "acme.signing": { type: "ConcurrentLimitGroup", config: { limit: 2 } } },
  gateTypes: {
    threshold: {
      routes: ["below", "at-or-above"],
      defaultRoute: "below",
      fn: ({ key, settings }) => (Number(key) >= Number(settings.at) ? "at-or-above" : "below"),
    },
  },
});
场它是什么?
profile插件的Profile字段,从0.5.0开始:一个对象 schema, defaults 对于没有配置文件集的字段, startup 它的worker在启动时读取的字段,并且只有Profile可以设置每个任务字段(profileOnly). 看到 配置文件和秘密。
workers工作模块按键。Worker模块的默认导出是一个类:它的构造函数获取Node Type接受的启动字段,以及 run({ settings, input }, context) 执行一项任务 (请参见 工人)。一名工人宣布不 configSchema 从0.5.0开始:它的配置是插件的 profile。模式是fyflow子集中的JSON模式。一个工人的 outputSchema,什么 run 返回(被每个版本的SDK接受,作为fyflow自己的Worker条目)是一个人填写的表单,用于标记失败的任务完成,以及编辑器在任何运行之前为其节点边缘和门的条件建议的路径。给 inputSchema 和 outputSchema 每个字段的描述和一个 examples list:一个节点的面板显示了它的Worker接收和返回的内容,并检查到达节点的内容与接收的内容。在任何运行之前,首先运行 outputSchema的 examples 是尝试并检查下一个节点的转换的示例; 如果没有一个,编辑器将从模式中生成更粗略的值。
nodeTypes节点类型的名称,每个命名这个插件的一个 workers,使用fyflow的声明字段(inline, pool, settings 违约, retryPolicy, groups;否 config 自0.5.0起),自0.2.0起应用程序如何显示它 (可选 icon 一行字 description),并且自0.5.0起 profile 它占用的域。
dataDirtrue 从0.6.0开始,要求主机提供插件自己的数据目录。见 插件的数据目录。
status一个函数说插件是如何的,显示在插件页面上的卡片上,自0.6.0以来。见 插件的状态。
gateTypes按名称的门类型: 它们的路由、默认路由、设置架构和决策 fn。
groupTypes按名称划分资源组类型:a create(config, key) 用于调度程序限制的工厂,以及 configSchema。
groupsfyflow内置类型的按键资源组(ConcurrentLimitGroup, RateLimitGroup, KeyedRateLimitGroup) 或这个插件自己的。节点类型可以使用其自己的插件的组,并且内置插件的 disk, cpu 和 gpu 组(下图)。

不要将设置命名为每个JavaScript对象的成员 (constructor, toString, valueOf, hasOwnProperty, __proto__). 主机将流定义的形状交给代理,作为一个JSON模式,其中包括您的 settingsSchema,并且一些JSON模式验证器误读了这样的属性: 他们拒绝或抛出有效的流。

插件提供的每个名称(节点类型,门类型,资源组类型,资源组)必须在主机加载的插件中是唯一的:主机首先看到的插件保留一个名称,之后重复的插件将被跳过。

插件目录

主机从它的插件目录(<dataDir>/plugins 除非 pluginDir, FYFLOW_PLUGIN_DIR 或 --plugin-dir 否则):

  • 一个 文件夹 持有 plugin.ts 或 plugin.js,或包裹(package.json 及其条目),因为主机布局了从应用程序安装的插件;
  • 一个 包 命名 fyflow-plugin-* 或 @scope/fyflow-plugin-* 在目录的 node_modules,用npm安装。一个包必须附带JavaScript: Node不会剥离TypeScript node_modules。

插件自己的导入从插件目录的 node_modules,所以 npm install 您在那里的其他依赖项(或捆绑它们)。SDK是主机提供的:在服务器上, npm install fyflow-plugin-sdk 在那里 (npm也安装它作为你的包的同行); 桌面应用程序把它自己在那里。更改在主机被告知时生效 重新加载。

对等范围

将插件使用的SDK版本声明为对等依赖:

{ "peerDependencies": { "fyflow-plugin-sdk": "^0.5.0" } }

主机实现其SDK版本(SDK_VERSION,在此导出)以及与之兼容的早期版本(SDK_COMPATIBLE)。它跳过,原因是,一个插件的范围不包括它们。0.5.0更改了配置文件,因此它与早期版本不兼容: 跳过为0.4或之前编写的插件,直到升级为止,因为 UPGRADING.md 描述.0.6.0和0.7.0仅添加到0.5.0,因此0.7上的主机加载一个声明插件 ^0.5.0 或 ^0.6.0. 从0.5开始,只对主机在运行时必须做的事情引发范围:请求数据目录的插件说 ^0.6.0,因为0.5上的主机会加载它并给它none。

图标

节点类型的 icon 命名应用程序的图标之一,从 Lucide: activity, archive, bell, bot, box, braces, brain, calculator, calendar, camera, chart-bar, check, clock, cloud, code, cpu, credit-card, database, download, file, file-text, filter, flag, folder, git-branch, globe, hash, image, key, link, list, lock, mail, map-pin, message-square, package, pen-tool, play, plug, repeat, search, send, server, settings, shield, shopping-cart, signature, sparkles, table, tag, terminal, timer, trash, upload, user, users, wand-sparkles, webhook, zap。集合之外的名称不是错误: 节点类型显示其种类的图标,插件页面显示警告。

工人

Worker模块的默认导出是一个类。主机用它的Node Type从配置文件中获取的启动字段来构建每个实例(池的每个worker:一个线程,或者在内联池中使用主机自己线程上的对象),并跨任务重用它,因此请将每个任务状态从字段中删除。 run 获取任务的信封(节点的信封) settings 和Task输入)和上下文;它返回的是Task的结果,而throw会带着错误消息使Task失败。自0.3.0以来,SDK类型的所有:

import type { TaskEnvelope, Worker, WorkerContext } from "fyflow-plugin-sdk";

type Config = { key?: string };
type Settings = { field?: string };

export default class Sign implements Worker<Settings> {
  readonly #config: Config;

  constructor(config: Config) {
    this.#config = config;
  }

  async run({ settings, input }: TaskEnvelope<Settings>, context: WorkerContext) {
    if (this.#config.key === undefined) throw new Error("No key: set `key` on this Node's Profile");
    context.sendProgress(0, "signing"); // a fraction from 0 to 1, not a percentage
    context.signal.throwIfAborted(); // aborted when the Task is Cancelled
    const value = settings.field === undefined ? input : (input as Record<string, unknown>)[settings.field];
    return { signed: await hmac(this.#config.key, value) }; // hmac: your own signing function
  }
}

WorkerClass<Config, Settings, Input, Output> 类型类本身,包括构造函数。检查 context.signal 步骤之间,或将其传递给任何需要一个 (fetch,streams): 立即停止的Worker会更快地释放其插槽,并且在取消后返回的内容将被丢弃。使用 import type因此,Worker模块在运行时不需要来自SDK的任何东西。上下文提供了Host显示的内容(进度)和Cancel;fyflow的调度程序传递更多信息,但SDK只承诺传递这些信息。

工人的生命周期

从0.5.0开始,SDK还承诺了任务之间的实例发生的情况,对于拥有长期存在的东西(子进程,连接,加载的模型)的Worker:

  1. 建造的: new Worker(config, pool),在那里 config 它的创业领域是什么 pool 一个 PoolContext。
  2. setup(),如果它有一个: 一次,在它的第一个任务之前等待。在这里启动它拥有的东西,而不是在构造函数中。
  3. run,用于任意数量的任务。
  4. teardown(),如果有的话:一次,当池退出实例时。那是当它已经闲置过了它的池子 idleTimeout (池每5秒检查一次,所以最多5秒后),当它要求被替换时,以及当它的运行时关闭时: 主机停止,或者在一次重新加载后,一旦被替换的运行时上一次运行结束。
import type { PoolContext, Worker } from "fyflow-plugin-sdk";

export default class Thumbnails implements Worker {
  readonly #pool: PoolContext | undefined;
  #service: Service | undefined; // Service, start: your own

  constructor(_config: object, pool?: PoolContext) {
    this.#pool = pool;
  }

  async setup() {
    // Port 0: another copy of this Worker may be running (see below).
    this.#service = await start({ port: 0, onExit: (error) => this.#pool?.terminateWithError(error, { canRestart: true }) });
  }

  async teardown() {
    await this.#service?.stop(); // undefined when setup() did not get that far
  }

  async run() {
    return this.#service?.thumbnail();
  }
}

依靠什么,不依靠什么:

  • 一个 setup() ,抛出 每个给定实例的Task都失败,包括它的消息和它的 teardown() 立即运行。它不会在该实例上再次尝试: 只有在重新加载时退役这一池时,或者一旦没有任务达到它的 idleTimeout这是源源不断的任务所不允许的。所以一个 setup() 这可能会因为一个传递的原因(端口被占用,服务仍在启动)而失败,应该在抛出之前在自身内部重试 teardown() 必须应付一个 setup() 那永远不会结束。
  • pool.terminateWithError(error, { canRestart: true }) 要求池替换实例,说它拥有的进程死亡后。它一次返回,从不抛出,所以从回调或计时器调用它。实例的 teardown() 运行时,其仍在运行的任务回到队列中,在新实例上再次运行,并且该实例已构建和设置。与 canRestart: false 这些任务反而失败了。池仅多次替换实例 (3,fyflue-scheduler的 maxWorkerRestarts); 之后,它不再构建,它的任务等待直到重新加载。因此,询问实例何时中断,而不是因为Task失败:throw from run 的。
  • 永远不要假设一个副本。 每个概要文件都有它自己的池,一个池可以容纳几个实例(最多为它的实例) maxThreads),并且在重新加载之后,被替换的运行时的实例保持运行它的运行,而新的运行时设置它自己的。绑定端口0,而不是固定端口,并保持Worker之外的任何共享。
  • idleTimeout 默认为5秒。对于设置成本高昂的工作人员,请给出其节点类型的 pool 长一点的(pool: { idleTimeout: 60_000 }); 配置文件可以为该节点类型覆盖它。 0 保留实例,直到其运行时关闭。

插件的数据目录

一个保持状态超过一个主机进程的插件(它的服务需要一个缓存,一个禁止等待结束的记录)集 dataDir: true (自0.6.0起)。然后主机给它一个自己的文件夹, plugin-data/<plugin id>/ 在主机的data目录下,并在构建插件的Workers之前创建它。插件的每个Worker在它的配置中找到它的绝对路径,如 host.dataDir,在内联池或线程池中:

import { join } from "node:path";
import type { HostConfig, Worker } from "fyflow-plugin-sdk";

type Config = { host: HostConfig; ttlHours?: number };

export default class Lookup implements Worker {
  readonly #cache: string;

  constructor(config: Config) {
    this.#cache = join(config.host.dataDir, "answers.sqlite");
  }
  // …
}
  • 该文件夹保存在重新加载、更新和卸载然后重新安装之间。应用程序永远不会删除它:要重新开始,请在主机停止时手动删除它。
  • 因此,路径在配置中有一个地方,一个请求一个的插件可能没有名为的启动字段 host;Host跳过了它,并给出了理由。一个插件,不要求得到没有文件夹和没有 host 在它的配置中,以及它自己的 host 领域是它自己的。
  • 几个实例,并在运行时重新加载替换,可以一次运行: 写入文件夹作为一个共享的地方 (一个SQLite数据库,一个文件被原子替换),而不是作为一个工人自己的。

插件的状态

一个在任务(连接,会话,速率限制后的队列)之间保持运行的插件可以说明它是如何运行的 status() (自0.6.0版起)。主持人在其插件页面上的插件卡上显示答案,在页面打开时每5秒再次询问一次,并将其提供给代理。一个没有插件 status() 显示无。

import { definePlugin } from "fyflow-plugin-sdk";
import { session } from "./session.ts"; // your own module, shared with the inline Workers

export default definePlugin({
  // …
  status: () =>
    session.blockedUntil === null
      ? { level: "ok", summary: `Logged in · ${session.queued} queued` }
      : {
          level: "warning",
          summary: "Blocked after a ban",
          details: [{ label: "Blocked until", value: session.blockedUntil.toLocaleTimeString() }],
        },
});
  • level 是 ok, info, warning 或 error; summary 是一行; details 是可选的标签和值对。它是数据:主机绘制它,所以插件仍然没有提供UI代码。
  • 它运行在主机自己的线程上,主机加载了插件的模块。它看到该模块与共享的内容 inline 工人,而不是线程工人持有的东西。
  • 一秒钟内回答。一个 status() 扔,拒绝,用别的东西回答,或者花更长的时间显示 error 有原因的状态。当一个答案仍然悬而未决时,主机不再询问。
  • 它是手握的 { dataDir } 当插件请求数据目录时,并且 {} 否则。
  • 主机从答案中删除其秘密,就像从错误中删除一样,但不要将其放在那里。

这个 disk, cpu 和 gpu 小组

主机的内置插件声明了三个资源组,任何插件的节点类型都可以命名,每个资源组都有一个主机可以在其资源组页面上覆盖的并发限制:

  • disk (出口 DISK_GROUP 从0.3.0开始),在读取或写入主机磁盘的任务上。它是4。当您的任务执行繁重的磁盘工作 (复制,转码,索引) 时,将其命名,以便它们与主机自己的文件系统节点类型共享一个预算,而不是在它们旁边打转磁盘。
  • cpu (出口 CPU_GROUP 从0.4.0开始),用于使处理器忙碌的任务(散列,编码,图像工作)。这是主机的逻辑处理器计数,因此来自每个插件的任务加在一起不会占用更多的处理器。
  • gpu (出口 GPU_GROUP 从0.7.0开始),在使用GPU (推理,渲染,GPU编码) 的任务上。它是1,因为主机不能计算它的gpu; 一个人在一台机器上提出更多。它只计数: 一个任务不告诉它持有哪个GPU,所以当限制高于1时,你的插件选择每个任务的设备 (从其设置,或通过跟踪本身)。

同时做这两件事的Task:

import { CPU_GROUP, DISK_GROUP, definePlugin } from "fyflow-plugin-sdk";

export default definePlugin({
  // …
  nodeTypes: { Transcode: { worker: "transcode", groups: [DISK_GROUP, CPU_GROUP] } },
});

其他插件的组保持遥不可及,所以禁用一个插件永远不会打破另一个。键是值,因此导入它们的插件在运行时需要SDK; 编写 "disk", "cpu" 或 "gpu" 而是在具有组的任何主机上工作。SDK 0.4.0之前的主机没有 cpu 并跳过一个插件命名它,所以一个插件,它的名字说 ^0.4.0 在其对等范围内;这同样适用于 gpu 和 ^0.7.0。

在工人生活时举行小组活动

groups 保存一个任务的每个组的一个单位,从开始到结束。一个Worker在它的生命周期内持有一些东西,比如一个在任务之间加载在GPU内存中的模型,在它的Node Type中命名这个组 pool.residentGroups 相反 (自0.7.0起): 池在构建实例时获取单元,并在实例退役时将其返回 (空闲超过其 idleTimeout(重新加载或主机停止)。另一个池只在它需要的东西还合适的时候才构建实例,它的任务同时等待:直到持有者的 idleTimeout 在持有者完成最后一个任务后5秒。因此,持有组驻留的节点类型需要一个 idleTimeout 以上0;主机拒绝0个插件或配置文件中的一个。

import { definePlugin, GPU_GROUP } from "fyflow-plugin-sdk";

export default definePlugin({
  // …
  groups: { "imagegen.vram": { type: "ConcurrentLimitGroup", config: { limit: 24 } } }, // GB
  workers: { model: { url: "./model.worker.ts" } },
  nodeTypes: {
    // Keeps its model loaded for 10 minutes after its last Task, holding the GPU and 16 GB of its memory meanwhile.
    Generate: {
      worker: "model",
      pool: { maxThreads: 1, idleTimeout: 600_000, residentGroups: { [GPU_GROUP]: 1, "imagegen.vram": 16 } },
    },
  },
});

residentGroups 是一个键列表 (每个键的一个单元) 或每个键包含多少个单元,并且可以命名相同的组 groups 五月: 插件自己的和 disk, cpu 和 gpu。主机拒绝命名任何其他插件。每个实例都有单位,所以一个带有 maxThreads: 2 有两个;并且每个概要文件都有自己的节点类型池,因此在两个概要文件下运行的任务的节点类型为每个保存单元。保持 maxThreads 对于持有整个设备的Worker,以及在多个配置文件下运行它的主机,需要的单元数量与一次有活动池的数量一样多。

配置文件和秘密

插件的配置存在于主机上,而不是流中: A 配置文件 是一个命名的配置与部分的每个插件,编辑在主机的用户界面的形式,从插件的 profile.schema. 每个主机都有一个默认配置文件。声明您的字段一次,在 profile,并在每个节点类型上列出它需要的节点: 几个节点类型需要的字段设置一次,用于所有节点类型。没有配置文件集的字段是其 defaults 价值。

一个 启动字段 (列在 profile.startup) 在Worker启动时读取: 节点类型采用的启动字段是其Worker的配置,构造函数的参数,并且更改在主机的下一次重新加载时生效。使用一个工人设置一次: 一个连接,一个加载的模型。

每个其他字段都是一个 每个任务字段:主机在每个任务被分派时读取它,并将节点类型占用的任务交给主机 run 作为 settings.profile ({} (当它不需要时),因此更改到达下一个任务时不需要重新加载。输入 TaskEnvelope<Settings, Input, ProfileFields>. 你的 settingsSchema 不能命名 profile. 列表中 profile.profileOnly 每个任务的字段只有一个配置文件可以设置: 一个授权,如文件夹任务可以达到,旁边的字段流可以设置为缩小它,您的工作人员检查授权。每个任务字段永远不会达到运行的记录,但什么 run 退货是记录的,所以不要退货。

标记的属性 profile.schema writeOnly: true 把它变成 秘密: 主机永远不会再次显示它,仅显示它是否已设置,并从错误和日志中编辑其值。只有架构的属性可以是机密。

版本控制

这个包遵循semver。 SDK_VERSION 始终等于包版本。

版本与发布记录

fyflow-plugin-sdk 共发布 6 个版本,首次发布于 2026/10/24;过去 12 个月发布了 6 个版本。

版本发布日期距今
0.7.0latest2026/11/04今天
0.6.02026/11/012 天前
0.5.02026/10/286 天前
0.3.02026/10/269 天前
0.2.02026/10/2410 天前
0.0.0-stage2026/10/2410 天前

关于 fyflow-plugin-sdk 的常见问题

fyflow-plugin-sdk 是什么?

fyflow-app插件的类型和definePlugin:节点类型,工人,门类型和资源组

如何安装 fyflow-plugin-sdk?

在项目目录运行 npm install fyflow-plugin-sdk,或使用 pnpm add / yarn add / bun add。

fyflow-plugin-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

fyflow-plugin-sdk 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

fyflow-plugin-sdk 可以商用吗?

fyflow-plugin-sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

fyflow-plugin-sdk 还在维护吗?

最新版本 0.7.0 发布于 2026/11/04(今天);至今共发布 6 个版本,其中过去 12 个月发布了 6 个;首次发布于 2026/10/24。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。