Insomnia 的 insomnia-api 包:Insomnia Cloud 共享 API 客户端的设计与使用
insomnia-api 是 Insomnia 仓库中负责与 Insomnia Cloud 后端通信的共享 API 层,桌面应用(insomnia)与 inso CLI 都通过它调用账户、组织、协作、Mock 等云端功能。本文以 packages/insomnia-api/README.md 为骨架,结合包内源码,完整讲解这个包如何安装引入、如何通过 configureFetch 注入可替换的网络实现、11 个功能模块分别封装了哪些端点,以及调用方在路由层如何利用 isApiError 处理服务端错误。读完本文,你可以准确理解该包的依赖注入架构、各模块的 API 面(方法、路径、请求体),并在自己的代码中正确引用与排错。
包的定位:跨 insomnia 与 inso-cli 共享的 API 功能与类型
README 对包的定位一句话概括得非常清楚:
This package contains the API functionality and types for the Insomnia application which could be shared across insomnia and inso-cli.
也就是说,这个包同时承担两个职责:
- API 功能封装:把每个云端端点封装成语义化的 TypeScript 函数(如
getUserProfile、createTeamProject),调用方不需要关心 URL 拼接、请求头与错误解析; - 类型共享:
User、UserEncryptionKeys、Space、CurrentPlan等类型从本包导出,让桌面端 UI、主进程与 CLI 工具使用同一份类型定义。
从 package.json 可以看到关键元信息:
- 包名
insomnia-api,private: true(不发布到 npm,只在 monorepo 内部通过 workspace 消费); - 当前版本
13.2.0; exports字段直接把入口指到 TypeScript 源码./src/index.ts(import与types条件均指向同一文件),因此消费方无需构建步骤,直接以源码形式引入;- 运行时依赖只有一个:
@getinsomnia/insomnia-v3-fetch(OpenAPI 生成的 v3 SDK),用于部分 v3 端点(如spaces.ts、user.ts中的类型)。
包提供的脚本(lint / type-check / test)也表明它以独立的 TypeScript 子包形式接受检查与单测:
"scripts": {
"lint": "eslint . --ext .ts,.tsx --cache",
"type-check": "tsc --noEmit --project tsconfig.json",
"test": "vitest run"
}
安装与引入:npm workspace 零安装 + 统一入口导出
README 的「Install」一节指出该包走 npm workspace,无需单独安装,在仓库根目录执行一次依赖安装后即可被其他 workspace 包直接 import 'insomnia-api'。README 给出的标准引入方式是:
import { getUserProfile, getEncryptionKeys, type User, type UserEncryptionKeys } from 'insomnia-api';
所有导出集中在 src/index.ts,结构上就是 11 个功能模块的桶式再导出,外加 fetch.ts 中 4 个与网络层相关的对象:
export * from './user';
export * from './vault';
export * from './enterprise';
export * from './trial';
export * from './project';
export * from './collaborators';
export * from './invite';
export * from './organizations';
export * from './spaces';
export * from './mock';
export * from './vcs';
export { configureFetch, type FetchConfig, ResponseFailError, isApiError } from './fetch';
值得注意的是 user.ts、spaces.ts 等文件还会把 @getinsomnia/insomnia-v3-fetch SDK 的部分类型(如 User、UserEncryptionKeys、Space)二次导出,使消费方只需依赖 insomnia-api 一个包名即可拿到全部云端类型——这正是 README 所说的 "API functionality and types" 的落地方式。
网络层设计:可注入的 fetch 与统一错误类型
理解这个包的关键在于 src/fetch.ts。它并不直接发起 HTTP 请求,而是定义了一个可注入的请求抽象:
export interface FetchConfig {
method: 'POST' | 'PUT' | 'GET' | 'DELETE' | 'PATCH';
path: string;
sessionId: string | null;
organizationId?: string | null;
data?: unknown;
origin?: string;
headers?: Record<string, string>;
timeout?: number;
}
export type Fetch = <T = void>(options: FetchConfig) => Promise<T>;
FetchConfig 各字段的含义:
| 字段 | 必填 | 说明 |
|---|---|---|
method |
是 | 仅允许 5 种 REST 动词 |
path |
是 | 相对 API 根地址的路径,如 /v3/users/me |
sessionId |
是 | 会话凭据,最终变成请求头;传 null 表示匿名请求 |
organizationId |
否 | 团队上下文,映射为 X-Insomnia-Org-Id 头 |
data |
否 | JSON 请求体 |
origin |
否 | 覆盖默认 API 根地址(用于 mockbin、外部资产等非同源端点) |
headers |
否 | 附加请求头 |
timeout |
否 | 超时时间,由具体实现决定默认值 |
注入机制采用「一次性配置」的守卫模式:
let fetch: Fetch = <T = void>(_options: FetchConfig): Promise<T> => {
throw new Error('Fetch has not been configured. Please call configureFetch() at application startup.');
};
export function configureFetch(_fetch: Fetch) {
if (configured) {
throw new Error('Fetch has already been configured and cannot be re-configured.');
}
fetch = _fetch;
configured = true;
}
这样设计有两个直接收益:
- 默认抛错:任何忘记初始化就调用 API 的代码会立即得到明确的启动期错误,而不是静默失败;
- 环境无关:渲染进程、主进程、CLI 各自注入符合自己运行环境的
fetch实现。
桌面端如何完成注入
在桌面应用中,两个入口分别在启动时完成注入。渲染进程 entry.client.tsx 与主进程 entry.main.ts 都执行同样的模式:
import { configureFetch } from 'insomnia-api';
configureFetch(options => insomniaFetch({ ...options, onDeepLink: (uri: string) => window.main.openDeepLink(uri) }));
真正干活的实现是 insomnia-fetch.ts,它把 FetchConfig 翻译成一个完整的 fetch 调用,并附加以下请求头:
| 请求头 | 来源 | 用途 |
|---|---|---|
X-Insomnia-Client |
getClientString() |
客户端标识(版本、构建号) |
insomnia-request-id |
generateId('desk') |
请求追踪 ID |
X-Origin |
origin 或 getApiBaseURL() |
声明请求来源,后端据此选择端点基址 |
X-Session-Id |
sessionId |
会话认证 |
Content-Type: application/json |
有 data 时自动附加 |
JSON 序列化 |
X-Insomnia-Org-Id |
organizationId |
团队作用域 |
X-Mockbin-Test |
Playwright 测试环境自动附加 | 让 mock 服务识别测试流量 |
除请求头外,该实现还处理了三类横切逻辑:
- 超时:
AbortSignal.timeout(timeout)包裹请求,超时抛出insomniaFetch timed out: {method} {path}; - 深链:若响应头带
x-insomnia-command,回调onDeepLink(uri)打开 deep link(这也是entry.client.tsx注入时传入该回调的原因); - 错误规范化:非 2xx 响应会尝试解析 JSON 中的
error/message字段,统一抛出ResponseFailError(携带原始Response对象);底层网络错误(如证书、ECONNREFUSED)则从err.cause中提取详情后重新抛出。
统一的错误判定
fetch.ts 同时导出了错误类型与类型守卫,调用方无需 instanceof 判断:
export class ResponseFailError extends Error {
response: Response;
constructor(name: string, msg: string, response: Response) {
super(msg);
this.name = name || 'ResponseFailError';
this.response = response;
}
}
export const isApiError = (error: unknown): error is ResponseFailError =>
error instanceof ResponseFailError;
桌面端路由层大量使用该模式区分「服务端拒绝」与「本地异常」,例如 organization.projectId.delete.tsx 中:
import { deleteTeamProject, isApiError } from 'insomnia-api';
// ...
if (isApiError(err)) {
// 用服务端返回的 name/message 展示错误
}
由于 insomnia-api 本身不关心 Response 如何呈现,这套「抛错类型 + 类型守卫」的组合让 UI 层可以稳定地拿到后端错误码(如 CODE-403)与错误消息。
功能模块总览:11 个领域与对应端点
src/index.ts 再导出的 11 个文件对应 11 个功能领域。以下按模块梳理其封装的端点与方法,全部路径与各函数签名均可在对应源码文件中核对。
user.ts:当前用户、账单与 onboarding
| 函数 | 方法 + 路径 | 说明 |
|---|---|---|
logout |
POST /auth/logout |
注销会话 |
getUserProfile |
GET /v3/users/me |
当前用户资料,返回 User |
getEncryptionKeys |
GET /v3/users/me/encryption-keys |
用户加密密钥(SRP 公钥材料) |
getOnboardingState |
GET /v3/users/me/onboarding |
新手引导状态 |
latchRequestThresholdReached |
POST /v3/users/me/onboarding/request-threshold |
幂等闩锁:标记账户达到首个请求毕业阈值,服务端存 sticky bit,重复调用安全,返回 204 无 body |
recordUserAction |
POST /v3/users/me/actions |
上报用户行为事件(event_id + action_type) |
getCurrentPlan |
GET /v1/billing/current-plan |
当前订阅计划,含 type(free / individual / team / enterprise / enterprise-member)、周期、状态(trialing / active)等字段 |
getUserFiles |
GET /v1/user/files |
列出远端同步文件 |
getLearningFeature |
GET /insomnia-production-public-assets/inapp-learning.json |
应用内学习入口配置;该端点不在 Insomnia API 域内,origin 指向 Google Storage 且 sessionId 传空串 |
latchRequestThresholdReached 的注释是理解「幂等闩锁」语义的好例子:因为服务端状态永不回退,客户端可以放心重试,丢失的调用会在下次尝试时自愈(见 user.ts L33-L44)。
vault.ts:金库(密码保险库)的 SRP 校验流程
vault.ts 封装了 Insomnia 本地金库的服务端半程逻辑,共 5 个函数:
getVault:GET /v1/user/vault,读取已有金库的salt;createVault:POST /v1/user/vault,提交{ salt, verifier }创建金库;resetVault:POST /v1/user/vault/reset,同样参数重置金库;verifyVaultA:POST /v1/user/vault-verify-a,上传客户端计算的srpA,返回{ sessionStarterId, srpB };verifyVaultM1:POST /v1/user/vault-verify-m1,提交srpM1与会话句柄,服务端返回srpM2供客户端二次校验。
srpA / srpB / srpM1 / srpM2 的字段命名表明它实现的是 SRP(Secure Remote Password)协议的两步交换:客户端先算 A 发上去,拿回 B 后本地算 M1,服务端验证后回 M2,双方由此在不传输密码的前提下确认密码正确。结合桌面端路由 auth.create-vault-key、auth.validate-vault-key、auth.reset-vault-key(如 auth.validate-vault-key.tsx)的存在,可以推断 UI 层的金库创建/校验流程正是驱动这一组函数完成的。
spaces.ts:v3 SDK 客户端 + 游标分页
spaces.ts 与其他模块不同,它不直接使用注入的 fetch,而是封装 @getinsomnia/insomnia-v3-fetch 生成的 OpenAPI SDK(DefaultApi):
interface V3ClientConfig {
getBaseURL: () => string;
getClientString: () => string;
generateRequestId: () => string;
// 代理感知的 fetch;否则 SDK 回退到全局 fetch,
// 在 Electron 主进程 / CLI 中会绕过系统代理与 OS 证书
fetchApi: FetchAPI;
}
export function configureV3Client(config: V3ClientConfig) { ... }
buildClient 用配置构造 Configuration:basePath 为 ${baseURL}/v3,apiKey 回调把 X-Session-ID 注入请求,并统一附加 X-Insomnia-Client、X-Origin、insomnia-request-id 三个头。这里的注释点明了与 configureFetch 相同的动机:让 SDK 请求也走代理感知的 fetch。
包内唯一的业务函数 getSpaces 以 pageSize: 200 循环拉取当前用户所属的全部 space(空间),通过解析 meta.page.next 中的 page[after] 游标翻页直到耗尽;SDK 抛出的 ResponseError 会被 mapSdkError 转换成本包统一的 ResponseFailError,并把 Problem JSON 中的 title/detail 带出来。源码中保留的 TODO 也值得注意:作者计划让调用方改为按需分页而非一次性拉全量(见 spaces.ts L77-L83)。
organizations.ts:组织特征、席位、角色与权限
organizations.ts 是端点最多的模块之一,覆盖团队(组织)管理的核心读接口:
Organization类型是Space的别名再导出,注释说明这是为了既有消费方的源码兼容;checkSeats:POST /v1/organizations/{orgId}/check-seats,给定邮件列表检查席位是否足够,返回{ isAllowed, code? },code可能是NEEDS_TO_UPGRADE或NEEDS_TO_INCREASE_SEATS;getOrganizationRoles:GET /v1/organizations/roles;getOrganizationFeatures:GET /v1/organizations/{orgId}/features,返回 7 项功能开关(bulkImport、gitSync、orgBasicRbac、aiMockServers、aiCommitMessages、aiMcpClient、konnectSync,每项含enabled与可选reason)及billing状态;getOrganizationStorageRule:GET /v1/organizations/{orgId}/storage-rule,返回enableCloudSync/enableLocalVault/enableGitSync三个存储开关及isOverridden;getOrgUserPermissions:GET /v1/organizations/{orgId}/user-permissions,返回 15 个Permission权限位(如own:organization、create:invitation、leave:organization)到布尔值的映射;- 成员管理三件套:
deleteOrganizationMember(DELETE .../members/{userId})、updateUserRoles(PATCH .../members/{userId}/roles)、getOrganizationMemberRoles(GET .../members/{userId}/roles)。
这些函数与桌面端路由一一对应(如 collaborators-check-seats.tsx、[members.organizationId.members.$userId.roles.tsx?utm_source=gitcode_repo_files)),从源码结构看,路由文件就是这些 API 函数在 UI 层的直接消费者。
project.ts / collaborators.ts / invite.ts:团队项目与协作链路
project.ts 封装团队项目(team project)的 CRUD 与统计:
fetchTeamProjects/createTeamProject/updateTeamProject/deleteTeamProject,路径均在/v1/organizations/{orgId}/team-projects下;updateGitProjectCount:PATCH /v1/organizations/{orgId}/git-projects,上报 Git 同步项目数量。
collaborators.ts 走的是带 desktop 前缀的端点族,覆盖协作的完整生命周期:
getCollaborators:GET /v1/desktop/organizations/{orgId}/collaborators,支持per_page(默认 25)、page、filter三个查询参数,返回分页元数据 +Collaborator[](type为invite/member/group,metadata 含角色、邮件、过期时间等);searchCollaborators:GET .../collaborators/search/{keyword};startAddingCollaborators:POST .../collaborators/start-adding,提交{ teamIds, emails },服务端返回每个账号的publicKey(JSON Web Key 字符串)等「协作指令」;finishAddingCollaborators:POST .../collaborators/finish-adding,客户端用拿到的公钥加密项目密钥后,提交{ teamIds, keys, accountIds, roleId }完成添加——这是一条典型的端到端加密协作链路:先取公钥、本地加密、再提交密文;unlinkCollaborator:DELETE .../collaborators/{id}/unlink;getRealTimeCollaborators:POST /v1/organizations/{orgId}/collaborators,提交{ project, file }获取在线协作者的实时状态(UserPresence,含分支、文件、团队等)。
invite.ts 处理邀请与密钥协调:
reinvite(POST .../invites/{invitationId}/reinvite)、updateInvitationRole(PATCH .../invites/{invitationId})、revokeInvitation(DELETE .../invites/{invitationId});getMyProjectKeys/reconcileFileKeys:获取并协调「文件(项目)密钥」,涉及ProjectKey(encKey)、ProjectMember(publicKey)、MemberProjectKey(encSymmetricKey)等类型,说明密钥的分配与同步也是通过该包完成的。
mock.ts:Mockbin 服务
mock.ts 对接独立的 mockbin 服务(通过 origin: mockbinUrl 覆盖基址):
fetchMockbinLogs:GET {mockbinUrl}/bin/log/{compoundId},带insomnia-mock-method头,返回 HAR 格式的请求日志(MockbinLogOutput);upsertMockbin:PUT {mockbinUrl}/bin/upsert/{compoundId},写入一条Har.Response作为 mock 响应。
这与桌面端 mock-server 路由(如 [mock-route.organizationId.project.workspaceId.mock-server.mock-route.$mockRouteId.tsx?utm_source=gitcode_repo_files) 中对 upsertMockbin 与 isApiError 的调用)相印证:UI 里编辑一条 mock 规则,最终就是调用这里的函数完成远端 upsert。
enterprise.ts / trial.ts / vcs.ts:配额、试用与 GraphQL 代理
- enterprise.ts:
getResourceUsage(GET /v1/user/resource-usage,mock 配额与autoPurchase配置)、getOwnEnterprises(GET /v1/user/enterprises)、getAccountUsedSeats(GET /v1/accounts/seats,成员数/邀请数/已用/总量)、getEnterpriseLicenseUsage(GET /v1/enterprise/{enterpriseId}/license-usage,含free额度字段); - trial.ts:
getTrialEligibility(GET /v1/trials/eligibility)与startTrial(POST /v1/trials/start); - vcs.ts:
runVcsGraphQL以POST /graphql?{name}的形式代理云端 GraphQL 操作,name标识查询名,请求体为{ query, variables },返回{ data, errors }——Insomnia 的 Insomnia Sync(云端同步)正是通过这条 GraphQL 通道进行的。
测试与验证方式
包内自带 Vitest 单测,覆盖 SDK 封装(getSpaces 分页)与用户端点两条典型链路:
由于网络层是注入式的,测试只需 configureFetch 注入一个 stub 实现即可断言各函数的路径、方法与序列化行为,无需真实后端。在仓库中验证该包可执行(只读查看即可,不需要修改仓库):
cd packages/insomnia-api
npm run test # vitest run
npm run type-check # tsc --noEmit
npm run lint
小结
insomnia-api 的价值在于把「与 Insomnia Cloud 通信」这件事收敛成一个纯 TypeScript、零构建、依赖注入的库:
- 入口与类型全部从包根导出(src/index.ts),消费方一行
import ... from 'insomnia-api'即可拿到函数与类型; - 网络实现与包解耦:
configureFetch一次性注入,桌面端两个入口(entry.client.tsx、entry.main.ts)注入代理感知的insomniaFetch,v3 SDK 侧则由configureV3Client完成类似注入; - 错误模型统一:
ResponseFailError+isApiError让 UI 路由层能一致地展示服务端错误; - 领域划分清晰:user / vault / organizations / collaborators / invite / project / mock / enterprise / trial / spaces / vcs 共 11 个模块,端点与方法名一一对应,便于按功能检索与复用。
如果你要为 inso CLI 或新的客户端接入 Insomnia Cloud,这个包就是应当遵循的封装范式:注入自己的 Fetch 实现、按领域调用具名函数、用 isApiError 判定失败原因。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00