用 @corsair-dev/habitica 插件把 Habitica 接入 Corsair:70+ 端点、双凭证认证与本地镜像实战指南
【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair
本指南围绕 Corsair 仓库中的 Habitica 插件(packages/habitica)展开,讲解如何将 Habitica 这个把任务与目标游戏化的习惯追踪平台,通过一套完整端点安全地接入你自己的应用:从安装、双凭证(API Token + User ID)认证、70+ 端点清单与风险分级,到速率限制、错误处理、本地数据镜像与审计日志的底层实现。读完你将掌握该插件的完整调用面、配置方式与源码级工作原理,可直接基于此构建自己的 Habitica 集成。
Habitica 插件是什么
@corsair-dev/habitica是 Corsair 的官方插件之一(见 packages/habitica/package.json),包描述为 "Habitica plugin for Corsair"。Habitica 是一个"游戏化习惯追踪器",把日常任务和人生目标包装成角色扮演游戏(见 plugin-docs.yaml 中的description)。插件让 Corsair 应用能够代理、缓存并审计用户对 Habitica API 的访问,典型场景包括:为你的用户展示他们的待办清单与习惯、让 AI Agent 代替用户创建任务/打分会话、围绕挑战与公会(party/guild)构建协作类功能。
从源码结构看,插件遵循 Corsair 的标准插件形态:index.ts 是插件的唯一入口,它导出一个habitica()工厂函数,组装出id: 'habitica'、认证配置、端点集合、端点元数据、schema 与错误处理器等完整插件对象。
安装与依赖
在包管理器中以依赖方式安装:
pnpm add @corsair-dev/habitica按照 package.json 的声明,它对外导出 ESM 构建产物dist/index.js与类型dist/index.d.ts,并声明两个peerDependencies:
| Peer 依赖 | 版本要求 | 用途 |
|---|---|---|
corsair | >=0.1.0 | 插件运行时的核心框架(端点、上下文、事件日志、HTTP 传输) |
zod | ^4.1.13 | 端点输入/输出与实体 schema 校验 |
仓库内提供pnpm build(tsc --build --force && tsup)、pnpm typecheck、pnpm test(Jest 单元测试)与pnpm test:live(真实账号集成测试,见下文"测试"一节)等脚本。
在应用中启用插件
与仓库中其他插件一致,插件通过habitica()工厂函数实例化。核心类型定义于 index.ts:
import { habitica } from '@corsair-dev/habitica'; export const habiticaPlugin = habitica({ // authType: 'api_key', // 默认即 'api_key',可省略 // key: '...', // 可选:固定 API Token // userId: '...', // 可选:固定账号 User ID // errorHandlers: { ... }, // 可选:覆盖/追加错误处理器 // permissions: { ... }, // 可选:按端点配置权限 });各配置项说明:
| 选项 | 类型 | 说明 |
|---|---|---|
authType | PickAuth<'api_key'> | 认证方式,默认(且仅支持)'api_key';源码用as const satisfies AuthTypes收窄字面量类型(见 index.ts) |
key | string | 固定的 Habitica API Token;仅在keyBuilder的source === 'endpoint'时优先返回(见 index.ts) |
userId | string | 账号 User ID,以x-api-user发送;不配置时回退到存储的user_idkey |
hooks | InternalHabiticaPlugin['hooks'] | 插件钩子 |
errorHandlers | CorsairErrorHandler | 追加的自定义错误处理器,会与内置处理器合并 |
permissions | PluginPermissionsConfig<...> | 端点级权限配置 |
双凭证认证:为什么 userId 是第二个"凭证"
Habitica 的凭证由两半组成:x-api-user(User ID)与x-api-key(API Token),二者都会被服务端校验。源码在 client.ts 中明确指出:拿着有效 Token 配另一个账号的 User ID,会被 401 拒绝(There is no account that uses those credentials.),所以 User ID 不是"Token 可推导的路由提示",而是独立的第二凭证。
插件把 User ID 声明为账号级 key(account-scoped key),使其与 Token 一同存储,而无需每次调用都传:
// index.ts export const habiticaAuthConfig = { api_key: { account: ['user_id'] as const, }, } as const satisfies PluginAuthConfig;凭证解析逻辑位于 endpoints/shared.ts:配置优先,其次存储的 key,且故意没有"发现(discovery)"回退——这与 Harvest 插件不同:Harvest 可以询问 Token 能触达哪些账号,而 Habitica 的每一条需要认证的路由都要求先有 User ID,因此不存在任何能"发现"它的路由。两处都拿不到时抛出HabiticaUserIdMissingError,错误信息会明确提示"在插件选项中设置userId,或在user_idkey 下存储一个值"(见 client.ts),这比发一个必然 401 的请求友好得多。
端点全景:71 个操作的完整清单
插件端点按模块.操作两级命名(如tasks.create),对应 index.ts 中的habiticaEndpointsNested结构。每个端点同时具备:输入/输出 zod schema(habiticaEndpointSchemas)、风险等级与描述元数据(habiticaEndpointMeta)。以下按模块完整列出 README 与源码确认的全部端点:
认证 auth(3)
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
auth.login | habitica.api.auth.login | read | Exchange a password for an API token |
auth.register | habitica.api.auth.register | write | Register a new Habitica account and mint its credential |
auth.social | habitica.api.auth.social | read | Authenticate through a social provider |
任务 tasks(14)
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
tasks.addTag | habitica.api.tasks.addTag | write | Apply an existing tag to a task |
tasks.create | habitica.api.tasks.create | write | Create a habit, daily, todo or reward |
tasks.createChallengeTask | habitica.api.tasks.createChallengeTask | write | Add a task to a challenge |
tasks.delete | habitica.api.tasks.delete | destructive | Permanently delete a task |
tasks.deleteChecklistItem | habitica.api.tasks.deleteChecklistItem | write | Remove a checklist item from a task |
tasks.get | habitica.api.tasks.get | read | Retrieve any task by id |
tasks.list | habitica.api.tasks.list | read | List the account's tasks |
tasks.listChallengeTasks | habitica.api.tasks.listChallengeTasks | read | List a challenge's tasks |
tasks.move | habitica.api.tasks.move | write | Move a task to a position in its list |
tasks.score | habitica.api.tasks.score | write | Score a task up or down |
tasks.unlinkAllChallengeTasks | habitica.api.tasks.unlinkAllChallengeTasks | destructive | Unlink every task of a challenge, optionally deleting members' copies |
tasks.update | habitica.api.tasks.update | write | Update a task |
tasks.updateChecklistItem | habitica.api.tasks.updateChecklistItem | write | Update a checklist item's text |
(共 14 项,含tasks.create/list/get/update/delete/score/move/updateChecklistItem/deleteChecklistItem/addTag/createChallengeTask/listChallengeTasks/unlinkAllChallengeTasks及tasks.get可检索挑战任务。)
挑战 challenges(9)
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
challenges.clone | habitica.api.challenges.clone | write | Duplicate a challenge |
challenges.create | habitica.api.challenges.create | write | Create a challenge in a group |
challenges.delete | habitica.api.challenges.delete | destructive | Permanently delete a challenge and its tasks |
challenges.exportCsv | habitica.api.challenges.exportCsv | read | Export a challenge as CSV |
challenges.get | habitica.api.challenges.get | read | Retrieve a challenge |
challenges.join | habitica.api.challenges.join | write | Join a challenge |
challenges.leave | habitica.api.challenges.leave | write | Leave a challenge |
challenges.listByGroup | habitica.api.challenges.listByGroup | read | List a group's challenges |
challenges.listForUser | habitica.api.challenges.listForUser | read | List the challenges the account takes part in |
公会/队伍 groups(11)
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
groups.create | habitica.api.groups.create | write | Create a party or guild |
groups.get | habitica.api.groups.get | read | Retrieve a group by id |
groups.getParty | habitica.api.groups.getParty | read | Retrieve the account's party |
groups.getTavern | habitica.api.groups.getTavern | read | Retrieve the Tavern |
groups.invite | habitica.api.groups.invite | write | Invite people to a group |
groups.inviteToQuest | habitica.api.groups.inviteToQuest | write | Invite the party to a quest |
groups.leave | habitica.api.groups.leave | write | Leave a group |
groups.list | habitica.api.groups.list | read | List groups by type |
groups.listMembers | habitica.api.groups.listMembers | read | List a group's members |
groups.removeMember | habitica.api.groups.removeMember | write | Remove a member from the party |
groups.update | habitica.api.groups.update | write | Update a group's properties |
聊天 chat(3)
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
chat.deleteMessage | habitica.api.chat.deleteMessage | destructive | Permanently delete a chat message |
chat.list | habitica.api.chat.list | read | Read a group's chat messages |
chat.markSeen | habitica.api.chat.markSeen | write | Mark a group's chat as read |
用户 user(11)
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
user.addPushDevice | habitica.api.user.addPushDevice | write | Register a push-notification device |
user.deleteMessage | habitica.api.user.deleteMessage | destructive | Permanently delete an inbox message |
user.deletePushDevice | habitica.api.user.deletePushDevice | write | Unregister a push-notification device |
user.equip | habitica.api.user.equip | write | Equip or unequip gear, a pet, a mount or a costume |
user.get | habitica.api.user.get | read | Read the account's user document |
user.markNotificationSeen | habitica.api.user.markNotificationSeen | write | Mark one notification as seen |
user.markNotificationsSeen | habitica.api.user.markNotificationsSeen | write | Mark several notifications as seen |
user.movePinnedItem | habitica.api.user.movePinnedItem | write | Reorder a pinned reward |
user.readCard | habitica.api.user.readCard | write | Mark a received card as read |
user.reset | habitica.api.user.reset | destructive | Reset the account, deleting every task and returning to level 1 |
user.update | habitica.api.user.update | write | Update user fields by dot path |
内容 content(10)
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
content.dismissNews | habitica.api.content.dismissNews | write | Dismiss the current announcement |
content.get | habitica.api.content.get | read | Fetch the whole game content catalogue |
content.getByType | habitica.api.content.getByType | read | Fetch the content catalogue with named categories EXCLUDED |
content.marketGear | habitica.api.content.marketGear | read | List gear for sale in the market |
content.modelPaths | habitica.api.content.modelPaths | read | List a model's field paths and types |
content.news | habitica.api.content.news | read | Read the latest Bailey announcement |
content.status | habitica.api.content.status | read | Check that the Habitica API is up |
content.timeTravelers | habitica.api.content.timeTravelers | read | List the Time Travellers shop stock |
content.validateCoupon | habitica.api.content.validateCoupon | read | Check whether a coupon code is valid |
content.worldState | habitica.api.content.worldState | read | Read world events and the world boss |
导出 exports(3)
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
exports.history | habitica.api.exports.history | read | Export task history as CSV |
exports.inbox | habitica.api.exports.inbox | read | Export the inbox as HTML |
exports.userData | habitica.api.exports.userData | read | Export the whole account as JSON (contains the account email) |
标签 tags(4)
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
tags.create | habitica.api.tags.create | write | Create a tag |
tags.delete | habitica.api.tags.delete | destructive | Permanently delete a tag |
tags.list | habitica.api.tags.list | read | List every tag on the account |
tags.update | habitica.api.tags.update | write | Rename a tag |
Webhook webhooks(3)
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
webhooks.create | habitica.api.webhooks.create | write | Register an outbound webhook |
webhooks.list | habitica.api.webhooks.list | read | List the account's outbound webhooks |
webhooks.subscribe | habitica.api.webhooks.subscribe | write | Enable an existing webhook |
风险等级体系:read / write / destructive
每个端点的元数据都标注了风险等级(见 index.ts 的说明注释与habiticaEndpointMeta):
read:只取数据,不改状态。write:改变状态,但可撤销或可重复执行而无损失。destructive:保留给 Habitica无法恢复的删除操作——Habitica 是硬删除,没有软删除标记、没有回收站——外加账号重置。
值得注意的两个"反直觉"归类(源码注释明确解释了设计理由):
tasks.score是write而非read。它看起来只是读取角色数值,但它是全部操作中唯一"重放会改变结果"的:POST /tasks/:taskId/score/:direction打分会改变经验与金币,打两次就是打两次,因此归类为write(见 endpoints/tasks.ts)。auth.login是read。它用密码换取 API Token,不改状态;auth.register则是write,因为它真实创建了账号。
destructive共覆盖:challenges.delete、chat.deleteMessage、tasks.delete、tasks.unlinkAllChallengeTasks、user.deleteMessage、user.reset、tags.delete七项。在集成这类端点时,应配合 Corsair 的权限配置(permissions选项)做最小化授权。
传输层实现:Base URL、限流与 x-client
所有请求都经由 client.ts 中的四个传输函数,它们封装了 Habitica API 的种种怪癖:
版本化 Base 与"越界"的导出路径
- 常规 API Base 是
https://habitica.com/api/v3,版本号在路径里而非 Header。 - 三个数据导出操作(
exports.userData/exports.history/exports.inbox)位于版本化 Base 之外,即https://habitica.com/export/*。源码注释特别提醒:Habitica 服务端源码用authWithSession而非authWithHeaders中间件路由它们,看起来像要求浏览器会话;但实测(源码标注为 2026-08-15 验证)用与/api/v3完全相同的x-api-user/x-api-key头即可 200 访问,因此插件用独立的makeHabiticaExportRequest以同样的头部认证直连。
速率限制:30 次/分钟/账号
源码注释记录了实测结论:Habitica 每个 User ID 每分钟允许30 次已认证请求,超过返回 429TooManyRequests;实测第 30 次请求被拒,文档数字精确无误。插件的限流配置(client.ts):
const HABITICA_RATE_LIMIT_CONFIG: RateLimitConfig = { enabled: true, maxRetries: 3, initialRetryDelay: 1000, backoffMultiplier: 2, headerNames: { retryAfter: 'retry-after', remaining: 'x-ratelimit-remaining', limit: 'x-ratelimit-limit', }, };两个 Header 细节决定了为何不能直接套默认配置:
x-ratelimit-reset故意不配置:Habitica 发送的是Date.toString()格式(如"Sat Aug 15 2026 16:43:00 GMT+0000 ..."),共享辅助函数对其parseInt得到NaN会被丢弃,配置它只会暗示"插件按 reset 时间自调节"而实际不能,插件改由retry-after驱动。retry-after是小数秒(实测"21.069"):共享传输的parseInt会截断为 21,导致第一次重试提前零点几秒、可能再吃一个 429;但指数退避会在maxRetries内收敛,属预期而非缺陷。而走裸fetch的四个非 JSON 路径(三个导出 + 挑战 CSV)由插件自行解析,保留小数并向上取整,绝不在窗口内重试(见parseRetryAfterMs,client.ts)。
x-client:每次请求都带
Habitica 要求x-client头(文档格式UserID-AppName,但服务端并不校验格式)。插件固定发送'corsair':源码注释解释这是"稳定且诚实"的选择——不伪装成服务端从不检查的格式,也不携带 User ID,从而不向请求日志泄露账号信息。该头无条件发送,因为连无需认证的/api/v3/content缺了它都会 400Missing x-client headers.(client.ts)。
导出请求的 60 秒超时
导出是"整个账号的文档"而非一页行,慢响应是常态而非故障,因此makeHabiticaExportRequest/makeHabiticaTextRequest使用 60 秒超时(HABITICA_EXPORT_TIMEOUT_MS),高于共享传输的 20 秒(client.ts)。
响应解包与请求构造的工程细节
endpoints/shared.ts 承载了所有端点的公共逻辑:
unwrap:/api/v3的每个响应都是{"success":true,"data":...}信封,端点返回data而不是信封——success与传输层已检查的状态码冗余,调用方不应穿透无信息的包装层。非信封形状(如导出文档、挑战 CSV)原样返回。compactBody/compactQuery:剔除值为undefined的键。Habitica 的更新路由区分"字段缺失"与"显式 null":PUT /tasks/:taskId省略字段即保持不变,而序列化undefined两者都不是,所以未设字段必须在构造 body 前移除。pathSegment:对插入 URL 路径的值做encodeURIComponent。多个 Habitica 路径会插值非不透明 ID——优惠券码、任务 key、置顶奖励的点分path、装备key——原样拼接时若含/或?会静默指向别的路由。withRedactedPathValue:为两个把敏感值放进路径的操作(POST /coupons/validate/:code——有效优惠券是可兑现的凭证;DELETE /user/push-devices/:regId——设备标识符)提供错误脱敏。共享传输的ApiError只脱敏敏感查询参数、不碰路径段,因此这里把原始与百分号编码两种形态都掩码为[REDACTED],并保留状态码以便错误分类器仍能识别(shared.ts)。
匿名路由与认证路由分离
habiticaAnonymousCall服务于/status、/content、/models/:model/paths等无需认证的路由,以及三个铸证操作(注册/登录/社交认证)——后者的路由是authOptional,调用者还没有账号,不可能携带凭证。分离的意义在于:一个缺失的 User ID 不能拖垮一个根本不需要凭证的匿名操作(shared.ts)。
错误处理:识别 Habitica 的 429 与 401
error-handlers.ts 定义了四个处理器,匹配规则基于 Habitica 的真实错误词表(源码标注全部为 2026-08-15 实测):
| HTTP 状态 | error字段 |
|---|---|
| 400 | BadRequest |
| 401 | NotAuthorized,invalid_credentials |
| 404 | NotFound |
| 429 | TooManyRequests |
RATE_LIMIT_ERROR(429):maxRetries设为 5 而非传输层的 3,因为限流是固定的 1 分钟窗口,"等待"在这里是真正充分的策略(区别于一天都不重置的配额)。若错误携带retryAfter(ApiError或裸路径的HabiticaHttpError都可能带),则原样传递毫秒值。AUTH_ERROR(401):永不重试——同一个凭证再试还会失败。诊断要点:Habitica 对"错误 Token"和"错误 User ID"给出同一个401invalid_credentials/ "There is no account that uses those credentials.",两半凭证无法从响应区分,所以一次 401 意味着"两半里有一半错了"。缺 Header 则是另一种 401NotAuthorized/ "Missing authentication headers.",错误信息会明说。CLIENT_HEADER_ERROR:缺x-client头是400 而非 401。插件总是发送该头,所以实践中见到此错误指向"传输被绕过"而非调用方输入,重试无济于事。DEFAULT:兜底,不重试。
HabiticaHttpError(裸fetch路径使用)故意不附加响应体——失败的导出可能仍携带账号数据(userdata.json含账号持有者的邮箱),而这个错误对象最可能进入日志,因此只留状态行(client.ts)。
本地镜像(Mirror):缓存、驱逐与账号重置
插件把读取到的实体镜像到 Corsair 的实体存储(entity store),实现原理见 endpoints/persist.ts:
cacheEntity/cacheEntities:写入前先用实体 schema 做safeParse,Habitica 返回的形状不被 schema 识别时跳过而非写入——缓存永不持有插件无法读回的数据,schema 缺口表现为"缺行"而非"脏数据"。写入是尽力而为(best-effort),失败仅告警,不拖垮插件调用。批量写入按 16 并发分批(CACHE_WRITE_CONCURRENCY),因为GET /tasks/user不分页、一次返回全部任务,长期账号可能一次数百行。- 字段投影:实体 schema 用
.loose(),因此parsed.data仍携带未知属性。projectForCache以 schema 形状为白名单,并额外剔除两类字段:组的chat与 webhook 的url(OMIT_FROM_CACHE,见 persist.ts)——webhook URL 是调用方自己的端点,可能把密钥藏在路径或查询里,不进缓存。 evictEntity:Habitica 是硬删除——没有软删除、没有deleted_at、没有"包含已删"的列表,删除后下次读取即 404。因此删除操作后的镜像驱逐是必需的:tasks.delete用required: true,若本地镜像删不掉则抛HabiticaMirrorEvictionError,明确告知"远端已删、本地副本还在",因为重试删除会 404,需要处理的是镜像而非远端(persist.ts)。注意日志在驱逐之前写入(tasks.ts),避免必需的驱逐抛错时丢掉一条"确已发生"的破坏性变更审计记录。clearMirroredTasks:user.reset一次调用删除账号所有任务并返回重置后的用户,而非被删列表,因此没有 ID 可逐条驱逐。它通过先完整读出全部镜像 ID、再逐条deleteByEntityId清空(分页删除会因游标重排跳掉约一半)。重置本身已成功,失败仅告警而不抛错。
任务 schema 字段见 schema/database.ts:HabiticaTaskEntity覆盖type(habit/daily/todo/reward)、text、notes、tags、value、priority、challenge、group、reminders、checklist、history、repeat、streak、completed等 Habitica 官方 JSON 键名,字段名与官方 API 一致,并标注意value/history/completed是"时间点快照"——tasks.score后镜像不做回读刷新,因为花一个请求去刷新调用方没要的快照,对 30 次/分钟的预算不划算。
审计日志:哪些内容绝不入日志
所有端点调用都会通过logEventFromContext写入corsair_events。由于这些行继承事件日志的保留期,Habitica 插件的日志策略格外谨慎(endpoints/logging.ts):
auditPayload(input, identifierKeys)只记录显式列出的标识字段(如taskId、challengeId),其余已提供字段只记字段名列表(fields数组)而不记值——操作者能看到请求了什么,日志却不变成账号隐私数据的副本。因此任务标题与备注、聊天与收件箱消息、个人资料文本、以邮箱/用户名给出的群邀请等一律不入日志。- 集合类值(如任务 checklist、邀请收件人列表)用
countOf记计数而非内容。 - 三个铸证操作(register/login/social)使用更严格的
credentialAuditPayload():只记录"发生过一次尝试"——不记邮箱、用户名,甚至不记字段名,因为fields: ["username","password"]出现在保留日志里,本身就是诱使后人把字段名放宽成字段值的邀请函(logging.ts)。
Webhooks:插件不消费事件,只管理用户的出站 webhook
README 明确"No webhooks",源码把这一点讲得更透(index.ts 与 endpoints/webhooks.ts):
- 目录中没有 Habitica 的触发器,插件不注册任何 webhook handler、matcher 或租户解析器;脚手架生成的 webhook 文件被移除,而非留下暗示"存在该表面"的空 stub。
- 三个 webhook 操作是用户自己的出站 webhook——Habitica 回调用户自选的 URL——被当作普通操作实现。表面只有 create / list / subscribe(即 enable)三项;API 虽有通用 update 和 delete,但目录未列出,插件不发明目录之外的兄弟端点,保持与消费同一目录的其他插件一致。
webhooks.list值得镜像failures字段:这是 API 唯一的健康信号,Habitica 在连续失败达 10 次时会禁用 webhook(webhooks.ts)。webhooks.subscribe实为PUT /user/webhook/:id且 body 只有{ enabled: true },幂等。
测试:单元测试与真实账号集成测试
包的测试策略(integration.test.ts 文件头注释说明):
- 单元测试(
pnpm test)覆盖客户端、端点 schema、错误处理器等,见 client.test.ts、endpoints.test.ts、error-handlers.test.ts、schema.test.ts。 - 集成测试(
pnpm test:live)针对真实 Habitica 账号,默认与 CI 均排除、无凭证时自跳过:
HABITICA_USER_ID=<uuid> HABITICA_API_TOKEN=<token> pnpm test:live- 关键约束:Habitica 限 30 次已认证请求/分钟/账号,测试套件用
paced()以 2.6 秒间隔节流——早期 2.1 秒版本仍吃到 429,因为限流统计的是滚动分钟内的整体运行而非调用间隔;2.65 MB 的内容目录也只拉一次而非每个测试各拉一次。 - 故意永不实跑的操作:
user.reset(不可撤销)、auth.register/login/social(输入是凭证,注册会创建真实账号)、tasks.score(永久改变角色经验与金币,仅开发期手工跑过一次)、exports.userData(含账号持有者邮箱,仅断言其"可达性"而不读 body)、以及任何删除他人所属群组/成员/聊天消息的操作。 - 注意:把文件名作为位置参数传给 jest 会被当作
--testPathIgnorePatterns的值而静默排除该文件,必须用--testPathPattern。
源码地图与进一步阅读
- 插件入口与端点注册:packages/habitica/index.ts
- HTTP 传输、限流与凭证:packages/habitica/client.ts
- 错误分类器:packages/habitica/error-handlers.ts
- 公共请求/解包/脱敏辅助:packages/habitica/endpoints/shared.ts
- 本地镜像与驱逐:packages/habitica/endpoints/persist.ts
- 端点实现:
packages/habitica/endpoints/下的tasks.ts、challenges.ts、groups.ts、chat.ts、user.ts、auth.ts、content.ts、exports.ts、tags.ts、webhooks.ts - 实体与输入/输出 schema:packages/habitica/schema/database.ts、
packages/habitica/endpoints/types.ts - 测试:client.test.ts、endpoints.test.ts、error-handlers.test.ts、schema.test.ts、integration.test.ts
- 插件元信息:plugin-docs.yaml(displayName/description)
许可证
插件以 Apache-2.0 许可发布(见 README.md 与 package.json 的license字段)。
【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考