# @fernandoslim/odoo-jsonrpc 中文文档

规范地址：https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/

版本：2\.2\.0
npm 元数据更新时间：2026-10-08T01:40:27.033Z
中文译文更新时间：2026-11-14T14:47:42.903Z
软件包许可：ISC

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40fernandoslim/odoo-jsonrpc
原文对照：https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/?lang=original
项目主页：https://github.com/fernandoslim/odoo-jsonrpc#readme
源码仓库：https://github.com/fernandoslim/odoo-jsonrpc

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：171 kB，10 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=18
- 运行依赖：0 个
- 首次发布：2024-06-03T21:08:34.663Z
- 最新版本发布：2026-10-08T01:40:26.871Z
- 近 30 天下载量：19812（2026-10-15 至 2026-11-13，npm 官方下载统计）
- 已发布版本数：26（近 12 个月 2 个）

## 中文简介

JSON-RPC和JSON-2 API(Odoo 19+)的轻量级Odoo客户端,零依赖.

## 安装

```sh
npm install @fernandoslim/odoo-jsonrpc
```

也可以使用：`pnpm add @fernandoslim/odoo-jsonrpc`、`yarn add @fernandoslim/odoo-jsonrpc`、`bun add @fernandoslim/odoo-jsonrpc`

## README

# 奥多·杰森-RPC

JSON- RPC 和新的轻量级 Odoo 客户端 **JSON-2 API(奥多19+)**零依赖关系。 类型脚本第一. 在节点18+, Bun, Deno,和Cloudflare工人上工作.

依据 [奥杜阿等](https://github.com/vettloffah/odoo-await) (XML-RPC). 感谢 [维特洛法](https://github.com/vettloffah)。 。 。 .

-   [特征](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-features)
-   [安装](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-install)
-   [快速启动](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-quick-start)
-   [认证](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-authentication)
-   [JSON-2(奥杜19+)](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-json-2-odoo-19)
-   [选项](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-options)
-   [API 参考](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-api-reference)
-   [拥挤](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-crud)
-   [搜索读取( S)](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-search--search-read)
-   [域过滤器](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-domain-filters)
-   [关系场(多为2个/一为2个)](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-relational-fields-many2many--one2many)
-   [外地翻译](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-field-translations)
-   [外部标识符](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-external-identifiers)
-   [错误](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-errors)
-   [处理错误 `Try`](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-error-handling-with-try)
-   [断开连接](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-disconnect)
-   [类型脚本](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-typescript)
-   [基准](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-benchmarks)
-   [更改日志](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-changelog)
-   [许可证](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-license)

## 特征

-   零运行时间依赖关系
-   双重无害环境管理+共同JS与 `.d.ts` + 键 `.d.cts`
-   两项协议:JSON-RPC(每个Odoo版本)和JSON-2(Odoo 19+,Odoo 22后留下的)
-   JSON- RPC 的三个认证模式: 证书、 API 密钥、 现有会话
-   `fetch`\-基于 到处跑 `fetch` 可用
-   小表面 : `create`, (中文(简体) ), `read`, (中文(简体) ), `update`, (中文(简体) ), `delete`, (中文(简体) ), `search`, (中文(简体) ), `searchRead`, (中文(简体) ), `action`, (中文(简体) ), `call`, (中文(简体) ), `call_kw`,和外部ID帮助器——两个协议的代码相同
-   已打入错误( E)`OdooError`)带有HTTP状态,型号,方法和Odoo的例外名
-   可选请求超时
-   Go 风格错误帮助器( S)`Try`避免尝试/捕获噪音

## 安装

```bash
npm install @fernandoslim/odoo-jsonrpc
# or
pnpm add @fernandoslim/odoo-jsonrpc
# or
bun add @fernandoslim/odoo-jsonrpc
```

德诺/JSR:

```bash
deno add jsr:@fernandoslim/odoo-jsonrpc
```

## 快速启动

```ts
import OdooJSONRpc from '@fernandoslim/odoo-jsonrpc';

const odoo = new OdooJSONRpc({
  baseUrl: process.env.ODOO_BASE_URL!, // e.g. https://my-odoo.example.com (port is optional)
  db: process.env.ODOO_DB!,
  username: process.env.ODOO_USERNAME!,
  password: process.env.ODOO_PASSWORD!,
});

await odoo.connect();

const partnerId = await odoo.create('res.partner', {
  name: 'Kool Keith',
  email: 'lostinspace@example.com',
});

const [partner] = await odoo.read<{ id: number; name: string; email: string }>(
  'res.partner',
  partnerId,
  ['name', 'email']
);
```

## 认证

`connect()` **必须** 在任何其他方法之前调用(但 `initialize`) (中文(简体) ).

### 有用户名+密码

返回完整的会话响应,包括 `username`, (中文(简体) ), `partner_id`, (中文(简体) ), `server_version`等 (中文(简体) ). 另设置一个会话 cookie 供后续调用 .

```ts
const odoo = new OdooJSONRpc({
  baseUrl: 'https://my-odoo.example.com',
  port: 443,
  db: 'my-db',
  username: 'admin',
  password: 'secret',
});
await odoo.connect();
```

### 带有 API 密钥

轻取. 仅返回 `{ uid }`请求使用 `execute_kw` 通过 `/jsonrpc`。 。 。 .

```ts
const odoo = new OdooJSONRpc({
  baseUrl: 'https://my-odoo.example.com',
  port: 443,
  db: 'my-db',
  username: 'admin',
  apiKey: 'c721a30555935cbabe8851df3f3eb9e60e850711',
});
await odoo.connect();
```

### 带有当前会话标识

重用来自另一个来源的会话( cookie, 缓存等) .

```ts
const odoo = new OdooJSONRpc({
  baseUrl: 'https://my-odoo.example.com',
  port: 443,
  db: 'my-db',
  sessionId: '12eb065d6b17d27723a72f5dcb0d85071ae346e2',
});
await odoo.connect();
```

### 关于反应类型的分支

`connect()` 返回( n) `OdooAuthenticateWithCredentialsResponse` 或者说 `OdooAuthenticateWithApiKeyResponse`。使用 `isCredentialsResponse` 类型守护 :

```ts
import OdooJSONRpc, { isCredentialsResponse } from '@fernandoslim/odoo-jsonrpc';

const auth = await odoo.connect();
if (isCredentialsResponse(auth)) {
  console.log('Logged in as', auth.username);
} else {
  console.log('Logged in with API key, uid:', auth.uid);
}
```

### 云花岗工人

```ts
import { Hono } from 'hono';
import OdooJSONRpc, { Try } from '@fernandoslim/odoo-jsonrpc';

type Bindings = {
  ODOO_BASE_URL: string;
  ODOO_PORT: number;
  ODOO_DB: string;
  ODOO_USERNAME: string;
  ODOO_API_KEY: string;
};

const app = new Hono<{ Bindings: Bindings }>();
const odoo = new OdooJSONRpc();

app.use('/odoo/*', async (c, next) => {
  if (!odoo.is_connected) {
    await odoo.connect({
      baseUrl: c.env.ODOO_BASE_URL,
      port: c.env.ODOO_PORT,
      db: c.env.ODOO_DB,
      username: c.env.ODOO_USERNAME,
      apiKey: c.env.ODOO_API_KEY,
    });
  }
  return next();
});

app.get('/odoo/contacts/:id', async (c) => {
  const id = Number(c.req.param('id'));
  const [contacts, error] = await Try(() =>
    odoo.read<{ id: number; name: string; email: string }>('res.partner', id, ['name', 'email'])
  );
  if (error) return c.text(error.message, 422);
  if (!contacts.length) return c.text('not found', 404);
  return c.json(contacts[0], 200);
});
```

## JSON-2(奥杜19+)

Odoo 19介绍了外部的JSON-2 API(`POST /json/2/<model>/<method>`,无记名 API 密钥)并贬值 `/jsonrpc` 财务报告和审定财务报表 `/xmlrpc`,哪个 **Odoo 22 删除**。通过 `protocol: 'json2'`;其他一切不变.

```ts
const odoo = new OdooJSONRpc({
  baseUrl: 'https://my-odoo.example.com',
  db: 'my-db', // sent as X-Odoo-Database; optional on single-database servers
  apiKey: process.env.ODOO_API_KEY!,
  protocol: 'json2',
});

await odoo.connect(); // optional: checks the key and returns { uid }
const partners = await odoo.searchRead('res.partner', [['is_company', '=', true]], ['name'], { limit: 10 });
```

-   没有登录步骤:每个请求都带有密钥. `connect()` 只是验证它(`res.users.context_get`)),而第一个调用单独连接.
-   不需要用户名: 密钥识别用户 .
-   所有帮助者( A)`create`, (中文(简体) ), `read`, (中文(简体) ), `update`, (中文(简体) ), `delete`, (中文(简体) ), `search`, (中文(简体) ), `searchRead`, (中文(简体) ), `action`, (中文(简体) ), `getFields`, (中文(简体) ), `updateFieldTranslations`,外部IDs)发送命名参数JSON-2预期.
-   用于任何其他方法 **`call(model, method, params)`** 带有已命名参数: `ids`, (中文(简体) ), `context`,而且方法本身的论据.

```ts
await odoo.call('sale.order', 'action_confirm', { ids: [42] });
const count = await odoo.call<number>('res.partner', 'search_count', { domain: [['customer_rank', '>', 0]] });
```

-   `call_kw(model, method, args, kwargs)` 是立场。 在JSON-2上,它只在没有立场争论的情况下工作,或者只使用ID(英语:`[[42]]`); 任何其他抛出并指向您 `call()`。 。 。 .
-   `call()` 也从事JSON-RPC的研究工作(成为 `execute_kw` ,但参数名称跟随服务器:Odoo 17之前, `search` 带 `args`没有 `domain`。 。 。 .

API 密钥在 Odoo 下创建 _首选项 — 账户安全 — 新建 API 密钥_。对于非管理者,Odoo限制键的持续时间.

## 选项

| 选项 | 默认 | 含义 |
| --- | --- | --- |
| `baseUrl` | —— 说 | 例如服务器 URL. `https://my-odoo.example.com` |
| `port` | —— 说 | 可选文件:附件 `baseUrl` 当给定时 |
| `db` | —— 说 | 数据库. JSON-RPC规定;JSON-2可选 |
| `protocol` | `'jsonrpc'` | `'jsonrpc'` 或者说 `'json2'` |
| `timeoutMs` | 无 | 中止需要更长时间的任何请求; 它扔出一个 `OdooError` |
| `username` + 键 `password` 编号 : `apiKey` 编号 : `sessionId` | —— 说 | 全权证书(JSON-2仅使用) `apiKey`(中文(简体) ) |

## API 参考

| 方法 | 签署 | 回返 |
| --- | --- | --- |
| `connect` | `connect(config?)` | 认证回复 |
| `disconnect` | `disconnect()` | `boolean` |
| `call` | `call(model, method, params?)` | `any` (名称参数) |
| `call_kw` | `call_kw(model, method, args, kwargs?)` | `any` (职位) |
| `create` | `create(model, values)` | `number` (编) |
| `read<T>` | `read(model, id | ids, fields)` | `T[]` |
| `update` | `update(model, id, values)` | `boolean` |
| `delete` | `delete(model, id)` | `boolean` |
| `search` | `search(model, domain)` | `number[]` |
| `searchRead<T>` | `searchRead(model, domain, fields, opts?)` | `T[]` |
| `getFields` | `getFields(model)` | 字段映射 |
| `action` | `action(model, action, ids)` | `boolean` |
| `updateFieldTranslations` | `updateFieldTranslations(model, id, field, translations)` | `boolean` |
| `createExternalId` | `createExternalId(model, recordId, externalId, moduleName?)` | `number` |
| `searchByExternalId` | `searchByExternalId(externalId)` | `number` |
| `readByExternalId<T>` | `readByExternalId(externalId, fields?)` | `T` |
| `updateByExternalId` | `updateByExternalId(externalId, params)` | `boolean` |
| `deleteByExternalId` | `deleteByExternalId(externalId)` | `boolean` |

如果您需要的方法没有包起来, 请使用 `call` (名称参数)或 `call_kw` (职). 见 [外部 API](https://www.odoo.com/documentation/19.0/developer/reference/external_api.html)在Odoo 19+上, `/doc` 在服务器上记录每一个模型和方法.

## 拥挤

### 创建

```ts
const partnerId = await odoo.create('res.partner', { name: 'Kool Keith' });
```

> 要在创建时附加外部ID, 请打电话 `createExternalId` 后来 见 [外部标识符](https://npmcn.com/package/%40fernandoslim/odoo-jsonrpc/#readme-external-identifiers)。 。 。 .

### 读取

返回数组。 通过一个单一的ID或一系列的ID. 总是通过一个 `fields` 列表 — 基本模型( 如 `res.partner`)有100+字段.

```ts
type Partner = { id: number; name: string; email: string };
const records = await odoo.read<Partner>('res.partner', [54, 1568], ['name', 'email']);
```

### 更新

```ts
const ok = await odoo.update('res.partner', 54, { street: '334 Living Astro Blvd.' });
```

### 删除

```ts
const ok = await odoo.delete('res.partner', 54);
```

### 服务器动作

```ts
await odoo.action('account.move', 'action_post', [126996, 126995]);
```

> Odoo 服务器动作通常返回 `false` 在成功方面.

## 搜索读取( S)

### 搜索

返回匹配的记录ID.

```ts
const ids = await odoo.search('res.partner', [['country_id', '=', 'US']]);
```

### 搜索读取

返回匹配选定字段的记录。 支助 `offset`, (中文(简体) ), `limit`, (中文(简体) ), `order`,以及 `context`。 。 。 .

```ts
type Partner = { id: number; name: string; city: string };
const records = await odoo.searchRead<Partner>(
  'res.partner',
  [['country_id', '=', 'US']],
  ['name', 'city'],
  { limit: 5, offset: 10, order: 'name desc', context: { lang: 'en_US' } }
);

// Empty domain = all records
const all = await odoo.searchRead<Partner>('res.partner', [], ['name', 'city'], { limit: 10 });
```

## 域过滤器

Odoo 域为 三重数组 `[field, operator, value]`。普通运算符: `=`, (中文(简体) ), `!=`, (中文(简体) ), `<`, (中文(简体) ), `>`, (中文(简体) ), `<=`, (中文(简体) ), `>=`, (中文(简体) ), `like`, (中文(简体) ), `=like`, (中文(简体) ), `ilike`, (中文(简体) ), `in`, (中文(简体) ), `not in`, (中文(简体) ), `child_of`。逻辑运算符: `|` (或), `&` (AND,隐含), `!` (无).

完整列表 : [Odoo ORM 域](https://www.odoo.com/documentation/17.0/developer/reference/backend/orm.html#search-domains)。 。 。 .

```ts
// Single triple
await odoo.search('res.partner', [['name', '=like', 'john%']]);

// Multiple triples (AND is implicit)
await odoo.search('res.partner', [
  ['name', '=like', 'john%'],
  ['sale_order_count', '>', 1],
]);

// OR: email = X OR name ilike Y
await odoo.searchRead('res.partner', [
  '|',
  ['email', '=', 'charlie@example.com'],
  ['name', 'ilike', 'charlie'],
]);
```

## 关系场(多为2个/一为2个)

数值传递给奥多 **原始内容**。使用 Odoo 本地语言 [命令调图](https://www.odoo.com/documentation/17.0/developer/reference/backend/orm.html#odoo.fields.Command)数字 :

| 命令 | 含义 |
| --- | --- |
| `[0, 0, {values}]` | 创建新的相关记录并将其链接 |
| `[1, id, {values}]` | 更新现有相关记录 |
| `[2, id]` | 解开链接 **删除** 从 DB |
| `[3, id]` | 不删除而解链接 |
| `[4, id]` | 链接现有记录 |
| `[5]` | 全部取消链接( 无删除) |
| `[6, 0, [ids]]` | 用给定的ID替换链接设置 |

实例:

```ts
// Create a new related category on the fly
await odoo.update('res.partner', 278, {
  category_id: [[0, 0, { name: 'A new category' }]],
});

// Update an existing related category
await odoo.update('res.partner', 278, {
  category_id: [[1, 3, { name: 'Updated category' }]],
});

// Link existing categories (ids 3, 12, 6)
await odoo.update('res.partner', 278, {
  category_id: [[6, 0, [3, 12, 6]]],
});

// Unlink without deleting
await odoo.update('res.partner', 278, {
  category_id: [[3, 5]],
});

// Unlink and delete from DB
await odoo.update('res.partner', 278, {
  category_id: [[2, 5]],
});
```

## 外地翻译

更新跨语言的可变字段.

```ts
await odoo.updateFieldTranslations('product.template', 1, 'name', {
  de_DE: 'Neuer Name',
  en_GB: 'New name',
});
```

## 外部标识符

外部ID( 存储于 `ir.model.data`),让您通过一个稳定的跨系统的密钥参考记录——用于CSV的导入和第三方同步.

默认模块名称是 `__api__`因此,一个外部的ID喜欢 `'sku-42'` 变成 `'__api__.sku-42'` 在数据库中。 您可以使用 `moduleName` 参数 `createExternalId`。当你抬头看时,你会 **没有** 需要模块前缀.

```ts
// Link an external ID to an existing record
await odoo.createExternalId('product.product', 76, 'sku-42');

// Find id by external ID
const id = await odoo.searchByExternalId('sku-42');

// Read fields by external ID
const record = await odoo.readByExternalId<{ name: string; list_price: number }>(
  'sku-42',
  ['name', 'list_price']
);

// Update by external ID
await odoo.updateByExternalId('sku-42', { name: 'space shoe', list_price: 65479.99 });

// Delete by external ID
await odoo.deleteByExternalId('sku-42');
```

## 错误

每一次失败都会有 `OdooError` (扩展 `Error`:网络出错,超时出错,HTTP出错,回应不是JSON(一个代理的HTML页面),一个被拒绝的登录和由Odoo提出的出错.

```ts
import { OdooError } from '@fernandoslim/odoo-jsonrpc';

try {
  await odoo.action('sale.order', 'action_confirm', [42]);
} catch (error) {
  if (error instanceof OdooError) {
    error.message; // Odoo's message, e.g. "You cannot confirm an order without lines"
    error.status; // HTTP status (JSON-2 uses real ones: 401, 403, 404, 422…)
    error.exceptionName; // e.g. 'odoo.exceptions.UserError'
    error.model; // 'sale.order'
    error.method; // 'action_confirm'
  }
}
```

Odoo 拒绝的登录 `connect()` (地位401).

## 处理错误 `Try`

`Try` 完成承诺并返回 `[result, null] | [null, error]` 没有 `try/catch` 锅炉板.

```ts
import OdooJSONRpc, { Try } from '@fernandoslim/odoo-jsonrpc';

const [contacts, error] = await Try(() =>
  odoo.read<{ id: number; name: string }>('res.partner', 54, ['name'])
);
if (error) throw error;
if (!contacts.length) throw new Error('Contact not found');
const [contact] = contacts;
```

现实世界的例子——创建和确认销售令:

```ts
export const createSalesOrder = async (data: SalesOrder) => {
  const [id, createErr] = await Try(() => odoo.create('sale.order', data));
  if (createErr) throw createErr;

  const [, confirmErr] = await Try(() => odoo.action('sale.order', 'action_confirm', [id]));
  if (confirmErr) throw confirmErr;

  return id;
};
```

## 断开连接

在服务器上结束会话(证书/会话ID模式)。 拥有 API 密钥没有服务器会话: 它只清除本地状态 .

```ts
await odoo.disconnect();
```

## 类型脚本

所有出口都完全输入。 关键类型 :

```ts
import type {
  OdooConnection,
  OdooProtocol,
  OdooCallParams,
  ConnectionWithCredentials,
  ConnectionWithSession,
  OdooSearchDomain,
  OdooSearchReadOptions,
  OdooAuthenticateWithCredentialsResponse,
  OdooAuthenticateWithApiKeyResponse,
  UserContext,
  UserSettings,
} from '@fernandoslim/odoo-jsonrpc';
```

`read<T>` 财务报告和审定财务报表 `searchRead<T>` 接受行形状的通则,所以可以不进行铸造而输入记录.

## 基准

合成基准 [浩诺](https://github.com/honojs) —— 说 `hey -n 2000 -c 80`数字 :

|  | 区/区 | 丑闻 |
| --- | --- | --- |
| JSON-RPC 软件公司 | 第617.11号 | 122分钟 |
| XML - RPC 相机 | 352.98 (英语) | 213分钟 |

JSON-RPC每秒处理的请求增加~75%,运行速度平均为~43%.

## 更改日志

### 2.2.0 (中文(简体) )

-   **JSON-2(奥杜19+)**数字 : `protocol: 'json2'`无记名 API 键, `X-Odoo-Database`所有帮助者都致力于两个协议.
-   `call(model, method, params)` 带有命名参数.
-   `OdooError` 与 `status`, (中文(简体) ), `model`, (中文(简体) ), `method` 财务报告和审定财务报表 `exceptionName` 对每一个失败.
-   `timeoutMs` 选项.
-   `port` 可选 : `baseUrl` 也可以是满的 `https://…` URL (中文(简体) ).
-   修正: Odoo 拒绝的登录没有丢任何东西(`uid: false` 计算为连接);它现在投出 `connect()`。 。 。 .
-   修正: 错误被返回 `/web/session/*` 未检测到.
-   修补 : `action()` 以若干 ids 作为单独参数发送它们; 它现在发送列表 .
-   修补 : `create()` 财务报告和审定财务报表 `createExternalId()` 返回其类型表示数字的列表(一个数值列表仍然返回列表).
-   修补 : `disconnect()` 带有一个 API 键被扔出; 现在它清除了当地的州.
-   测试(Vites)针对一个能说出两个协议的默默奥杜语,加上对一个真实服务器的可选的只读检查(`tests/live.test.ts`) (中文(简体) ).

## 许可证

ISC——版权2024 Fernando Delgado. 全文见资料来源.

## 常见问题

### @fernandoslim/odoo-jsonrpc 是什么？

JSON-RPC和JSON-2 API\(Odoo 19\+\)的轻量级Odoo客户端,零依赖\.

### 如何安装 @fernandoslim/odoo-jsonrpc？

在项目目录运行 npm install @fernandoslim/odoo-jsonrpc，或使用 pnpm add / yarn add / bun add。

### @fernandoslim/odoo-jsonrpc 支持 TypeScript 吗？

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

### @fernandoslim/odoo-jsonrpc 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @fernandoslim/odoo-jsonrpc 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=18。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @fernandoslim/odoo-jsonrpc 可以商用吗？

@fernandoslim/odoo-jsonrpc 使用 ISC 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @fernandoslim/odoo-jsonrpc 的下载量有多少？

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

### @fernandoslim/odoo-jsonrpc 还在维护吗？

最新版本 2\.2\.0 发布于 2026/10/08（1 个月前）；至今共发布 26 个版本，其中过去 12 个月发布了 2 个；首次发布于 2024/06/04。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

