pi-remote-mesh 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +186 -2
- package/dist/index.d.ts +149 -0
- package/dist/index.js +1183 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -4
- package/src/commands.ts +116 -0
- package/src/config.ts +196 -0
- package/src/index.ts +31 -0
- package/src/ssh-engine.ts +553 -0
- package/src/status-bar.ts +82 -0
- package/src/tool-registry.ts +352 -0
- package/src/types.ts +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 kongzhu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,187 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 🌐 pi-remote-mesh
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Multi-device remote SSH execution mesh & on-demand tool exposure for the [Pi Coding Agent](https://github.com/earendil-works/pi-coding-agent).
|
|
4
|
+
> 专为 Pi 设计的多端 SSH 协同网格与按需工具调度中心:**一行配置管全家,按需唤醒省 Token,零泄露安全架构**。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 🌟 核心特性 (Features)
|
|
9
|
+
|
|
10
|
+
- 🎯 **On-Demand Tool Exposure (按需工具暴露)**
|
|
11
|
+
多设备工具可以按需暴露,减少当前请求中的工具声明;具体 Token 和缓存影响取决于模型及运行环境。
|
|
12
|
+
`pi-remote-mesh` 默认只暴露一个极轻量的 `remote_enable` 统一调度入口;当模型或用户需要操作特定设备时(如 VPS 或开发板),**动态将该设备工具注入当前会话,用完随时关闭或在重启时自动重置**。
|
|
13
|
+
|
|
14
|
+
- 🌐 **Multi-Device Mesh Topology (多端网格拓扑)**
|
|
15
|
+
无论你有几台设备(云端 VPS、常驻 Mac mini、Windows 主力机、安卓 Termux、树莓派/Arduino 开发板),只需在本地配置中声明,即可获得对应的执行与传输能力。
|
|
16
|
+
|
|
17
|
+
- 🔒 **Zero-Secret Design (零凭证硬编码)**
|
|
18
|
+
配置默认存放在本机 `~/.pi/remote-mesh.json`,建议设置 `chmod 600`;不要将凭据、私钥或敏感配置提交仓库。设备名称和地址可能出现在工具描述或输出中,应按模型提供商的隐私策略处理。
|
|
19
|
+
|
|
20
|
+
- ⚡ **Persistent SSH & SFTP Engine (长连接与文件传输)**
|
|
21
|
+
支持 SSH 连接复用与 SFTP 文件读写。写入先使用独占创建的临时文件,再请求服务器重命名;覆盖行为和原子性取决于服务器,无法保证所有服务器都支持覆盖。
|
|
22
|
+
|
|
23
|
+
- 🔄 **Local Loopback Bypass (智能防套娃检测)**
|
|
24
|
+
可配置 `localBypass`,当 Agent 在 Mac 本地运行时,自动识别并拒绝对 Mac 的 SSH 连接;请使用本地工具执行,防止自环回死锁。
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 📦 安装 (Installation)
|
|
29
|
+
|
|
30
|
+
实验性项目,部署前请审查安全策略与依赖。可从 GitHub 安装:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pi install git:github.com/kongzhuww/pi-remote-mesh
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
npm 包尚未发布;不要假定 npm 安装可用。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 🔒 SSH 主机身份验证
|
|
41
|
+
|
|
42
|
+
Every device requires `hostKeySha256` (OpenSSH `SHA256:...` fingerprint) or `hostFingerprint` (SHA-256 hex). Connections fail closed if it is absent or mismatched. Verify the fingerprint on the server or through an independent trusted channel before adding it to the local configuration; an unverified `ssh-keyscan` alone does not establish trust. For example, on the server, run `ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub` and compare it independently. Host key rotation requires independently verifying and updating the pin. Existing devices without a pin cannot connect until configured.
|
|
43
|
+
|
|
44
|
+
## 🚀 快速上手 (Quickstart)
|
|
45
|
+
|
|
46
|
+
### 1. 初始化配置模板
|
|
47
|
+
|
|
48
|
+
在终端或 Pi 会话中运行:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
/remote-mesh init
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
该命令将在 `~/.pi/remote-mesh.json` 生成一份标准的配置模板。
|
|
55
|
+
|
|
56
|
+
### 2. 填写设备信息
|
|
57
|
+
|
|
58
|
+
编辑 `~/.pi/remote-mesh.json`(支持 `~` 自动展开为用户家目录):
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"version": "1.0",
|
|
63
|
+
"toolNamingStyle": "compact",
|
|
64
|
+
"onDemandExposure": true,
|
|
65
|
+
"devices": {
|
|
66
|
+
"vps": {
|
|
67
|
+
"name": "Tokyo Cloud VPS",
|
|
68
|
+
"host": "1.2.3.4",
|
|
69
|
+
"port": 22,
|
|
70
|
+
"username": "ubuntu",
|
|
71
|
+
"privateKeyPath": "~/.ssh/id_rsa",
|
|
72
|
+
"hostKeySha256": "SHA256:REPLACE_WITH_INDEPENDENTLY_VERIFIED_FINGERPRINT",
|
|
73
|
+
"features": ["exec", "sftp", "status"],
|
|
74
|
+
"persistent": true,
|
|
75
|
+
"defaultCwd": "/home/ubuntu"
|
|
76
|
+
},
|
|
77
|
+
"phone": {
|
|
78
|
+
"name": "Android Termux",
|
|
79
|
+
"host": "192.168.1.50",
|
|
80
|
+
"port": 8022,
|
|
81
|
+
"username": "u0_a100",
|
|
82
|
+
"privateKeyPath": "~/.ssh/termux_key",
|
|
83
|
+
"hostKeySha256": "SHA256:REPLACE_WITH_INDEPENDENTLY_VERIFIED_FINGERPRINT",
|
|
84
|
+
"features": ["exec", "sftp"],
|
|
85
|
+
"persistent": true
|
|
86
|
+
},
|
|
87
|
+
"mac": {
|
|
88
|
+
"name": "Mac mini Host",
|
|
89
|
+
"host": "mac-mini.local",
|
|
90
|
+
"port": 22,
|
|
91
|
+
"username": "developer",
|
|
92
|
+
"privateKeyPath": "~/.ssh/id_ed25519",
|
|
93
|
+
"hostKeySha256": "SHA256:REPLACE_WITH_INDEPENDENTLY_VERIFIED_FINGERPRINT",
|
|
94
|
+
"features": ["exec", "status"],
|
|
95
|
+
"persistent": false,
|
|
96
|
+
"localBypass": true,
|
|
97
|
+
"localHostnames": ["mac-mini", "mac-mini.local"]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 3. 测试连通性
|
|
104
|
+
|
|
105
|
+
配置修改后先 `/reload` 或重启 Pi,再运行测试:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
/remote-mesh test vps
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
首次启用设备时确认一次;在同一会话中后续执行与文件写入不再逐次确认。断开、重载或切换会话后需重新授权。无交互 UI 时默认拒绝授权。这个授权允许任意远程命令与写入,插件并非沙箱,也无法保证 AI 不会误操作。取消操作只请求中断,不能保证远端子进程或后台任务停止。
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 🛠️ 工具与命令速查
|
|
116
|
+
|
|
117
|
+
### 🤖 注册的 Agent Tools
|
|
118
|
+
|
|
119
|
+
| 工具名称 | 暴露时机 | 功能描述 |
|
|
120
|
+
| :--- | :--- | :--- |
|
|
121
|
+
| **`remote_enable`** | **始终激活 (Always Active)** | 调度中心。接受 `{ device: "vps", action: "enable" \| "disable" \| "disconnect" \| "status" }`。按需将目标设备工具暴露给下一轮对话,或断开连接。 |
|
|
122
|
+
| **`{device}_exec`** | **按需激活 (On-Demand)** | 在远程设备上执行 Bash / Shell 命令,返回 stdout、stderr 与 exitCode。 |
|
|
123
|
+
| **`{device}_read`** | **按需激活 (On-Demand)** | 通过 SFTP 读取远程文件内容(支持 UTF-8 文本与文件大小防爆保护)。 |
|
|
124
|
+
| **`{device}_write`** | **按需激活 (On-Demand)** | 通过 SFTP 临时文件写入后请求重命名;覆盖及原子性由服务器决定。 |
|
|
125
|
+
| **`{device}_status`**| **按需激活 (On-Demand)** | 返回本地记录的连接状态,不主动探测网络连通性。 |
|
|
126
|
+
|
|
127
|
+
*(注:若配置 `"toolNamingStyle": "prefixed"`,工具名将变为 `remote_{device}_exec` 等。)*
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### 💻 终端控制台命令
|
|
132
|
+
|
|
133
|
+
在 Pi 命令行输入 `/remote-mesh`(或兼容别名 `/remote-tools`):
|
|
134
|
+
|
|
135
|
+
- `/remote-mesh status`:查看已配置的设备列表、连接状态及当前激活工具。
|
|
136
|
+
- `/remote-mesh enable <device|all>`:手动激活目标设备工具至会话。
|
|
137
|
+
- `/remote-mesh disable <device|all>`:手动隐藏目标设备工具以节省 Token。
|
|
138
|
+
- `/remote-mesh disconnect <device|all>`:关闭指定设备的持久化 SSH 连接并隐藏工具。
|
|
139
|
+
- `/remote-mesh test <device>`:发起一次快速网络与 SSH 连通性测试。
|
|
140
|
+
- `/remote-mesh init`:在 `~/.pi/remote-mesh.json` 创建初始模板。
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## ⚙️ 完整配置项说明 (Schema)
|
|
145
|
+
|
|
146
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
147
|
+
| :--- | :--- | :--- | :--- |
|
|
148
|
+
| `name` | string | `id` | 设备展示名称 |
|
|
149
|
+
| `host` | string | **必填** | 目标主机 IP、域名或 Tailscale 域名 |
|
|
150
|
+
| `port` | number | `22` | SSH 服务端口 |
|
|
151
|
+
| `username` | string | 当前系统用户 | 远程登录用户名 |
|
|
152
|
+
| `privateKeyPath`| string | 可选 | 私钥绝对路径或 `~/.ssh/...` 相对路径 |
|
|
153
|
+
| `hostKeySha256` / `hostFingerprint` | string | 二选一必填 | 分别接受 OpenSSH SHA256 base64 或 SHA-256 十六进制指纹,未匹配时拒绝连接 |
|
|
154
|
+
| `password` | string | 可选 | 密码认证(建议优先使用私钥) |
|
|
155
|
+
| `passphrase` | string | 可选 | 私钥解密密码 |
|
|
156
|
+
| `persistent` | boolean| `true` | 是否维持长连接复用(单次脚本建议设为 `false`) |
|
|
157
|
+
| `features` | array | `["exec", "sftp", "status"]` | 启用的功能模块列表 |
|
|
158
|
+
| `defaultCwd` | string | `"~"` | 远程命令默认目录;非默认目录使用 POSIX Shell 引用,非 POSIX 远端应保持默认并使用其原生命令语法 |
|
|
159
|
+
| `connectTimeoutMs` | number | `10000` | 连接超时时间(毫秒) |
|
|
160
|
+
| `maxOutputBytes` | number | `32768` | 终端最大截断字节数,防止长输出爆内存 |
|
|
161
|
+
| `maxOutputLines` | number | `300` | 终端最大输出行数 |
|
|
162
|
+
| `localBypass` | boolean| `false` | 若当前 Agent 就在该设备上运行,是否自动忽略 SSH |
|
|
163
|
+
| `localHostnames`| array | `[]` | 用于辅助识别本机的别名列表 |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 🚢 发布到 npm (Publishing Guide)
|
|
168
|
+
|
|
169
|
+
如果你是维护者,将本项目发布到 npm 仅需以下步骤:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# 1. 登录 npm 账号
|
|
173
|
+
npm login
|
|
174
|
+
|
|
175
|
+
# 2. 检查构建与单元测试
|
|
176
|
+
npm test
|
|
177
|
+
npm run build
|
|
178
|
+
|
|
179
|
+
# 3. 发布公开包
|
|
180
|
+
npm publish --access public
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 📄 License
|
|
186
|
+
|
|
187
|
+
[MIT License](LICENSE) © 2025 kongzhu
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import { Client, SFTPWrapper } from 'ssh2';
|
|
3
|
+
|
|
4
|
+
type DeviceFeature = "exec" | "sftp" | "status";
|
|
5
|
+
type AuthType = "key" | "agent" | "password";
|
|
6
|
+
interface SshAuthConfig {
|
|
7
|
+
type?: AuthType;
|
|
8
|
+
privateKeyPath?: string;
|
|
9
|
+
passphrase?: string;
|
|
10
|
+
password?: string;
|
|
11
|
+
agent?: string;
|
|
12
|
+
}
|
|
13
|
+
interface DeviceConfig {
|
|
14
|
+
id: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
host: string;
|
|
17
|
+
port?: number;
|
|
18
|
+
username: string;
|
|
19
|
+
auth?: SshAuthConfig;
|
|
20
|
+
/** SHA-256 host-key fingerprint in hexadecimal form, as accepted by ssh2. */
|
|
21
|
+
hostFingerprint?: string;
|
|
22
|
+
privateKeyPath?: string;
|
|
23
|
+
passphrase?: string;
|
|
24
|
+
password?: string;
|
|
25
|
+
persistent?: boolean;
|
|
26
|
+
features?: DeviceFeature[];
|
|
27
|
+
defaultCwd?: string;
|
|
28
|
+
connectTimeoutMs?: number;
|
|
29
|
+
/** Pinned OpenSSH SHA256 host key fingerprint, e.g. SHA256:... */
|
|
30
|
+
hostKeySha256?: string;
|
|
31
|
+
keepaliveIntervalMs?: number;
|
|
32
|
+
keepaliveCountMax?: number;
|
|
33
|
+
maxOutputBytes?: number;
|
|
34
|
+
maxOutputLines?: number;
|
|
35
|
+
env?: Record<string, string>;
|
|
36
|
+
localBypass?: boolean;
|
|
37
|
+
localHostnames?: string[];
|
|
38
|
+
toolPrefix?: string;
|
|
39
|
+
}
|
|
40
|
+
interface RemoteMeshConfig {
|
|
41
|
+
version?: string;
|
|
42
|
+
toolNamingStyle?: "compact" | "prefixed";
|
|
43
|
+
onDemandExposure?: boolean;
|
|
44
|
+
devices: Record<string, DeviceConfig>;
|
|
45
|
+
}
|
|
46
|
+
interface DeviceStatus {
|
|
47
|
+
id: string;
|
|
48
|
+
name: string;
|
|
49
|
+
host: string;
|
|
50
|
+
port: number;
|
|
51
|
+
username: string;
|
|
52
|
+
isLocal: boolean;
|
|
53
|
+
isConnected: boolean;
|
|
54
|
+
persistent: boolean;
|
|
55
|
+
features: DeviceFeature[];
|
|
56
|
+
activeTools: string[];
|
|
57
|
+
availableTools: string[];
|
|
58
|
+
lastError?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
declare const DEFAULT_CONFIG_FILENAME = "remote-mesh.json";
|
|
62
|
+
declare const LEGACY_CONFIG_FILENAME = "remote-devices.json";
|
|
63
|
+
declare function resolveUserPath(filepath: string): string;
|
|
64
|
+
declare function getConfigPaths(): string[];
|
|
65
|
+
declare function findConfigFile(): string | null;
|
|
66
|
+
declare function normalizeDeviceConfig(id: string, raw: Partial<DeviceConfig>): DeviceConfig;
|
|
67
|
+
declare function loadMeshConfig(): {
|
|
68
|
+
config: RemoteMeshConfig;
|
|
69
|
+
loadedFrom: string | null;
|
|
70
|
+
};
|
|
71
|
+
declare function isLocalHost(device: DeviceConfig): boolean;
|
|
72
|
+
declare function generateTemplateConfig(): RemoteMeshConfig;
|
|
73
|
+
|
|
74
|
+
interface ExecResult {
|
|
75
|
+
stdout: string;
|
|
76
|
+
stderr: string;
|
|
77
|
+
exitCode: number;
|
|
78
|
+
durationMs: number;
|
|
79
|
+
isTruncated?: boolean;
|
|
80
|
+
exitSignal?: string;
|
|
81
|
+
}
|
|
82
|
+
declare function quotePosixShell(value: string): string;
|
|
83
|
+
declare class SshEngine {
|
|
84
|
+
private devices;
|
|
85
|
+
private clients;
|
|
86
|
+
private sftpWrappers;
|
|
87
|
+
private connecting;
|
|
88
|
+
private pendingClients;
|
|
89
|
+
private ephemeralSftpClients;
|
|
90
|
+
private generations;
|
|
91
|
+
constructor(devices: Record<string, DeviceConfig>);
|
|
92
|
+
updateDevices(devices: Record<string, DeviceConfig>): void;
|
|
93
|
+
getDevice(id: string): DeviceConfig;
|
|
94
|
+
isConnected(id: string): boolean;
|
|
95
|
+
private buildConnectConfig;
|
|
96
|
+
getClient(id: string): Promise<Client>;
|
|
97
|
+
exec(id: string, command: string, customCwd?: string, signal?: AbortSignal): Promise<ExecResult>;
|
|
98
|
+
getSftp(id: string): Promise<SFTPWrapper>;
|
|
99
|
+
private releaseSftp;
|
|
100
|
+
readFile(id: string, remotePath: string, maxBytes?: number, signal?: AbortSignal): Promise<{
|
|
101
|
+
content: string;
|
|
102
|
+
bytes: number;
|
|
103
|
+
}>;
|
|
104
|
+
ensureRemoteDir(sftp: SFTPWrapper, remoteDir: string, signal?: AbortSignal): Promise<void>;
|
|
105
|
+
writeFile(id: string, remotePath: string, content: string, signal?: AbortSignal): Promise<{
|
|
106
|
+
bytes: number;
|
|
107
|
+
}>;
|
|
108
|
+
disconnect(id: string): Promise<boolean>;
|
|
109
|
+
disconnectAll(): Promise<Record<string, boolean>>;
|
|
110
|
+
getStatus(id: string, activeTools?: string[], availableTools?: string[]): DeviceStatus;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
declare function getToolNamesForDevice(device: DeviceConfig, style?: "compact" | "prefixed"): {
|
|
114
|
+
exec?: string;
|
|
115
|
+
read?: string;
|
|
116
|
+
write?: string;
|
|
117
|
+
status?: string;
|
|
118
|
+
all: string[];
|
|
119
|
+
};
|
|
120
|
+
declare function registerMeshTools(pi: ExtensionAPI, config: RemoteMeshConfig, engine: SshEngine): {
|
|
121
|
+
handleControl: (targetDevice: string, action: "enable" | "disable" | "disconnect" | "status", ctx?: ExtensionContext) => Promise<{
|
|
122
|
+
action: "status";
|
|
123
|
+
summary: Record<string, any>;
|
|
124
|
+
isError: boolean;
|
|
125
|
+
target?: undefined;
|
|
126
|
+
activeToolsCount?: undefined;
|
|
127
|
+
affectedTools?: undefined;
|
|
128
|
+
disconnectResults?: undefined;
|
|
129
|
+
} | {
|
|
130
|
+
target: string;
|
|
131
|
+
action: "enable" | "disable" | "disconnect";
|
|
132
|
+
activeToolsCount: number;
|
|
133
|
+
affectedTools: string[];
|
|
134
|
+
disconnectResults: Record<string, boolean> | undefined;
|
|
135
|
+
isError: boolean;
|
|
136
|
+
summary?: undefined;
|
|
137
|
+
}>;
|
|
138
|
+
allMeshToolNames: Set<string>;
|
|
139
|
+
deviceToolMap: Map<string, string[]>;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
declare function registerMeshCommands(pi: ExtensionAPI, config: RemoteMeshConfig, loadedFrom: string | null, engine: SshEngine, handleControl: (device: string, action: "enable" | "disable" | "disconnect" | "status", ctx?: ExtensionContext) => Promise<any>): void;
|
|
143
|
+
|
|
144
|
+
declare function updateMeshStatusBar(ctx: ExtensionContext, config: RemoteMeshConfig, engine: SshEngine): void;
|
|
145
|
+
declare function clearMeshStatusBar(ctx: ExtensionContext, config: RemoteMeshConfig): void;
|
|
146
|
+
|
|
147
|
+
declare function export_default(pi: ExtensionAPI): void;
|
|
148
|
+
|
|
149
|
+
export { type AuthType, DEFAULT_CONFIG_FILENAME, type DeviceConfig, type DeviceFeature, type DeviceStatus, type ExecResult, LEGACY_CONFIG_FILENAME, type RemoteMeshConfig, type SshAuthConfig, SshEngine, clearMeshStatusBar, export_default as default, findConfigFile, generateTemplateConfig, getConfigPaths, getToolNamesForDevice, isLocalHost, loadMeshConfig, normalizeDeviceConfig, quotePosixShell, registerMeshCommands, registerMeshTools, resolveUserPath, updateMeshStatusBar };
|