feat: 实现 SSH Remote — 本地 REPL + 远端工具执行

SSH Remote 允许在本地运行交互式 REPL,同时将工具调用(Bash、文件读写等)
通过 SSH 隧道转发到远程主机执行。

核心模块:
- SSHSessionManager: NDJSON 双向通信、权限转发、指数退避重连
- SSHAuthProxy: 本地认证代理 + SSH -R 反向端口转发,nonce 验证
- SSHProbe: 远端主机平台/架构/已有二进制探测
- SSHDeploy: 远端二进制部署(scp)
- createSSHSession: 会话编排(probe → deploy → spawn → attach)

新增选项:
- --remote-bin: 跳过 probe/deploy,使用自定义远端二进制
- ANTHROPIC_AUTH_NONCE: API 请求认证 nonce header

包含 17 个单元测试和完整文档。
This commit is contained in:
unraid
2026-04-24 14:25:56 +08:00
parent 2a5b263641
commit 03811f973b
10 changed files with 2010 additions and 15 deletions

View File

@@ -869,6 +869,7 @@ type PendingSSH = {
local: boolean;
/** Extra CLI args to forward to the remote CLI on initial spawn (--resume, -c). */
extraCliArgs: string[];
remoteBin: string | undefined;
};
const _pendingSSH: PendingSSH | undefined = feature("SSH_REMOTE")
? {
@@ -878,6 +879,7 @@ const _pendingSSH: PendingSSH | undefined = feature("SSH_REMOTE")
dangerouslySkipPermissions: false,
local: false,
extraCliArgs: [],
remoteBin: undefined,
}
: undefined;
@@ -1084,6 +1086,17 @@ export async function main() {
rawCliArgs.splice(eqI, 1);
}
};
const rbIdx = rawCliArgs.indexOf('--remote-bin');
if (rbIdx !== -1 && rawCliArgs[rbIdx + 1] && !rawCliArgs[rbIdx + 1]!.startsWith('-')) {
_pendingSSH.remoteBin = rawCliArgs[rbIdx + 1];
rawCliArgs.splice(rbIdx, 2);
}
const rbEqIdx = rawCliArgs.findIndex(a => a.startsWith('--remote-bin='));
if (rbEqIdx !== -1) {
_pendingSSH.remoteBin = rawCliArgs[rbEqIdx]!.split('=').slice(1).join('=');
rawCliArgs.splice(rbEqIdx, 1);
}
extractFlag("-c", { as: "--continue" });
extractFlag("--continue");
extractFlag("--resume", { hasValue: true });
@@ -4643,6 +4656,7 @@ async function run(): Promise<CommanderCommand> {
dangerouslySkipPermissions:
_pendingSSH.dangerouslySkipPermissions,
extraCliArgs: _pendingSSH.extraCliArgs,
remoteBin: _pendingSSH.remoteBin,
},
isTTY
? {
@@ -5980,6 +5994,11 @@ async function run(): Promise<CommanderCommand> {
"--dangerously-skip-permissions",
"Skip all permission prompts on the remote (dangerous)",
)
.option(
"--remote-bin <command>",
"Custom remote binary command (skips probe/deploy). " +
"Example: --remote-bin 'bun /path/to/project/dist/cli.js'",
)
.option(
"--local",
"e2e test mode — spawn the child CLI locally (skip ssh/deploy). " +