# Xrk Node Runtime

> 编写或审查 core/src 代码时，确保使用 Node 26 稳定 API，禁止旧写法（fetch/exec/错误/二进制）。AI 改 Core 前必读。

- Skill: `sunflowermm/xrk-node-runtime` (Agent Skill)
- Install (CLI): `npx skillmds@latest add sunflowermm/xrk-node-runtime`
- Raw SKILL.md: https://api.skillmd.com/api/skills/sunflowermm/xrk-node-runtime/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: sunflowermm (https://skillmd.com/u/sunflowermm)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/sunflowermm/xrk-node-runtime

---


## 文档

- 权威清单：`docs/node-26-runtime.md`
- 写法与性能（非 Node 专项）：`docs/coding-style.md` · skill **`xrk-coding-style`**
- 工具模块：`docs/底层架构设计.md` §1 工具表

## 版本

- **要求**：Node ≥ 26.0（推荐 / 实测 **26.7.x**，至少 ≥ 26.5.1）；`package.json` engines 与 `app.js` 启动校验一致。
- **勿用**：Node 24 特性检测回退、polyfill、未在文档列出的实验 API（如 `node:ffi`，除非用户明确要求）。

---

## Core / 业务代码（`core/**/*.js`）必用

| 场景 | 正确写法 | 禁止 |
|------|----------|------|
| HTTP 出站 | 全局 `fetch(url, { signal: AbortSignal.timeout(ms), ...opts })` | `node-fetch`、`import fetch from ...` |
| LLM 代理 | `buildFetchOptionsWithProxy(config, opts)`（`#utils/llm/proxy-utils.js`） | `https-proxy-agent`、`options.agent` |
| Shell 命令 | `import { exec } from '#utils/exec-async.js'`（有 `#` 的 core）或相对路径到 `src/utils/exec-async.js` | `util.promisify(exec)`、`child_process/promises` |
| 流式 Shell | 需 `stdout`/`stderr` 实时流时可用 `child_process.exec` 回调 API（如 `remote-command.js`） | 在普通 await 场景自写 promisify |
| 判错 | `Error.isError(err)`；需包装时用 `normalizeError(err)`（`#utils/normalize-error.js`） | `err instanceof Error` |
| Buffer 编码 | `buf.toBase64()`、`buf.toHex()`、`Uint8Array.fromBase64(s)` | `buf.toString('base64'|'hex')`、`Buffer.from(s,'base64')` |
| Map 初始化 | `map.getOrInsert(k, () => ({ ... }))` / `getOrInsertComputed` | `map.get(k) \|\| (map.set(k,v), v)` 样板（可写时） |
| 下载文件 | `fetch` + `Readable.fromWeb` + `pipeline`（见 `subserver-client.fetchSubserverToPath`） | 自写 `node-fetch` pipeline |
| 路径匹配 | `new URLPattern({ pathname })` | 手写 regex fallback、`globalThis.URLPattern ?` |
| URL-safe Base64 | `buf.toBase64({ alphabet: 'base64url' })`、`Uint8Array.fromBase64(s, { alphabet: 'base64url' })` | 手写 base64url 替换 |
| 文件 glob | `import { glob } from 'glob'`（`RuntimeUtil.glob` 已封装） | 动态加载 fast-glob、自写 `#getGlobLib` |

## 基础设施（`src/**/*.js`）

- 同上；**唯一**允许 `util.promisify(exec)` 的位置：`src/utils/exec-async.js`。
- 错误：`errorHandler` + `normalizeError`；日志：`Error.isError` 分支（见 `log.js`）。

## 代码示例（Core HTTP handler）

```javascript
import { HttpResponse } from '#utils/http-utils.js';
import { normalizeError } from '#utils/normalize-error.js';

export default {
  routes: [{
    method: 'GET',
    path: '/api/example/ping',
    handler: HttpResponse.asyncHandler(async (req, res) => {
      const resp = await fetch('https://example.com', {
        signal: AbortSignal.timeout(5000)
      });
      if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
      return HttpResponse.success(res, await resp.text());
    }, 'example')
  }]
};
```

## 代码示例（Core 工作流 / 插件 catch）

```javascript
} catch (err) {
  const error = normalizeError(err);
  RuntimeUtil.makeLog('error', error.message, 'MyStream');
}
```

## 代码示例（Shell / 热重载）

```javascript
import { exec } from '#utils/exec-async.js';

const { stdout, stderr } = await exec('node --version');
// 至 26.7 仍无 node:child_process/promises — 勿在各文件 promisify(exec)
```

## 代码示例（二进制 / 设备路径）

```javascript
const fileId = Buffer.from(filePath, 'utf8').toBase64({ alphabet: 'base64url' });
const path = Buffer.from(Uint8Array.fromBase64(fileId, { alphabet: 'base64url' })).toString('utf8');
```

## 审查清单（改 Core 前自检）

- [ ] 无 `node-fetch`、`https-proxy-agent`
- [ ] 无 `import ... child_process/promises`
- [ ] 无文件内 `promisify(exec)`（应用 `exec-async.js`）
- [ ] 无 `AbortController` + `setTimeout(abort)`
- [ ] 无 `instanceof Error` 作判错（展示类型名除外）
- [ ] 无 `URLPattern` / `Error.isError` 存在性检测
- [ ] 无 `toString('base64'|'hex')` / `Buffer.from(s,'base64')` 新代码
- [ ] Core 改底层需求：提 issue/说明，**不直接改** `src/infrastructure/`、`src/utils/`（见 `xrk-core-code.mdc`）

