@dfinity/oisy-wallet-signer

中文文档
6.0.0发布于 2026/05/22Apache-2.0TypeScriptES Module零依赖构建来源已验证近 30 天下载 5900

一个旨在便利一个dApp和互联网计算机上的OISY钱包之间通信的图书馆.

中文使用文档
文档目录19 节
  1. :火箭:介绍
  2. 目录
  3. : 计算机:安装
  4. : 写入 手:钱包中的用法
  5. 1. 签字人初始化
  6. 2. 实施断开
  7. 3. 登记提示
  8. : writing hand:在客户端中使用
  9. 1. 初始化钱包
  10. 2. 实施断开
  11. 3. 请求权限和账户
  12. 4. 呼叫电筒
  13. : 信息 桌面 人物:提示和诡计
  14. 实例
  15. 本地运行测试
  16. 局部运行 E2E 测试
  17. 与 OISY 钱包一起开发
  18. 本地运行 Pseudo 钱包签名器
  19. :对接:社区

OISY 钱包签名器

一个图书馆,旨在便利一个DApp和 OISY 钱包 编辑 互联网 计算机。 。 。 .


Internet Computer portal GitHub CI Library Checks Workflow Status GitHub CI Library Tests Workflow Status GitHub CI E2E Tests Workflow Status

:火箭:介绍

OISY钱包签名器是一个轻量级库,旨在将dApps与互联网计算机上的OISY钱包连接起来,实现安全的信息签名和交易批准.

它执行红十字委员会讨论和制定的各种标准 身份和钱包标准工作组。 。 。 .

虽然主要为OISY开发,但库可以被集成到任何钱包或项目中去寻求签名者能力.

此外,它还包括有意见的客户,这些客户可以与比较方案或红十字委员会分类账进行互动,而不需要任何具体的JavaScript框架,从而使图书馆完全不可知.

目录

: 计算机:安装

# with npm
npm install @dfinity/oisy-wallet-signer
# with pnpm
pnpm add @dfinity/oisy-wallet-signer
# with yarn
yarn add @dfinity/oisy-wallet-signer

: 写入 手:钱包中的用法

要在钱包或项目中使用 OSY 钱包签名器, 请遵循这些步骤 :

1. 签字人初始化

将您的申请转换为开始收听和处理红十字委员会信息的签名器,需要初始化 Signer 对象.

import {Signer} from '@dfinity/oisy-wallet-signer/signer';

const signer = Signer.init({
  owner
});

这个 owner 是非匿名身份,可以与签名人交互。 通常,它是您的应用程序的用户.

[TIP] 当本地开发时, 您可以以参数初始化签名符 host 说明你的复制品. 默认使用 https://icp-api.io

const signer = Signer.init({
  owner,
  host: 'http://localhost:4943'
});

2. 实施断开

在继续前进之前,重要的是要实施签名器的断开. 当用户签名退出时或当其使用的组件被卸载时,这种情况可能发生.

signer.disconnect();

断开签名器可以移除它的收听器,同时也重新发送它正在通信的源指示.

3. 登记提示

OISY钱包签名库支持各种需要与您的应用程序交互的标准. 例如,当 dApp 请求您钱包或工程支持的账户列表时,您需要通过自动回复或要求用户选择来提供此信息.

这些类型的交互——客户端请求签名人提供信息,然后由签名人询问您的应用程序,然后将回复发回客户端——由这个库中我们所谓的"即时"来管理.

因此,为了有效实施这些功能,您需要在申请中注册提示.

[尖叫声] 注册同样的多倍的提示会覆盖先前附加的提示 。 库只支持一次一个活动提示 . 虽然这种模式在各种应用中被证明是有效的,我们实施过这个标志符,但请注意这个API特别会因为这个原因被打破变化.

A. 请求权限

由 OSY 钱包签名库支持的任何动作必须首先由您的钱包或工程的用户批准 红十字委员会-25:签字人互动标准。 。 。 .

如果得到许可,可以采取行动。 如果从未获准或批准,图书馆将继续在每次请求采取行动时迅速获得许可。 如果被拒绝,它将拒绝对动作的访问,并对客户端做出错误回应.

一旦客户端明确要求,即触发权限,但如果签名人发现某个动作被调用,且这些动作从未被设定,则自动启动.

[注] 权限的寿命周期目前为7天. 这意味着您钱包或项目的用户可能每周都会被敦促重新确认他们设定的权限.

import {
  ICRC25_REQUEST_PERMISSIONS,
  type IcrcScope,
  type PermissionsConfirmation,
  type PermissionsPromptPayload
} from '@dfinity/oisy-wallet-signer';

let scopes: IcrcScope[] | undefined = undefined;
let confirm: PermissionsConfirmation | undefined = undefined;

signer.register({
  method: ICRC25_REQUEST_PERMISSIONS,
  prompt: ({confirm: confirmScopes, requestedScopes}: PermissionsPromptPayload) => {
    scopes = requestedScopes;
    confirm = confirmScopes;
  }
});

要注册权限提示, 您需要指定方法 ICRC25_REQUEST_PERMISSIONS 并提供每次请求权限时由库执行的回调.

这个 prompt 回调收到两个参数:

  • requestedScopes:需要批准或拒绝的所请求的范围。 例如,这可以包括列出账户或启动呼叫罐行动等权限.
  • confirm:一个可以用来响应签名者的回调,并由此延伸给客户端的回调,有被授予或拒绝的许可.

流量如下:

  1. 这个 prompt 调用由签名器触发 .
  2. 在你的应用或钱包,你收到 requestedScopes 编辑 confirm 调用.
  3. 您要么敦促您的用户批准或拒绝请求的范围, 要么您的应用程序自动做出这些决定 。 例如,这种决策过程的结果可能储存在一个称为“......”的变量中 yourScopes。 。 。 .
  4. 最后,您通过拨打回话来确认签名人的权限: confirm(yourScopes);。 。 。 .

B. 账目

客户端可以要求您钱包或项目管理账户的信息. 这种行为由 红十字委员会-27:账目 规格.

如果获得必要的许可,则每次请求账户列表都会触发相应的提示.

如果客户端请求账户,且权限被否决,签名者会自动回答错误. 若您没有获得许可或被拒绝,您将首先做出这一决定.

import {
  ICRC27_ACCOUNTS,
  type AccountsApproval,
  type AccountsPromptPayload
} from '@dfinity/oisy-wallet-signer';

let approve: AccountsApproval | undefined = undefined;

signer.register({
  method: ICRC27_ACCOUNTS,
  prompt: ({approve: approveAccounts}: AccountsPromptPayload) => {
    approve = approveAccounts;
  }
});

要登记账户的提示, 您需要指定方法 ICRC27_ACCOUNTS 并提供每次请求账户时由图书馆执行的回调.

这个 prompt 回调收到唯一的参数 :

  • approve:一个可以用来响应签名者的回调,并由此延伸给客户端,并带有账户.

流量如下:

  1. 客户端请求提供账户列表.
  2. 签字人检查权限. (单位:千美元) 如果从未给予或拒绝,签名人会提示您确认或否认范围. b. 如果被拒绝,则以错误回答. c. 将数据删除; 如果获得许可 prompt 调用由签名器触发 .
  3. 在你的应用或钱包,你收到 approve 调用.
  4. 您要么敦促用户选择账户列表, 要么您的应用程序自动决定哪些账户可以共享 。 例如,您将这些账户存储在一个名为 yourAccounts。 。 。 .
  5. 最后,你给签名人批准账户 通过呼叫回调: approve(yourAccounts);。 。 。 .

签名人不会执行对弹匣的呼叫,除非首先提示您的钱包或项目批准或拒绝行动.

为了对请求采取的行动作出解释,签字人从目标罐取回了希望是人可读的同意信息,具体定义如下: ICRC-21: 大筒呼叫同意信件 的规格,并显示该规格供用户审查.

如果罐子没有执行ICRC-21标准,而且由于这个库是为钱包签名器OISY设计的,图书馆将试图解码请求参数——如果目标方法是 icrc1_transfer, (中文(简体) ), icrc2_approve,或 icrc2_approve_from ——动态生成一个类似于红十字委员会SNS分类账生成的同意信息. 解码后的信件将由库明确提供,以表明它是以这种方式生成的.

由于只有在请求发信箱呼叫时才会获取同意信息,因此只有在获得必要的呼叫许可时,才会触发此提示.

import {
  type ConsentMessageApproval,
  type ConsentMessagePromptPayload,
  type ResultConsentMessage,
  type ResultConsentInfo,
  ICRC21_CALL_CONSENT_MESSAGE,
  type Rejection
} from '@dfinity/oisy-wallet-signer';

let approve: ConsentMessageApproval | undefined = undefined;
let reject: Rejection | undefined = undefined;
let consentInfo: ResultConsentInfo | undefined = undefined;

let loading = false;

signer.register({
  method: ICRC21_CALL_CONSENT_MESSAGE,
  prompt: ({status, ...rest}: ConsentMessagePromptPayload) => {
    switch (status) {
      case 'result': {
        approve = rest.approve;
        reject = rest.reject;
        consentInfo = rest.consentInfo;
        loading = false;
        break;
      }
      case 'loading': {
        loading = true;
        break;
      }
      default: {
        approve = undefined;
        reject = undefined;
        consentInfo = undefined;
        loading = false;
      }
    }
  }
});

要注册权限提示, 您需要指定方法 ICRC21_CALL_CONSENT_MESSAGE 并提供一个回调,每次收到同意消息后,由库执行.

这个 prompt 调用后收到若干参数:

  • status: 获取同意信息需要 更新呼叫,这需要几秒钟才能完成。 此外,由于网络问题或目标罐没有实施这种API,这一过程可能会出错。 由于这个原因,每通呼叫可能多次触发回调,每次提供不同的状态.
  • approve:一个用于批准同意消息的回调. 批准消息指示签名人着手执行向目标收发筒发出的呼吁.
  • reject:回调拒绝收发筒呼叫,指不会采取任何行动,客户会收到回复错误.
  • consentInfo: 包含同意信息,格式为Markdown. 其规定如下: Ok 字段,如果由目标罐产生,或者在 Warn 字段,如果由库构建.

[注] OSY钱包签名器目前只支持对同意消息的通用显示.

流量如下:

  1. 客户要求呼叫一个罐.
  2. 签名人检查权限 :
    • 如果许可既没有被授予也没有被拒绝,签名者会提示您确认或否认范围.
    • 如果权限被拒绝,签名人会以出错回应.
    • 如果获得许可 prompt 由带有状态的签名者触发回调 loading。 。 。 .
  3. 签名者获取同意信息 :
    • 如果获取成功, 您的应用程序或钱包会收到同意信息 (consentInfo报告,第2段 approve 财务报告和审定财务报表 reject 召回电话.
    • 如果获取信件失败,签字人将检查目标方法是否是红十字委员会先前规定的三种方法之一(icrc1_transfer, (中文(简体) ), icrc2_approve,或 icrc2_approve_from) (中文(简体) ).
      • 如果匹配, 签名人会试图获取分类账 metadata 并建立一个 Markdown 格式化的同意信息.
        • 如果此尝试成功, 您的应用程序或钱包会收到同意信息( O) :consentInfo将字段设置为 Warn,连同 approve 财务报告和审定财务报表 reject 召回电话.
        • 如果失败, 调用会随状态而触发 error,则客户端被错误通知.
  4. 您向您的用户提交格式化的同意信息, 并提示他们批准或拒绝该通知 .
  5. 根据用户的决定,你要么批准要么拒绝收发筒:
    • 要拒绝, 您执行回调 reject();,它促使签名者以出错回应客户端.
    • 为了批准,你执行回调 approve();指示签字人执行枪口呼叫.

D. 呼叫筒

拨打弹匣不需要您钱包或项目的直接交互 。 然而,您可能想要在呼叫执行时向您的用户提供一些视觉反馈,或者在呼叫成功或失败时告知用户. 为此,您可以注册一个提示 红十字委员会-49:呼叫罐头 规格.

签名人只有在获得必要的许可且同意信息获得批准后才会执行呼叫.

import {
  type CallCanisterPromptPayload,
  ICRC49_CALL_CANISTER,
  type Status
} from '@dfinity/oisy-wallet-signer';

let status: Status | undefined = undefined;

signer.register({
  method: ICRC49_CALL_CANISTER,
  prompt: ({status: callCanisterStatus, ...rest}: CallCanisterPromptPayload) => {
    status = callCanisterStatus;
  }
});

需要指定方法后, 才能注册一个呼叫器的提示 ICRC49_CALL_CANISTER 并提供由图书馆执行的回调.

这个 prompt 调用后收到若干参数:

  • status:每个呼叫可能多次触发回调,每次提供不同的状态: executing, (中文(简体) ), result,或 error。 。 。 .
  • result:如果呼叫"罐"成功,则提示提供结果. 这些成果包括: contentMap (单位:千美元) certificate两者都是杂碎.
  • error: 如果呼叫失败, 弹筒或网络( 如果可用) 返回的任何出错都会通过提示提供 .

[TIP] 在 OISY 钱包中, 我们不显示结果或错误 。 我们只诠释状态,因为呼叫收音机的实际结果预计将由请求签名人执行动作的客户端处理.

流量如下:

[注] 导致执行呼叫到弹筒的流量在上一章"同意消息"中被描述. 以下描述假设许可和同意信息已经获得批准.

  1. 在启动呼叫之前,签名人会通知请求即将启动,状态设定为 executing。 。 。 .
  2. 呼叫枪管被执行.
  3. 收到结果或出错后,会以相应的状态触发提示,并将相应的响应发送给客户端.

: writing hand:在客户端中使用

如导言所提到,这个图书馆主要是为OISY而开发的. 但是,它也包括有意见的客户,这些客户可以在JavaScript应用程序中与比较方案或红十字委员会分类账进行互动.

[大 这些客户目前只支持红十字委员会第一和第二方法。 如果您想使用其他功能或国际比较方案转移,请联系.

1. 初始化钱包

与支持签名标准的钱包或项目通信,通过创建客户端来初始化.

[注] 在此和以下各章中,我们使用 IcpWallet 客户端可以通过一个签名符与比较方案分类账进行交互。 但是,如果你想通过签注器与红十字委员会的分类账进行联系,这种做法是类似的 IcrcWallet。 。 。 .

import {IcpWallet} from '@dfinity/oisy-wallet-signer/icp-wallet';

const wallet = await IcpWallet.connect({
  url: 'https://staging.oisy.com/sign'
});

[TIP] 当本地开发时, 您可以以参数初始化签名符 host 说明你的复制品. 用于解码在默认值以外的端口运行的响应 .

const wallet = await IcpWallet.connect({
  url: 'https://staging.oisy.com/sign',
  host: 'http://localhost:6666'
});

这个 url 是您想要连接的签名器的 URL。 就OISY而言,生产URL是: https://staging.oisy.com/sign。如果想要连接到本地钱包或工程,例如,可以指定像 URL http://localhost:5174/sign。 。 。 .

除此参数外,还可以各种附加参数来建立连接:

  • windowOptions: 允许您指定应如何打开带有钱包或工程的弹出符作为签名符。 您可以定义其位置(例如,以中心或右上角)并设定特定的维度. 默认情况下,该签名器作为弹出式打开,位于桌面上用户浏览器右上边缘,或者作为移动上的一个单独的分页.
  • connectionOptions: 让您在与签名者建立连接时指定超时和投票选项 .
  • onDisconnect:通过提供这个回调,如果用户关闭了签名窗口,就可以通知您的应用程序.

例如:

import {IcpWallet} from '@dfinity/oisy-wallet-signer/icp-wallet';
import {DEFAULT_SIGNER_WINDOW_FEATURES} from '@dfinity/oisy-wallet-signer';

const onDisconnect = (): void => console.log('The user has close the wallet popup.');

const wallet = await IcpWallet.connect({
  windowOptions: {
    width: 576,
    height: 625,
    position: 'center',
    features: DEFAULT_SIGNER_WINDOW_FEATURES
  },
  url: 'http://localhost:5174/sign',
  onDisconnect
});

2. 实施断开

在继续前,必须实施钱包的断开. 当用户签名退出时或当其使用的组件被卸载时,这种情况可能发生.

wallet.disconnect();

断开钱包会关闭其弹出,去掉等待回复的收听者,并停止检查钱包是否仍在活动的投票.

3. 请求权限和账户

在与OISY互动时,您不需要明确请求权限以遵守规格,因为钱包使用的签名库会自动处理您的权限. 然而,根据我们从早期用户收集的反馈,似乎在启动任何其他行动之前,如果必要的话,明确要求一次授权,可以提供更平稳,更直观的经验.

同样, 您可能想要在您的应用程序中显示关于签名器中可用的账户的信息 。 这在与OISY整合时特别相关,因为它目前支持一个单一的账户,并且可以在不提示用户的情况下响应账户请求(假设已经获得权限).

出于这些原因,我们建议在连接建立之后将这两项业务连锁起来——请求许可和检索帐户信息.

import {IcpWallet} from '@dfinity/oisy-wallet-signer/icp-wallet';
import type {IcrcAccount, IcrcScopeMethod, IcrcScopesArray} from '@dfinity/oisy-wallet-signer';

const wallet = await IcpWallet.connect({
  url: 'https://staging.oisy.com/sign'
});

const {allPermissionsGranted} = await wallet.requestPermissionsNotGranted();

if (!allPermissionsGranted) {
  // Inform the user that all permissions are required to continue
  return;
}

const accounts = await wallet.accounts();

const account = accounts?.[0];

4. 呼叫电筒

最后,你准备拨打电筒,以转移国际比较方案(或红十字委员会的代号,如果你正在使用 IcrcWallet) (中文(简体) ).

这个图书馆的有意见的客户端使用与其对等客户端相同的接口,这些接口不需要签名,而是必须操作,即:, @dfinity/ledger-icp 财务报告和审定财务报表 @dfinity/ledger-icrc。这意味着,作为请求提供或作为答复接收的任何有效载荷与这些标准接口所使用的有效载荷相同.

例如,让我们假设我们想从用户在钱包里的账户中转出ICP到在您的客户端注册的账户.

import type {Account} from '@dfinity/ledger-icp';

const E8S_PER_ICP = 100_000_000n;
const ONE_ICP = 1n * E8S_PER_ICP;

const to: Account = {
  owner: YOUR_USER_PRINCIPAL,
  subaccount: []
};

const request = {
  owner: to,
  amount: ONE_ICP
};

await wallet.icrc1Transfer({
  owner: account.owner,
  request
});

这个 request 参数是转让的有效载荷。 它载有发送比较方案所需的标准信息。 在这个例子中,目标接受者是用户在您的应用程序上签名(没有子账户),我们的目标是转移1个比较方案.

要处理罐子呼叫,需要针对已知的账户启动请求,这就是为什么 owner 会 议 日 程 和 议 程 icrc1Transfer 设置为 account 这一点已在上一章中解决.

: 信息 桌面 人物:提示和诡计

实例

您可以在以下列表中找到图书馆实施的各种实例: 演示。 。 。 .

[TIP] 这些例子在斯维尔特v5中被实施.

  • 这个 钱包 前端app是一个测试应用程序,显示签名者在库中的能力.

  • 这个 依赖方 客户端包含两种不同的路线:

    1. A 类 演示 模拟一个应用程序中图书馆的“真实生活”使用情景,例如在网络商店.
    2. 扩展 客户端 主要用于端到端(E2E)测试,允许对每个标准分别进行评价.

本地运行测试

以下列方式在本地运行测试套件:

git clone https://github.com/dfinity/oisy-wallet-signer
cd oisy-wallet-signer
npm ci
npm run test

局部运行 E2E 测试

虽然测试套件涵盖了大多数案例,但库中也包含一些E2E测试,这些测试更侧重于"幸福路径"的情景,而不是"延伸边缘案例".

要在当地运行,你需要安装朱诺CLI. 采取以下步骤:

  1. 安装嵌入器 :

确定您在您的机器上安装了 Docker(窗口, (中文(简体) ), 麦克OS,或 链接) (中文(简体) ).

[注] 对于有M处理器的MacBooks来说,重要的是要使用多克桌面版本4.25.0或后期,最理想的是最新的可用版本.

  1. 开始演示:

导航到 演示 目录并启动环境 :

[注] 每个命令应在单独的终端或背景中运行.

cd demo
npm ci
npx @junobuild/cli emulator start
npm run dev:party
npm run dev:wallet
  1. 运行测试 :

返回根目录并执行测试 :

npm run e2e

与 OISY 钱包一起开发

依赖方的演示可用于测试OISY钱包内签名器特征的整合.

  1. 安装 OISY 钱包
git clone https://github.com/dfinity/oisy-wallet
cd oisy-wallet
npm ci
npm run deploy
  1. 启动本地复制

内部 oisy-wallet 文件夹 :

dfx start
  1. 运行演示

导航到 演示 oISY 钱包签名器的文件夹,并在端口启动演示 5173数字 :

npm run dev:party
  1. 启动 OISY 钱包

单位 oisy-wallet ,在端口运行本地开发服务器 5174数字 :

npm run dev

本地运行 Pseudo 钱包签名器

如果你正在构建一个依赖方(客户端)应用程序——无论您正在使用哪个库——并且想要测试签名者互动而不部署在主网上,您可以运行本repo中包含的伪钱包签名器,用于演示和E2E测试目的.

此伪钱包执行签名人提供的所有功能, 允许您测试账户列表, 权限请求, 并完全在本地环境中进行呼叫 .

  1. 清除寄存器并安装库依赖性 :
git clone https://github.com/dfinity/oisy-wallet-signer
cd oisy-wallet-signer
npm ci
  1. 导航到演示目录并准备环境 :
cd demo
npm ci
npm run sync:all
  1. 适应端口(如有需要)

如果您的本地复制没有在默认端口运行 4943 演示时,请确保更新 LOCAL_REPLICA_HOST 变量在 .env 数据 相应的文件.

  1. 启动伪钱包
npm run dev:wallet

这将在端口启动伪钱包界面 5174 (或另一个可用的端口).

从客户端应用程序中,您现在可以使用 http://localhost:5174/sign。 。 。 .

如果您使用这个库中一个有意见的客户端,而你的复制品运行在一个非标准端口,也可以通过主机参数:

import {IcpWallet} from '@dfinity/oisy-wallet-signer/icp-wallet';

const wallet = await IcpWallet.connect({
  url: 'http://localhost:5174/sign',
  host: 'http://localhost:5987'
});

:对接:社区

版本与发布记录

@dfinity/oisy-wallet-signer 共发布 64 个版本,首次发布于 2024/10/22;过去 12 个月发布了 13 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
6.0.0latest2026/05/225 个月前
4.1.32026/03/257 个月前
4.1.22026/03/058 个月前
4.1.12026/03/058 个月前
4.1.1-next-2026-03-04next2026/03/058 个月前
4.1.02025/12/1910 个月前

关于 @dfinity/oisy-wallet-signer 的常见问题

@dfinity/oisy-wallet-signer 是什么?

一个旨在便利一个dApp和互联网计算机上的OISY钱包之间通信的图书馆.

如何安装 @dfinity/oisy-wallet-signer?

在项目目录运行 npm install @dfinity/oisy-wallet-signer,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:zod(^4)、@icp-sdk/core(^4)、@dfinity/utils(~4.0.3)、@icp-sdk/canisters(~3.1.0)、@dfinity/zod-schemas(^3)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@dfinity/oisy-wallet-signer 支持 TypeScript 吗?

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

@dfinity/oisy-wallet-signer 支持 ESM 还是 CommonJS?

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

@dfinity/oisy-wallet-signer 可以商用吗?

@dfinity/oisy-wallet-signer 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@dfinity/oisy-wallet-signer 的下载量有多少?

根据 npm 官方下载统计,@dfinity/oisy-wallet-signer 在 2026/10/12 – 2026/11/10 这 30 天内被下载 5,900 次,日均约 197 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dfinity/oisy-wallet-signer 还在维护吗?

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