agent-ssh-cli 0.1.2 → 0.2.0

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/README.md CHANGED
@@ -11,8 +11,8 @@
11
11
  <a href="https://github.com/sleepinginsummer/agent-ssh-cli/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="License MIT"></a>
12
12
  <a href="https://nodejs.org/"><img src="https://img.shields.io/badge/Node.js-%3E%3D18-339933?logo=node.js&logoColor=white" alt="Node.js >=18"></a>
13
13
  <a href="https://www.npmjs.com/"><img src="https://img.shields.io/badge/npm-%3E%3D8-CB3837?logo=npm&logoColor=white" alt="npm >=8"></a>
14
- <a href="https://github.com/sleepinginsummer/agent-ssh-cli"><img src="https://img.shields.io/badge/Windows-MacOS-0078D6?labelColor=0078D6&color=C0C0C0" alt="Windows/MacOS"></a>
15
- <a href="https://github.com/sleepinginsummer/agent-ssh-cli/releases"><img src="https://img.shields.io/badge/release-v0.1.2-blue" alt="release v0.1.2"></a>
14
+ <a href="https://github.com/sleepinginsummer/agent-ssh-cli"><img src="https://img.shields.io/badge/Windows-macOS-Linux-0078D6?labelColor=0078D6&color=C0C0C0" alt="Windows/macOS/Linux"></a>
15
+ <a href="https://github.com/sleepinginsummer/agent-ssh-cli/releases"><img src="https://img.shields.io/badge/release-v0.2.0-blue" alt="release v0.2.0"></a>
16
16
  <a href="https://github.com/sleepinginsummer/agent-ssh-cli/pulls"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen" alt="PRs welcome"></a>
17
17
  </p>
18
18
 
@@ -52,7 +52,9 @@
52
52
  - 本机网络可访问目标 SSH 服务器
53
53
  - 目标服务器已开启 SSH 服务
54
54
  - 如使用私钥认证,私钥文件需对当前用户可读
55
- - `agentsshcli exec/upload/download` 的连接缓存支持 macOS/Linux/Windows
55
+ - 当前版本内部执行器已迁移到 Rust,npm 仍作为安装入口
56
+ - `agentsshcli exec/upload/download` 默认使用 Rust daemon 连接缓存,也可以通过 `--no-cache` 直连
57
+ - 预编译平台包支持 macOS arm64/x64、Linux x64/arm64、Windows x64
56
58
 
57
59
  ### 安装步骤
58
60
 
@@ -67,6 +69,44 @@ agentsshcli --help
67
69
 
68
70
  打开 [SKILL.md](SKILL.md),将其添加到 agent 中。
69
71
 
72
+ ### 本地开发构建
73
+
74
+ 当前仓库保留 npm 安装入口,实际执行逻辑由 Rust 原生二进制完成。开发或源码安装时需要先构建原生二进制:
75
+
76
+ ```bash
77
+ npm run build:native
78
+ npm run build:native-bin
79
+ npm run build:native-package
80
+ npm test
81
+ ```
82
+
83
+ 执行链路:
84
+
85
+ ```text
86
+ agentsshcli 命令
87
+ -> bin/agentsshcli.js
88
+ -> native/target/release/agentsshcli-native
89
+ ```
90
+
91
+ 已由 Rust 实现:
92
+
93
+ - `agentsshcli list`
94
+ - `agentsshcli init-config`
95
+ - `agentsshcli exec ...` / `agentsshcli exec --no-cache ...`
96
+ - `agentsshcli upload ...` / `agentsshcli upload --no-cache ...`
97
+ - `agentsshcli download ...` / `agentsshcli download --no-cache ...`
98
+ - Rust daemon 连接缓存与 `--cache-ttl`
99
+
100
+ 发布 npm 包前先生成对应平台的预编译产物和平台子包,并检查包内容:
101
+
102
+ ```bash
103
+ npm run build:native-package
104
+ npm pack --dry-run
105
+ (cd npm/darwin-arm64 && npm pack --dry-run)
106
+ ```
107
+
108
+ 发布形态为主包 `agent-ssh-cli` 加平台 optional 子包:`@agent-ssh-cli/darwin-arm64`、`@agent-ssh-cli/darwin-x64`、`@agent-ssh-cli/linux-x64`、`@agent-ssh-cli/linux-arm64`、`@agent-ssh-cli/win32-x64`。预编译产物目录格式:`native-bin/<platform>-<arch>/agentsshcli-native`,Windows 文件名为 `agentsshcli-native.exe`。
109
+
70
110
  ## 配置
71
111
 
72
112
  初始化配置(格式参数和ssh-mcp-server一致):
@@ -147,12 +187,19 @@ AGENT_SSH_CONFIG=/path/to/config.json
147
187
 
148
188
  ```bash
149
189
  agentsshcli list
190
+ agentsshcli exec --no-cache 密码服务器 "pwd"
150
191
  ```
151
192
 
152
193
  完成安装!
153
194
 
154
195
  ## 卸载和清理
155
196
 
197
+ 更新到最新版:
198
+
199
+ ```bash
200
+ npm install -g agent-ssh-cli@latest
201
+ ```
202
+
156
203
  ```bash
157
204
  npm uninstall -g agent-ssh-cli
158
205
  npm cache clean --force
@@ -1,7 +1,56 @@
1
1
  #!/usr/bin/env node
2
- import { runAgentSshCli } from "../src/cli.js";
2
+ import { createRequire } from "node:module";
3
+ import { spawnSync } from "node:child_process";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
3
7
 
4
- runAgentSshCli(process.argv.slice(2)).catch((error) => {
5
- console.error(error.message);
8
+ const require = createRequire(import.meta.url);
9
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
10
+ const projectRoot = path.resolve(currentDir, "..");
11
+
12
+ function getExecutableName() {
13
+ return process.platform === "win32" ? "agentsshcli-native.exe" : "agentsshcli-native";
14
+ }
15
+
16
+ function getPlatformPackageBinary() {
17
+ const packageName = `@agent-ssh-cli/${process.platform}-${process.arch}`;
18
+ try {
19
+ const packageJsonPath = require.resolve(`${packageName}/package.json`);
20
+ return path.join(path.dirname(packageJsonPath), "bin", getExecutableName());
21
+ } catch {
22
+ return undefined;
23
+ }
24
+ }
25
+
26
+ function getCandidatePaths() {
27
+ const executableName = getExecutableName();
28
+ return [
29
+ getPlatformPackageBinary(),
30
+ path.join(projectRoot, "native-bin", `${process.platform}-${process.arch}`, executableName),
31
+ path.join(projectRoot, "native-bin", executableName),
32
+ path.join(projectRoot, "native", "target", "release", executableName),
33
+ path.join(projectRoot, "native", "target", "debug", executableName)
34
+ ].filter(Boolean);
35
+ }
36
+
37
+ function findNativeBinary() {
38
+ return getCandidatePaths().find((candidate) => fs.existsSync(candidate));
39
+ }
40
+
41
+ const binaryPath = findNativeBinary();
42
+ if (!binaryPath) {
43
+ console.error(`未找到 ${process.platform}-${process.arch} 的 Rust 原生可执行文件,请安装对应平台包或先运行 npm run build:native-bin`);
6
44
  process.exit(1);
45
+ }
46
+
47
+ const result = spawnSync(binaryPath, process.argv.slice(2), {
48
+ stdio: "inherit"
7
49
  });
50
+
51
+ if (result.error) {
52
+ console.error(result.error.message);
53
+ process.exit(1);
54
+ }
55
+
56
+ process.exit(result.status ?? 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-ssh-cli",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "基于 CLI 的 SSH 代理工具,按 ssh-mcp-server 的能力一一映射",
5
5
  "type": "module",
6
6
  "engines": {
@@ -9,7 +9,6 @@
9
9
  },
10
10
  "files": [
11
11
  "bin/",
12
- "src/",
13
12
  "example.config.json",
14
13
  "README.md"
15
14
  ],
@@ -17,9 +16,17 @@
17
16
  "agentsshcli": "bin/agentsshcli.js"
18
17
  },
19
18
  "scripts": {
20
- "test": "node --check bin/agentsshcli.js && node --check src/cli.js && node --check src/config.js && node --check src/daemon-client.js && node --check src/daemon-paths.js && node --check src/ssh-client.js && node --check src/ssh-daemon.js"
19
+ "test": "node --check bin/agentsshcli.js && cargo test --manifest-path native/Cargo.toml",
20
+ "build:native": "cargo build --release --manifest-path native/Cargo.toml",
21
+ "test:native": "cargo test --manifest-path native/Cargo.toml",
22
+ "build:native-bin": "node scripts/build-native-bin.js",
23
+ "build:native-package": "npm run build:native-bin && node scripts/build-native-package.js"
21
24
  },
22
- "dependencies": {
23
- "ssh2": "^1.17.0"
25
+ "optionalDependencies": {
26
+ "@agent-ssh-cli/darwin-arm64": "0.2.0",
27
+ "@agent-ssh-cli/darwin-x64": "0.2.0",
28
+ "@agent-ssh-cli/linux-x64": "0.2.0",
29
+ "@agent-ssh-cli/win32-x64": "0.2.0",
30
+ "@agent-ssh-cli/linux-arm64": "0.2.0"
24
31
  }
25
32
  }
package/src/cli.js DELETED
@@ -1,464 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import process from "node:process";
4
- import { fileURLToPath } from "node:url";
5
- import { loadConfig, findConnection, validateLocalPath, getDefaultConfigPath } from "./config.js";
6
- import { requestDaemon } from "./daemon-client.js";
7
- import { normalizeCacheTtl } from "./daemon-paths.js";
8
- import { executeRemoteCommand, uploadFile, downloadFile } from "./ssh-client.js";
9
-
10
- const currentDir = path.dirname(fileURLToPath(import.meta.url));
11
- const packageJson = JSON.parse(fs.readFileSync(path.join(currentDir, "..", "package.json"), "utf8"));
12
-
13
- const HELP_TEXT = {
14
- agentsshcli: `
15
- 用法:
16
- agentsshcli list [--config <path>] [--json]
17
- agentsshcli exec [--config <path>] [--no-cache] [--cache-ttl <ms>] <connectionName> <command>
18
- agentsshcli exec [--config <path>] [--no-cache] [--cache-ttl <ms>] --connection <name> --command <command> [--directory <dir>] [--timeout <ms>]
19
- agentsshcli upload [--config <path>] [--no-cache] [--cache-ttl <ms>] <connectionName> <localPath> <remotePath>
20
- agentsshcli upload [--config <path>] [--no-cache] [--cache-ttl <ms>] --connection <name> --local <path> --remote <path>
21
- agentsshcli download [--config <path>] [--no-cache] [--cache-ttl <ms>] <connectionName> <remotePath> <localPath>
22
- agentsshcli download [--config <path>] [--no-cache] [--cache-ttl <ms>] --connection <name> --remote <path> --local <path>
23
- agentsshcli init-config
24
- agentsshcli help [list|exec|upload|download]
25
- agentsshcli --help
26
- agentsshcli --version
27
-
28
- 说明:
29
- agent-ssh-cli 统一入口。
30
- `,
31
- sshls: `
32
- 用法:
33
- agentsshcli list [--config <path>] [--json]
34
- agentsshcli help list
35
- agentsshcli --version
36
-
37
- 说明:
38
- 列出当前配置文件中的 SSH 连接。
39
- `,
40
- sshx: `
41
- 用法:
42
- agentsshcli exec [--config <path>] [--no-cache] [--cache-ttl <ms>] <connectionName> <command>
43
- agentsshcli exec [--config <path>] [--no-cache] [--cache-ttl <ms>] --connection <name> --command <command> [--directory <dir>] [--timeout <ms>]
44
- agentsshcli help exec
45
- agentsshcli --version
46
-
47
- 说明:
48
- 在远端执行命令。
49
- `,
50
- sshupload: `
51
- 用法:
52
- agentsshcli upload [--config <path>] [--no-cache] [--cache-ttl <ms>] <connectionName> <localPath> <remotePath>
53
- agentsshcli upload [--config <path>] [--no-cache] [--cache-ttl <ms>] --connection <name> --local <path> --remote <path>
54
- agentsshcli help upload
55
- agentsshcli --version
56
-
57
- 说明:
58
- 上传本地文件到远端。
59
- `,
60
- sshdownload: `
61
- 用法:
62
- agentsshcli download [--config <path>] [--no-cache] [--cache-ttl <ms>] <connectionName> <remotePath> <localPath>
63
- agentsshcli download [--config <path>] [--no-cache] [--cache-ttl <ms>] --connection <name> --remote <path> --local <path>
64
- agentsshcli help download
65
- agentsshcli --version
66
-
67
- 说明:
68
- 下载远端文件到本地。
69
- `
70
- };
71
-
72
- function printHelp(commandName) {
73
- process.stdout.write(`${HELP_TEXT[commandName].trim()}\n`);
74
- }
75
-
76
- function printVersion() {
77
- process.stdout.write(`${packageJson.version}\n`);
78
- }
79
-
80
- function getHelpName(commandName) {
81
- const names = {
82
- list: "sshls",
83
- exec: "sshx",
84
- upload: "sshupload",
85
- download: "sshdownload"
86
- };
87
- return names[commandName] || commandName;
88
- }
89
-
90
- function initConfig() {
91
- const target = getDefaultConfigPath();
92
- const source = path.join(currentDir, "..", "example.config.json");
93
- if (fs.existsSync(target)) {
94
- throw new Error(`${target} 已存在,未覆盖`);
95
- }
96
- fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
97
- fs.copyFileSync(source, target);
98
- process.stdout.write(`已创建 ${target}\n`);
99
- }
100
-
101
- function parseGlobalArgs(argv) {
102
- const args = [...argv];
103
- let configPath = getDefaultConfigPath();
104
- let help = false;
105
- let version = false;
106
- let json = false;
107
- let noCache = false;
108
- let cacheTtlMs;
109
- const remaining = [];
110
-
111
- while (args.length > 0) {
112
- const current = args.shift();
113
- if (current === "--help" || current === "-h") {
114
- help = true;
115
- continue;
116
- }
117
- if (current === "--version" || current === "-v") {
118
- version = true;
119
- continue;
120
- }
121
- if (current === "--json") {
122
- json = true;
123
- continue;
124
- }
125
- if (current === "--no-cache") {
126
- noCache = true;
127
- continue;
128
- }
129
- if (current === "--cache-ttl") {
130
- const value = args.shift();
131
- if (!value) {
132
- throw new Error("--cache-ttl 缺少毫秒值");
133
- }
134
- cacheTtlMs = normalizeCacheTtl(value);
135
- continue;
136
- }
137
- if (current === "--config") {
138
- const value = args.shift();
139
- if (!value) {
140
- throw new Error("--config 缺少路径");
141
- }
142
- configPath = path.resolve(value);
143
- continue;
144
- }
145
- remaining.push(current, ...args);
146
- break;
147
- }
148
-
149
- return {
150
- configPath,
151
- help,
152
- version,
153
- json,
154
- noCache,
155
- cacheTtlMs,
156
- args: remaining
157
- };
158
- }
159
-
160
- function findOptionIndexes(args, names) {
161
- const indexes = [];
162
- for (let index = 0; index < args.length; index += 1) {
163
- if (names.includes(args[index])) {
164
- indexes.push(index);
165
- }
166
- }
167
- return indexes;
168
- }
169
-
170
- function takeOption(args, names) {
171
- const indexes = findOptionIndexes(args, names);
172
- if (indexes.length > 1) {
173
- throw new Error(`参数重复声明: ${names[0]}`);
174
- }
175
- if (indexes.length === 0) {
176
- return {
177
- present: false,
178
- value: undefined
179
- };
180
- }
181
- const index = indexes[0];
182
- const value = args[index + 1];
183
- if (!value || value.startsWith("--")) {
184
- throw new Error(`${args[index]} 缺少参数值`);
185
- }
186
- args.splice(index, 2);
187
- return {
188
- present: true,
189
- value
190
- };
191
- }
192
-
193
- function ensureNoUnknownOptions(args) {
194
- const unknown = args.find((item) => item.startsWith("--"));
195
- if (unknown) {
196
- throw new Error(`不支持的参数: ${unknown}`);
197
- }
198
- }
199
-
200
- function ensureNoExtraPositionals(args) {
201
- if (args.length > 0) {
202
- throw new Error(`存在多余的位置参数: ${args.join(" ")}`);
203
- }
204
- }
205
-
206
- function takePositional(args, fieldName) {
207
- const value = args.shift();
208
- if (value && value.startsWith("--")) {
209
- throw new Error(`${fieldName} 位置参数非法: ${value}`);
210
- }
211
- return value;
212
- }
213
-
214
- function ensureNoMixedInput(namedValue, positionalValue, fieldName) {
215
- if (namedValue !== undefined && positionalValue !== undefined) {
216
- throw new Error(`${fieldName} 同时使用了命名参数和位置参数,保留一种即可`);
217
- }
218
- }
219
-
220
- function resolveValue(args, optionNames, fieldName) {
221
- const namedOption = takeOption(args, optionNames);
222
- if (namedOption.present) {
223
- return namedOption.value;
224
- }
225
- return takePositional(args, fieldName);
226
- }
227
-
228
- function parseListArgs(argv) {
229
- const parsed = parseGlobalArgs(argv);
230
- if (parsed.help || parsed.version) {
231
- return parsed;
232
- }
233
- if (parsed.args.length > 0) {
234
- throw new Error(`agentsshcli list 不接受位置参数: ${parsed.args.join(" ")}`);
235
- }
236
- return parsed;
237
- }
238
-
239
- function parseExecuteArgs(argv) {
240
- const parsed = parseGlobalArgs(argv);
241
- if (parsed.help || parsed.version) {
242
- return parsed;
243
- }
244
- const args = [...parsed.args];
245
- const connectionNameOption = takeOption(args, ["--connection", "-c"]);
246
- const commandOption = takeOption(args, ["--command"]);
247
- const directoryOption = takeOption(args, ["--directory", "-d"]);
248
- const timeoutOption = takeOption(args, ["--timeout", "-t"]);
249
- const connectionNamePositional = takePositional(args, "connectionName");
250
- const commandPositional = takePositional(args, "command");
251
-
252
- ensureNoMixedInput(connectionNameOption.present ? connectionNameOption.value : undefined, connectionNamePositional, "connectionName");
253
- ensureNoMixedInput(commandOption.present ? commandOption.value : undefined, commandPositional, "command");
254
-
255
- const connectionName = connectionNameOption.present ? connectionNameOption.value : connectionNamePositional;
256
- const command = commandOption.present ? commandOption.value : commandPositional;
257
- const directory = directoryOption.present ? directoryOption.value : undefined;
258
- const timeoutValue = timeoutOption.present ? timeoutOption.value : undefined;
259
- for (let index = 0; index < args.length; index += 1) {
260
- if (args[index]?.startsWith("--")) {
261
- throw new Error(`不支持的参数: ${args[index]}`);
262
- }
263
- }
264
- ensureNoUnknownOptions(args);
265
- ensureNoExtraPositionals(args);
266
- if (!connectionName || !command) {
267
- throw new Error("缺少必填参数 connectionName 或 command,使用 --help 查看说明");
268
- }
269
- const timeout = timeoutValue === undefined ? 30000 : Number(timeoutValue);
270
- if (!Number.isFinite(timeout) || timeout <= 0) {
271
- throw new Error("timeout 必须是正整数毫秒值");
272
- }
273
- return {
274
- ...parsed,
275
- connectionName,
276
- command,
277
- directory,
278
- timeout
279
- };
280
- }
281
-
282
- function parseTransferArgs(argv, mode) {
283
- const parsed = parseGlobalArgs(argv);
284
- if (parsed.help || parsed.version) {
285
- return parsed;
286
- }
287
- const args = [...parsed.args];
288
- const connectionName = resolveValue(args, ["--connection", "-c"], "connectionName");
289
- const localOptionNames = ["--local", "-l"];
290
- const remoteOptionNames = ["--remote", "-r"];
291
- let localPath;
292
- let remotePath;
293
-
294
- if (mode === "upload") {
295
- localPath = resolveValue(args, localOptionNames, "localPath");
296
- remotePath = resolveValue(args, remoteOptionNames, "remotePath");
297
- } else {
298
- remotePath = resolveValue(args, remoteOptionNames, "remotePath");
299
- localPath = resolveValue(args, localOptionNames, "localPath");
300
- }
301
-
302
- ensureNoUnknownOptions(args);
303
- ensureNoExtraPositionals(args);
304
-
305
- if (!connectionName || !localPath || !remotePath) {
306
- throw new Error("缺少必填参数,使用 --help 查看说明");
307
- }
308
-
309
- return {
310
- ...parsed,
311
- connectionName,
312
- localPath,
313
- remotePath
314
- };
315
- }
316
-
317
- export async function runListServers(argv) {
318
- const parsed = parseListArgs(argv);
319
- if (parsed.help) {
320
- printHelp("sshls");
321
- return;
322
- }
323
- if (parsed.version) {
324
- printVersion();
325
- return;
326
- }
327
- const configs = loadConfig(parsed.configPath);
328
- const output = configs.map((item) => ({
329
- name: item.name,
330
- host: item.host,
331
- port: item.port,
332
- username: item.username
333
- }));
334
- process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
335
- }
336
-
337
- export async function runExecute(argv) {
338
- const parsed = parseExecuteArgs(argv);
339
- if (parsed.help) {
340
- printHelp("sshx");
341
- return;
342
- }
343
- if (parsed.version) {
344
- printVersion();
345
- return;
346
- }
347
- const configs = loadConfig(parsed.configPath);
348
- const connection = findConnection(configs, parsed.connectionName);
349
- const result = parsed.noCache
350
- ? await executeRemoteCommand(connection, parsed.command, parsed.directory, parsed.timeout)
351
- : (await requestDaemon(parsed.configPath, {
352
- operation: "execute",
353
- configPath: parsed.configPath,
354
- cwd: process.cwd(),
355
- connectionName: parsed.connectionName,
356
- command: parsed.command,
357
- directory: parsed.directory,
358
- timeout: parsed.timeout,
359
- cacheTtlMs: parsed.cacheTtlMs
360
- })).stdout;
361
- if (result) {
362
- process.stdout.write(`${result}\n`);
363
- }
364
- }
365
-
366
- export async function runUpload(argv) {
367
- const parsed = parseTransferArgs(argv, "upload");
368
- if (parsed.help) {
369
- printHelp("sshupload");
370
- return;
371
- }
372
- if (parsed.version) {
373
- printVersion();
374
- return;
375
- }
376
- const configs = loadConfig(parsed.configPath);
377
- const connection = findConnection(configs, parsed.connectionName);
378
- if (parsed.noCache) {
379
- const validatedLocalPath = validateLocalPath(configs, parsed.localPath);
380
- await uploadFile(connection, validatedLocalPath, parsed.remotePath);
381
- } else {
382
- await requestDaemon(parsed.configPath, {
383
- operation: "upload",
384
- configPath: parsed.configPath,
385
- cwd: process.cwd(),
386
- connectionName: parsed.connectionName,
387
- localPath: parsed.localPath,
388
- remotePath: parsed.remotePath,
389
- cacheTtlMs: parsed.cacheTtlMs
390
- });
391
- }
392
- process.stdout.write("File uploaded successfully\n");
393
- }
394
-
395
- export async function runDownload(argv) {
396
- const parsed = parseTransferArgs(argv, "download");
397
- if (parsed.help) {
398
- printHelp("sshdownload");
399
- return;
400
- }
401
- if (parsed.version) {
402
- printVersion();
403
- return;
404
- }
405
- const configs = loadConfig(parsed.configPath);
406
- const connection = findConnection(configs, parsed.connectionName);
407
- if (parsed.noCache) {
408
- const validatedLocalPath = validateLocalPath(configs, parsed.localPath);
409
- fs.mkdirSync(path.dirname(validatedLocalPath), { recursive: true });
410
- await downloadFile(connection, parsed.remotePath, validatedLocalPath);
411
- } else {
412
- await requestDaemon(parsed.configPath, {
413
- operation: "download",
414
- configPath: parsed.configPath,
415
- cwd: process.cwd(),
416
- connectionName: parsed.connectionName,
417
- localPath: parsed.localPath,
418
- remotePath: parsed.remotePath,
419
- cacheTtlMs: parsed.cacheTtlMs
420
- });
421
- }
422
- process.stdout.write("File downloaded successfully\n");
423
- }
424
-
425
- export async function runAgentSshCli(argv) {
426
- const [command, ...args] = argv;
427
- if (!command || command === "--help" || command === "-h") {
428
- printHelp("agentsshcli");
429
- return;
430
- }
431
- if (command === "--version" || command === "-v" || command === "version") {
432
- printVersion();
433
- return;
434
- }
435
- if (command === "help") {
436
- const target = getHelpName(args[0] || "agentsshcli");
437
- if (!HELP_TEXT[target]) {
438
- throw new Error(`未知帮助命令: ${args[0]}`);
439
- }
440
- printHelp(target);
441
- return;
442
- }
443
- if (command === "init-config") {
444
- initConfig();
445
- return;
446
- }
447
- if (command === "list") {
448
- await runListServers(args);
449
- return;
450
- }
451
- if (command === "exec") {
452
- await runExecute(args);
453
- return;
454
- }
455
- if (command === "upload") {
456
- await runUpload(args);
457
- return;
458
- }
459
- if (command === "download") {
460
- await runDownload(args);
461
- return;
462
- }
463
- throw new Error(`未知命令: ${command},使用 agentsshcli --help 查看说明`);
464
- }