# @byokit/discover 中文文档

规范地址：https://npmcn.com/package/%40byokit/discover/

版本：0\.8\.7
npm 元数据更新时间：2026-10-25T11:19:13.280Z
中文译文更新时间：2026-11-08T21:42:56.838Z
软件包许可：Apache-2\.0

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

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

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

- 包体积（解压后）：122 kB，27 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22\.18
- 运行依赖：3 个
- peer 依赖：expo-modules-core \>=3\.0\.0
- 首次发布：2026-10-06T13:55:16.889Z
- 最新版本发布：2026-10-25T11:19:12.733Z
- 近 30 天下载量：3814（2026-10-09 至 2026-11-07，npm 官方下载统计）
- 已发布版本数：11（近 12 个月 11 个）

## 中文简介

TypeScript套件让人们通过他们已经支付的人工智能计划,在网络,电话和桌面上登录到你的应用程序.

## 安装

```sh
npm install @byokit/discover
```

也可以使用：`pnpm add @byokit/discover`、`yarn add @byokit/discover`、`bun add @byokit/discover`

## README

# @byokit/发现

[![npm](https://img.shields.io/npm/v/@byokit/discover?style=flat&label=npm)](https://www.npmjs.com/package/@byokit/discover) [![CI](https://img.shields.io/github/actions/workflow/status/umeranjum17/byokit/ci.yml?style=flat&branch=main)](https://github.com/umeranjum17/byokit/actions/workflows/ci.yml) [![Apache 2.0](https://img.shields.io/badge/license-Apache--2.0-666?style=flat)](https://npmcn.com/package/%40byokit/discover/LICENSE) ![Node | React Native](https://img.shields.io/badge/platform-Node%20%7C%20React%20Native-666?style=flat)

**电话地址可以拨家庭电脑.**  
对于 `@byokit/pair`是因为 `offer({ urls })`: 私人隧道服务到一个人的尾网,直接 加密链接,另一个私人覆盖网络或局域网. 在节点上还可以通过mDNS对计算机进行广告宣传; 在电话(React Industrial)中,它浏览周围的mDNS服务.

原为 `@byokit/reach`,它继续作为已贬值的shim通过0.7.x再将这个软件包输出出去 并在0.8.0中删除.

## 安装

```sh
npm install @byokit/discover
```

[![npm](https://img.shields.io/npm/v/@byokit/discover?style=flat&label=)](https://www.npmjs.com/package/@byokit/discover) • 妇女 [最近发布](https://github.com/umeranjum17/byokit/releases?q=discover-v) • 妇女 [所有释放](https://github.com/umeranjum17/byokit/releases)

## iOS 设置( Expo)

iOS 只返回本地网络 mDNS 的结果, 当应用程序声明它浏览的 Bonjour 服务类型和 a 使用说明。 添加配置插件并列出您通过的类型 `browse()`数字 :

```json
{
  "expo": {
    "plugins": [["@byokit/discover", { "services": ["_byokit._tcp"] }]]
  }
}
```

`services` 需要且必须是非空; 空列表未按名称预建而非写入 空的 Bonjour 列表 . `localNetwork` 覆盖 iOS 提示中显示的使用字符串( 合理的默认) 用于省略时。 预建写入 `NSBonjourServices` 财务报告和审定财务报表 `NSLocalNetworkUsageDescription` 输入 `Info.plist`; 在添加插件后重建本地二进制 。 Android 不需要插件 : `react-native-zeroconf`自己的清单已经合并 `CHANGE_WIFI_MULTICAST_STATE`。 。 。 .

## 快速启动

```sh
npm install @byokit/discover
```

确定8792端口服务器的地址, 聆听联系人说的地方, 把地址放入对齐 提供。 持久性 `ingress` 并传回作为 `previous` {\\fn黑体\\fs22\\bord1\\shad0\\3ahbe\\4ah00\\fscx67\\fscy66\\2chffffff\\3ch808080}下次再说.

```ts
import { createServer } from 'node:http';
import { reach } from '@byokit/discover';

const { urls, bind, ingress, pendingCleanup } = await reach({ port: 8792, previous: saved.ingress });
// One listener per address reach names, never a wildcard: loopback behind Serve, the selected tailnet IP for
// direct Tailscale, the classified LAN (or overlay) addresses for those routes.
const servers = bind.map((address) => createServer(handler).listen(8792, address));
saved.ingress = ingress;
if (pendingCleanup) saved.pendingCleanup = pendingCleanup; // retry unserve() later
const { text } = host.offer({ role: 'control', urls });     // show `text` as a QR
```

带有由编造而成的界面列表的局域网路由,因此它从不触及Tailscale或网络. 多克的桥是 跳过; NetBird 风格 `wt0` 地址为 `private` 路线,而不是局域网 :

```ts
import type { NetworkInterfaceInfo } from 'node:os';
import { reach, routes } from '@byokit/discover';

const nic = (address: string): NetworkInterfaceInfo =>
  ({ address, netmask: '255.255.255.0', family: 'IPv4', mac: '02:00:00:00:00:01', internal: false, cidr: `${address}/24` });
const interfaces = { en0: [nic('192.168.1.20')], docker0: [nic('172.17.0.1')], wt0: [nic('100.90.1.2')] };

console.log(routes(interfaces));
console.log(await reach({ port: 8792, via: 'lan', interfaces }));
```

```text
{
  lan: [ '192.168.1.20' ],
  private: [ { address: '100.90.1.2', interface: 'wt0' } ],
  tailscale: []
}
{ urls: [ 'ws://192.168.1.20:8792' ], bind: [ '192.168.1.20' ] }
```

## API 一览

| 导出 | 它会做什么 |
| --- | --- |
| `reach({ port, via?, previous?, tailscale?, interfaces?, address? })` | 拨号 `urls`,则 `bind` 收听的地址(每个听众一个,从来不是通配符) `ingress` 持续(加) `pendingCleanup` 当删除失败时) |
| `directRoutes({ port, listen?, interfaces?, tailnetIPs?, hosts?, path? })` | 多个直接收听器 `hosts` 和配对 `urls`,带有明确的回放/尾网/局域网范围 |
| `phoneNetwork({ nativeModule? })` | 电话Wi-Fi/手机运输和VPN状态(`yes`, (中文(简体) ), `no`, (中文(简体) ), `unknown`(中文(简体) ) |
| `nativeAddresses({ nativeModule? })` | IPv4接口证据,包括实际前缀长度; 4. 节点默认读取其界面 |
| `routeOf(url)` | 地址提示 : `home`, (中文(简体) ), `tailscale`, (中文(简体) ), `relay`, (中文(简体) ), `loopback` 或者说 `unknown` |
| `observe({ urls, priorEvidence?, nativeModule?, addresses?, probe? })` | 当前前缀证据和有界限的探测器,保留宿主先前的证据 |
| `probe(url, { timeout?, fetch? })` | 类型 `answers`, (中文(简体) ), `refused`, (中文(简体) ), `timeout` 或者说 `unknown` 观察 |
| `routes(interfaces?, tailnetIPs?)` | 这台计算机的IPv4路线: `lan` 地址:, `private` 覆盖地址和 `tailscale` 地址 |
| `recommend({ port?, interfaces?, tailscale?, state?, serve?, lan?, private?, current? })` | 每一条日常语言的路线,首先推荐: `{ via, recommended, sentence, needs, disabledReason? }` 每条路线 |
| `advertise({ type, port, name?, txt?, addresses? })` | 出版物 `_<type>._tcp` 超过 mDNS(节点),仅使用所选地址;返回 `{ stop }` |
| `tailscaleState({ bin?, timeoutMs? })` | 非投掷安装、后端、登录、密钥过期、对等和地址诊断 |
| `firewall({ port, platform?, run?, timeoutMs? })` | 本地防火墙是否在端口上被挡入 : `{ blocked, how, nextStep }`,在不访问管理员的情况下读取 |
| `needsSignin(status)`, (中文(简体) ), `isPeer(status, ip)` | 原始纯帮助 `tailscale status --json`: 明确的登录状态和同行IP成员 |
| `tailscaleStatus`, (中文(简体) ), `tailscaleName`, (中文(简体) ), `magicDnsName` | 读取本地 Tailscale 状态和此机器的 MagicDNS 名称 |
| `inspectServe`, (中文(简体) ), `serveRootProxy` | 检查谁持有服务根 : `free`, (中文(简体) ), `ours`, (中文(简体) ), `occupied`, (中文(简体) ), `funnel`, (中文(简体) ), `disabled` 或者说 `inconclusive` |
| `serve`, (中文(简体) ), `unserve` | 制作或删除应用程序的“ 服务” 映射, 并进行所有权检查 |
| `SERVE_OWNED_ERROR`, (中文(简体) ), `FUNNEL_ERROR` | 带漏斗的根和根的错误消息 |
| `browse({ type })`, (中文(简体) ), `scan({ type, ms })` | 只反应本地条目: 发现 mDNS 服务, 流式或时间框 |
| 类型 | `Via`, (中文(简体) ), `Reach`, (中文(简体) ), `RecommendVia`, (中文(简体) ), `RecommendEntry`, (中文(简体) ), `RecommendOptions`, (中文(简体) ), `PrivateRoute`, (中文(简体) ), `ServeIngress`, (中文(简体) ), `ServeRoot`, (中文(简体) ), `TailscaleOptions`, (中文(简体) ), `TailscaleState`, (中文(简体) ), `TailscalePeer`, (中文(简体) ), `Bonjour`, (中文(简体) ), `BonjourRecord`; 在本地反应 `BrowseService`, (中文(简体) ), `BrowseHandle`, (中文(简体) ), `BrowseOptions`, (中文(简体) ), `BrowseEvents`, (中文(简体) ), `BrowseEventName`, (中文(简体) ), `ZeroconfLike` |

## 路线

`via` 选择路径 :

| `via` | 地址 | 服务器绑定 |
| --- | --- | --- |
| `auto` (默认) | 如果安装了Tailscale, 则使用局域网 | 现下表 |
| `tailscale` | `wss://<MagicDNS name>` 通过尾图服务 | `127.0.0.1` |
| `tailscale-direct` | `ws://<tailnet IP>:<port>` | 选中的尾网 IP |
| `private` | 另一个覆盖网络(NetBird, WireGuard, ZeroTier,.) | 每个覆盖地址 |
| `lan` | 物理外观界面上的私人IPv4地址(未知多克,VM或VPN桥接) | 每个局域网地址 |

`bind` 是一个列表:每个听众一个地址,所以手机到达了它提供的地址上的服务器. 局域网 或上线路由绑定每个机密地址的名称, 绝不使用通配符—— 通配符打开端口 这台电脑的每一个接口, 包括那部电话从未提供的接口.

直接 Tailscale 绑定第一个 CGNAT IPv4 地址 `Self.TailscaleIPs`关闭局域网. 过 `address` 与 `via: 'tailscale-direct'` 从同一列表中选择另一个 IPv4 地址; 在删除已有的Service映射之前,拒绝使用外国地址和通配符.

`routes(interfaces, tailnetIPs)` 包括名为 Tailscale 的界面 `tailscale`; 通过 即兴表演 `ips` 以识别 MacOS 上的分级地址 `utun` 接口。 其他叠加, 包括没有tailscale 证据的 CGNAT 地址,请在 `private`。该函数不运行 CLI.

只IPv6网络:尚未.

分级只能是运输 Link的握手仍然检查每个设备密钥.

### 建议

`recommend()` 每个混凝土路线列出一个条目(`tailscale`, (中文(简体) ), `tailscale-direct`, (中文(简体) ), `private`, (中文(简体) ), `lan`与 因此推荐令和每日副本在一个地方直播. `sentence` 财务报告和审定财务报表 `needs` 来自 [`@byokit/ui`](https://npmcn.com/package/%40byokit/ui/README.md#routes)是因为 `routeChoices()`;这里只添加可用性. `auto` 这不是一条路线:它选择 建议条目。 过 `state`, (中文(简体) ), `serve`, (中文(简体) ), `lan` 财务报告和审定财务报表 `private` 假的,在不触摸尾盘的情况下决定 网络;没有选项,它将探测此计算机(`tailscaleState`, (中文(简体) ), `inspectServe` 打开 `port`, (中文(简体) ), `routes`) (中文(简体) ).

```ts
import { recommend } from '@byokit/discover';

const entries = await recommend({
  state: { installed: true, backendState: 'Running', needsSignin: false, dnsName: 'dev.tailnet.ts.net', ips: ['100.64.0.1'] },
  serve: { status: 'free' },
  lan: ['192.168.1.20'],
  private: [],
});
console.log(entries.map((e) => `${e.recommended ? '●' : '○'} ${e.via}: ${e.sentence}`));
```

```text
● tailscale: Your phone reaches this computer from anywhere.
○ tailscale-direct: Same as above without Tailscale Serve. Pick this if Serve is already used on this computer for something else.
○ private: Pick this if this computer and phone are already on one.
○ lan: Easiest. Works while your phone is on the same Wi-Fi as this computer. Nothing else to install.
```

顺序是目前的健康路线(通过该路线为 `current`)),后为"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作"服务"作服务" root被取出,被禁用,被放出,或者无名——然后是私人的覆盖,然后是Same Wi-Fi. 轨距已安装但 签名退出后仍可选择, 登录在 `needs`,而建议使用相同的Wi-Fi。 没有准备就等于没有进入 建议,每个条目都带有 `disabledReason`。 。 。 .

## 多个直接听众

`directRoutes` 是同步的,没有运行 CLI,服务或收听器。 默认情况下它会选择回转加 机密的尾网地址 提供 `listen` 只允许其真正的范围。 局域网约束个人 局域网地址,因此启用局域网不会隐含地打开无关的上盖或被禁用的范围. `reach()` 遵循同一规则: `bind` 列表名称,每个收听者一个地址,以及局域网或覆盖 路径从不要求通配符. 过 `tailscaleState().ips` 作为 `tailnetIPs` 用于未命名接口。 地址必须在界面中存在 shapt; CLI 仅凭证据不能创建收听器. 刷新快照并调和听众 当网络或对接窗口政策发生变化时.

```ts
import type { NetworkInterfaceInfo } from 'node:os';
import { directRoutes } from '@byokit/discover';

const nic = (address: string): NetworkInterfaceInfo => ({ address, netmask: '255.255.255.0',
  family: 'IPv4', mac: '02:00:00:00:00:01', internal: false, cidr: `${address}/24` });
const umer = directRoutes({
  port: 8792, path: '/link',
  interfaces: { en0: [nic('192.168.1.20')], utun3: [nic('100.101.2.3')] },
  tailnetIPs: ['100.101.2.3'],
  listen: { loopback: true, tailnet: true, lan: true },
});
console.log(umer.hosts); // ['127.0.0.1', '100.101.2.3', '192.168.1.20']
console.log(umer.urls);  // ['ws://192.168.1.20:8792/link', 'ws://100.101.2.3:8792/link']
// Create a server for each host; give urls to link's offer only after those servers listen.
```

`hosts` 用明确的被绑定的收听器覆盖范围选择(通过数组而不是逗号分隔) 字符串). 明确钉住 `0.0.0.0` 故意打开每个 IPv4 接口;其拨号 URL 仅扩展 至分类局域网/尾网地址。 一个空的 `hosts` 数组返回没有听众或 URL 。 拨号 URL 设置局域网 在尾网之前,删除重复,并仅在没有远程候选时包含回放.

## 电话路由证据( 反应本地和节点)

使用 `@byokit/discover/react-native` 明确或根 `react-native` 出口条件。 都暴露出来了 `nativeAddresses`, (中文(简体) ), `routeOf`, (中文(简体) ), `observe`, (中文(简体) ), `probe` 财务报告和审定财务报表 `phoneNetwork` 与节点相同的类型。 这些函数导入 no 节点模块. Expo 应用程序使用捆绑 `ByokitReach` 默认情况下在Android/iOS上的本地模块. 重建当地人 安装 @ byokit/ 发现后二进制 。 Android 读取界面前缀和网络能力; iOS 读取 接口 netmask 和 NWPath 传输。 赤裸回放本地应用程序可以提供本地地址模块 结束 `nativeModule` 每个电话。 这支持没有全局单子的现有模块和假相. 阅读器 `addresses(): Promise<NativeAddress[]>`返回时 `{ address, prefixLength?, interface? }` 每个IPv4接口。 获取 `prefixLength` 从平台上 界面前缀/网格,从不猜测 24。 仅保留字符串的阅读器需要适配器,不能 提供前缀证据,直到其本土实施暴露.

此示例运行时没有本地网络( 使用节点根导入以在节点下运行 ) :

```ts
import { nativeAddresses, routeOf, observe, probe } from '@byokit/discover/react-native';
import type { NativeAddressesModule } from '@byokit/discover/react-native';

const umer: NativeAddressesModule = {
  addresses: async () => [{ address: '192.168.1.2', prefixLength: 23, interface: 'wlan0' }],
};
const fakeFetch: typeof fetch = async () => new Response('', { status: 503 });
console.log(await nativeAddresses({ nativeModule: umer }));
console.log(routeOf('ws://100.64.0.3:8792/link')); // 'tailscale'
console.log(await probe('ws://192.168.0.20:8792/link', { timeout: 4000, fetch: fakeFetch }));
const facts = await observe({
  urls: ['ws://192.168.0.20:8792/link', 'ws://100.64.0.3:8792/link'],
  nativeModule: umer,
  priorEvidence: { anywhere: 'anywhere', peer: true, reached: { tailscale: 123 } },
  probe: { timeout: 4000, fetch: fakeFetch },
});
console.log(facts.home, facts.target, facts.knock?.state); // true, LAN URL, 'answers'
```

对于VPN/错误网络旗帜, `phoneNetwork()` 返回新的活动网络快照 :

```ts
import { phoneNetwork } from '@byokit/discover/react-native';

// Expo: const network = await phoneNetwork();
// Runnable fake for Umer's phone (Node can use the root import):
const network = await phoneNetwork({ nativeModule: {
  phoneNetwork: async () => ({ onWifi: true, cellular: false, vpnActive: 'yes' }),
} });
console.log(network); // { onWifi: true, cellular: false, vpnActive: 'yes' }
```

Android 使用活动网络的 `TRANSPORT_WIFI`, (中文(简体) ), `TRANSPORT_CELLULAR` 财务报告和审定财务报表 `TRANSPORT_VPN` 能力; 多个运输旗可以是真实的. iOS 将 NWPath 用于 Wi-Fi/ cellular 并总是返回 `vpnActive: 'unknown'`。 。 。 . 没有本地读取器, 读取失败或没有路径输出 `{ onWifi: false, cellular: false, vpnActive: 'unknown' }`· ; 背面的假运输旗并不能证明网络不存在。 节点返回后退, 除非 读者被注射。 此快照从未确定可以达到特定的对等点 。 包裹 安卓正常的原生货品 `ACCESS_NETWORK_STATE` 权限; 没有运行时间权限对话框 这是需要的。 曝光自动链接发现捆绑模块; 安装 `expo-modules-core` 如果在本地应用程序中 尚未存在。 仅限JS的环境可以注入读取器;所有本土加载被推迟使用.

`observe` 比较主机的家用IPv4地址与电话的实际接口前缀. 它更喜欢一个 匹配家用 URL, 否则当快照包含一个 CGNAT 地址时, 尾网 URL . `home` 不详 (单位:千美元)`undefined`(a) 没有可用的前缀证据; `vpn` 未知,没有地址. `tailnet` 表示一个 候选 URL 存在; `vpn` 表示当地CGNAT地址存在. 导航卫星系统 `.ts.net` 分类是路由 提示,不是 Tailscale 、 共享对等方、 Wi- Fi 关联或可达性的证据 . `routeOf` 识别中继器 網址由 `/link/v1/` 返回时 `unknown` 用于不正确/不支持或其他公共URL。 IPv6 证据是 尚未支持 . `priorEvidence` 保留先前已认证的主机, 它不会被刷新 根据本意见. `addresses` 可直接提供快照;节点默认为自己的IPv4接口. 不可用/ 失效的本地模块给出空快照, 并且从未发明网络证据 .

`probe` 将 WebSocket URL 映射到 HTTP( S)并保存其路径。 每个 HTTP 状态都意味着 `answers`没有 被认证的链接。 明确 `ECONNREFUSED` 表示 `refused`; 通用本地获取错误(包括 dNS/TLS 可能的失败)指 `unknown`。 。 。 . `timeout` 即使获取也忽略了中止. 默认约束为4000ms. 探测器只是对呼叫者所通过 URL (或候选人) 的主动请求 `observe` 选择);它从不运行 Tailscale CLI,在中或更改服务。 弹出 `fetch` 用于离线测试. Apps决定自己的下线措辞,仍然通过链接认证.

## 可达到性和防火墙

两次检查在设置其勾选符前进行,顺序如下:

1.  **电话能接通这些地址吗?** 这是 `@byokit/pair`是因为 [`check(urls)`](https://npmcn.com/package/%40byokit/pair/README.md#checking-reachability-before-pairing):它以 `LINK_PROBE` 不花一次性的票, 所以检查的报价 仍然对以后。 设置 自动检查自己公布的URL;电话在征得同意前重新检查扫描报价。 这个包 不再出口——从 `@byokit/pair`回答探测器的宿主.
2.  **这是电脑的防火墙封锁了港口吗?** 这是这个包裹的 `firewall({ port })` (只有节点). 它只读普通用户可能读到的.. `ufw status`,则 `nft list ruleset`,则 `iptables -S` linux; macOS上的应用程序防火墙状态; `netsh advfirewall` ——和 从未要求管理员访问 。 任何无法读取的东西 `'unknown'`这是一个诚实的答案: 把它当作"检查另一种方式", 从来没有像封锁或清晰.

```ts
import { firewall } from '@byokit/discover';

const verdict = await firewall({ port: 8792 });
console.log(verdict.blocked, verdict.how);  // true | false | 'unknown', plus the firewall that said so
if (verdict.blocked !== false) console.log(verdict.nextStep);  // plain words, never a sudo command
```

`blocked: true` 列出拒绝规则; `false` 表示防火墙已关闭或让端口通过. `'unknown'` 覆盖一个活动防火墙,没有规则命名端口(包括macOS,它决定每 app 而不是每个端口),需要管理员访问读取的状态,并设置此帮助工具 不检查。 过 `platform` 财务报告和审定财务报表 `run` 仅在测试中,用于重放被扭曲的命令输出 .

## 分级规则

这些是Muxr第0004号决定的规则.

-   **永远不要漏斗.** 设置用途 `tailscale serve --yes --bg --https=443 http://127.0.0.1:<port>`,哪个 只在尾网内可见。 拒绝已启用的漏斗根, 包括其代理匹配时 ; 在更改路线或 DNS 名称之前关闭漏斗.
-   **服务器保持回转** 服务( A)`bind: ['127.0.0.1']`) (中文(简体) ).
-   **这台机器是自己的 `Self.DNSName` 已使用.** 缺少或无效的MagicDNS名称是一个错误. 登录退出或 已损坏的尾图也是个错误 。 无人不问就回到局域网.
-   **未记录的根处理器被拒绝 .** 若为 `/` 打开 `<name>:443` 已经有任何处理器, `reach` 抛出,除非 `previous` 记录匹配的应用创建的映射。 选择 `tailscale-direct` 或删除不相关的绘图.
-   **所有权指纹.** 坚持返回 `ingress` (单位:千美元)`{ kind, port, dnsName, proxy }`并传为 `previous`。 。 。 . `unserve` 财务报告和审定财务报表 `reach({ previous })` 只删除 `/` 同时它仍然指向这个代理; 兄弟姐妹路径仍然存在. 在 DNS 重命名时, 尝试在启用新名称之前删除已记录的旧根; 清理失败 返回其指纹 `pendingCleanup`,除了旧根上的漏斗停止了过渡。 设置后 或去除后,再次检查根;在设置后报告被占用的根,没有作进一步修改,同时 路由切换器保存失败清理 `pendingCleanup`。 。 。 . 分级CLI没有比较和设置:检查和写之间的变化仍然可以被覆盖. 校验后写会检测到一个相冲突的最终状态,但不能消除这一种族.
-   **直倒倒倒倒倒倒倒倒.** 如果服务在尾网被禁用( 错误包括管理员链接) , 时间 退出,或被带走,使用 `via: 'tailscale-direct'`与 `previous`,也可以删除此软件包的映射. 如果无法校验清理, 请求的路由仍然以 `pendingCleanup: previous`;坚持 指纹和重试 `unserve(pendingCleanup)` 等会. `auto` 也保留 `previous` 当纹章消失时 它会回到局域网。 在删除工作服务映射前对地址进行明确的路由切换检查. 成功的清理省略 `pendingCleanup`。 。 。 . 如果一个 Service 写入成功但状态无法校验, 抛出的错误也带有 `error.pendingCleanup`;在报告错误之前坚持它,然后检查或稍后重试清理.

CLI是: 校对: `tailscale` 打开 `PATH`然后是macOS应用程序。 过 `tailscale: { bin, timeoutMs }` 来选择另一个。 这个 下级步骤也导出 : `tailscaleStatus`, (中文(简体) ), `tailscaleName`, (中文(简体) ), `inspectServe`, (中文(简体) ), `serve`, (中文(简体) ), `unserve`,以及 `routes` 用于界面列表.

对于一个接线员, `await tailscaleState()` 返回时 `{ installed, backendState, needsSignin, reason, dnsName, ips, keyExpiry?, Peer? }` 没有扔. `backendState`, (中文(简体) ), `reason` 和已实现的 `dnsName` 当未知时可以缺席. 缺失的CLI,超时,变形的JSON和守护进程故障都是诊断结果;他们从不设置Service或签名. `needsSignin` 只对 `NeedsLogin`· ; `NeedsMachineAuth` 相反,要求行政部门批准 `Stopped` 这并不意味着 登录。 现有 `tailscaleStatus()` 仍然有CLI失败.

`keyExpiry` 保存一个有效的 `Self.KeyExpiry` 日期字符串; 主机选择如何标签已过期的密钥 . `Peer` 是经过验证的 `{ TailscaleIPs, DNSName?, Online? }`时缺席 当 CLI 报告空地图时为空 。 额外的字段和错误的条目被丢弃 .

纯洁的帮手 **原始状态 JSON**· ; `isPeer` 也接受快照键入 `Peer` 地图. `isPeer(status, ip)` 检查 `Peer[*].TailscaleIPs`,接受一个 IPv4-映射的套接字地址(`::ffff:100.64.0.2`),并排除 `Self`。 。 。 . 离线同伴仍然算作同伴. 同伴会籍是一个发现提示:链接的握手必须认证设备.

## mDNS 数据

### 广告(节点)

`advertise({ type, port, name?, txt?, addresses? })` 发表 `_<type>._tcp` 与 [bonjour服务](https://www.npmjs.com/package/bonjour-service) 返回时 `{ stop }`拨号 URL `txt`。 。 。 . 该工具包过滤 bonjour-service的 A/AAA记录到 `addresses`,默认为 `routes().lan`,所以多克,VPN和 尾网地址默认除外. 明确的列表可以选择包括 IPv6 在内的接口地址; 只有 地址 bonjour- service 生成可以发布. 一个空列表不发布 A/AAAA 记录. 所选列表 当广告开始时被快照,同样的过滤器适用于停站时的告别记录. 发现元数据 (PTR,SRV和TXT)被保留. 找到错误的计算机的设备会失败链接的握手,因为主机密钥被固定.

```ts
import { advertise } from '@byokit/discover';

const ad = await advertise({ type: 'muxr', port: 8792, txt: { url: 'ws://192.168.1.20:8792' } });
// later
await ad.stop();
```

### 浏览( 本地反应)

在本地反应( 软件包) 下 `react-native` 出口条件, `@byokit/discover` 也浏览局域网这样一个应用程序 可以在不输入地址的情况下找到服务 :

```ts
import { browse, scan } from '@byokit/discover';

// Stream discovery: start, then stop when done.
const found = browse({ type: 'muxr' });                  // protocol 'tcp', domain 'local.' by default
found.on('found', (s) => console.log(s.name, s.addresses, s.port, s.txt));   // first resolve of a name
found.on('updated', (s) => console.log('refreshed', s.name));                // a known name resolved again
found.on('lost', (name) => console.log(name, 'left'));
found.on('error', (error) => console.log(error.message));
found.on('stopped', ({ reason }) => console.log(reason)); // 'preempted' if another browse starts
found.stop();                                            // idempotent

// Time-boxed: collect for N ms, then stop. Later resolves replace the earlier entry, first-seen order.
const hosts = await scan({ type: 'ssh', ms: 8000 });     // rejects on error or preemption
```

A 类 `BrowseService` 携带 `name`, (中文(简体) ), `host`, (中文(简体) ), `addresses`, (中文(简体) ), `port` (单位:千美元)`0` 当时,天台没有赏赐任何人 `txt` (弦声) (仅限数值)。 被钉住 `react-native-zeroconf` 抚养(`0.17.4`由@byokit/discover提供,而不是应用.

-   **一一一一一一浏览.** 一个本地浏览器一次运行一个浏览. 开始另一个 `browse` 或者说 `scan` 预置当前控件, 包括当两者请求相同类型时: 它收到一个 `stopped` 事件为 `{ reason: 'preempted' }`,失去已知的服务,而不恢复。 一个先发制人 `scan` 拒绝; 呼叫者 需要不断发现时,必须在完成其他扫描后开始新的浏览.
-   **活动匹配.** 已解决的事件必须在 `fullName`; 清除仅适用于 浏览找到的名字 。 原生错误在先发后被忽略一秒钟. 本地模块 提供不扫描 ID: 用于新浏览重用名称的未打入后删除, 或第二秒后出现错误, 仍然可以归结为新扫描;静态第二时发生的一个真实错误也被忽略.
-   **入会.** 默认节点条目不导入本地模块; 使用 `react-native` 出口条件 眉来眉去. 博览会的应用程序在添加了@byokit/discover后必须重建其本土二进制.
-   **机器人模拟器.** mDNS多播对仿真器不起作用;在真设备上测试发现.

## 测试

`test/journey-reach.test.ts` 驱动构建的软件包, 如主机应用程序 : `reach`, (中文(简体) ), `serve`编号 :`unserve`, (中文(简体) ), `recommend`, (中文(简体) ), `advertise`, (中文(简体) ), `routes`编号 :`directRoutes` 财务报告和审定财务报表 `tailscaleState` 对付假人 `tailscale` 记录每个电话的CLI 真实的 二进制从不运行,也没有包出: mDNS 广告使用假的发布器, React Industrial 浏览 API (单位:千美元)`test/browse.test.ts`)使用假零conf模块.

## 链接

-   [字节](https://npmcn.com/package/README.md): 其他软件包和示例
-   [`@byokit/pair`](https://npmcn.com/package/%40byokit/pair):对齐和加密链接这些地址用于
-   [`examples/herdr-kit`](https://npmcn.com/package/examples/herdr-kit): 选取 `via` 并坚持服务 `ingress`
-   [变动情况](https://npmcn.com/package/%40byokit/discover/CHANGELOG.md)

## 许可证

阿帕奇2.0 (英语). 见 [自由主义](https://npmcn.com/package/%40byokit/discover/LICENSE) 财务报告和审定财务报表 [通知](https://npmcn.com/package/NOTICE)。 。 。 .

## 常见问题

### @byokit/discover 是什么？

TypeScript套件让人们通过他们已经支付的人工智能计划,在网络,电话和桌面上登录到你的应用程序\.

### 如何安装 @byokit/discover？

在项目目录运行 npm install @byokit/discover，或使用 pnpm add / yarn add / bun add。

### @byokit/discover 支持 TypeScript 吗？

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

### @byokit/discover 支持 ESM 还是 CommonJS？

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

### @byokit/discover 需要什么版本的 Node\.js？

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

### @byokit/discover 可以商用吗？

@byokit/discover 使用 Apache-2\.0 许可。宽松许可：允许商业使用、修改和再分发，并附带明确的专利授权；分发时需保留许可与 NOTICE 文件，修改过的文件要注明改动。以上为概括，不构成法律意见，请以许可原文为准。

### @byokit/discover 的下载量有多少？

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

### @byokit/discover 还在维护吗？

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

