refactor: reduce complexity in cmdConnect and setup_claude_code_config (#1191)

Extract helper functions to reduce nesting and duplication:

1. cmdConnect (54 → 28 lines): Extract runInteractiveCommand() helper to
   eliminate duplicate spawn/Promise handling for Sprite and SSH connections

2. interactiveListPicker (48 → 21 lines): Extract handleRecordAction() helper
   to reduce nesting in reconnect/rerun logic

3. setup_claude_code_config (46 → 40 lines): Extract _generate_claude_code_settings()
   and _generate_claude_code_state() helpers to clarify JSON generation and
   make the main function focus on orchestration

All changes preserve existing behavior and pass existing tests.

Agent: complexity-hunter

Co-authored-by: spawn-refactor-bot <refactor@openrouter.ai>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
A 2026-02-15 02:18:40 -08:00 committed by GitHub
parent 90417c2e1b
commit 8564e6d984
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 103 additions and 87 deletions

View file

@ -1523,6 +1523,45 @@ async function resolveListFilters(
return { manifest, agentFilter, cloudFilter };
}
/** Handle reconnect or rerun action for a selected spawn record */
async function handleRecordAction(
selected: SpawnRecord,
manifest: Manifest | null
): Promise<void> {
if (!selected.connection) {
// No connection info -- just rerun
p.log.step(`Spawning ${pc.bold(buildRecordLabel(selected, manifest))}`);
await cmdRun(selected.agent, selected.cloud, selected.prompt);
return;
}
const action = await p.select({
message: "What would you like to do?",
options: [
{ value: "reconnect", label: "Reconnect to existing VM", hint: `ssh ${selected.connection.user}@${selected.connection.ip}` },
{ value: "rerun", label: "Spawn a new VM", hint: "Create a fresh instance" },
],
});
if (p.isCancel(action)) {
handleCancel();
}
if (action === "reconnect") {
try {
await cmdConnect(selected.connection);
} catch (err) {
p.log.error(`Connection failed: ${getErrorMessage(err)}`);
p.log.info(`VM may no longer be running. Use ${pc.cyan(`spawn ${selected.agent}/${selected.cloud}`)} to start a new one.`);
}
return;
}
// Rerun (create new spawn)
p.log.step(`Spawning ${pc.bold(buildRecordLabel(selected, manifest))}`);
await cmdRun(selected.agent, selected.cloud, selected.prompt);
}
/** Show interactive picker to select and reconnect/rerun a previous spawn */
async function interactiveListPicker(records: SpawnRecord[], manifest: Manifest | null): Promise<void> {
p.log.info(pc.dim(`Filter: ${pc.cyan("spawn list -a <agent>")} or ${pc.cyan("spawn list -c <cloud>")} | Clear: ${pc.cyan("spawn list --clear")}`));
@ -1542,35 +1581,7 @@ async function interactiveListPicker(records: SpawnRecord[], manifest: Manifest
}
const selected = records[choice];
// If there's connection info, offer to reconnect or rerun
if (selected.connection) {
const action = await p.select({
message: "What would you like to do?",
options: [
{ value: "reconnect", label: "Reconnect to existing VM", hint: `ssh ${selected.connection.user}@${selected.connection.ip}` },
{ value: "rerun", label: "Spawn a new VM", hint: "Create a fresh instance" },
],
});
if (p.isCancel(action)) {
handleCancel();
}
if (action === "reconnect") {
try {
await cmdConnect(selected.connection);
} catch (err) {
p.log.error(`Connection failed: ${getErrorMessage(err)}`);
p.log.info(`VM may no longer be running. Use ${pc.cyan(`spawn ${selected.agent}/${selected.cloud}`)} to start a new one.`);
}
return;
}
}
// Rerun (create new spawn)
p.log.step(`Spawning ${pc.bold(buildRecordLabel(selected, manifest))}`);
await cmdRun(selected.agent, selected.cloud, selected.prompt);
await handleRecordAction(selected, manifest);
}
export async function cmdListClear(): Promise<void> {
@ -1641,62 +1652,57 @@ export async function cmdLast(): Promise<void> {
// ── Connect ────────────────────────────────────────────────────────────────────
/** Connect to an existing VM via SSH */
async function cmdConnect(connection: VMConnection): Promise<void> {
// Handle Sprite console connections
if (connection.ip === "sprite-console" && connection.server_name) {
p.log.step(`Connecting to sprite ${pc.bold(connection.server_name)}...`);
return new Promise<void>((resolve, reject) => {
const child = spawn("sprite", ["console", "-s", connection.server_name], {
stdio: "inherit",
});
child.on("close", (code: number | null) => {
if (code === 0 || code === null) {
resolve();
} else {
reject(new Error(`Sprite console connection failed with exit code ${code}`));
}
});
child.on("error", (err) => {
p.log.error(`Failed to connect: ${getErrorMessage(err)}`);
p.log.info(`Try manually: ${pc.cyan(`sprite console -s ${connection.server_name}`)}`);
reject(err);
});
});
}
// Handle SSH connections
p.log.step(`Connecting to ${pc.bold(connection.ip)}...`);
const sshCmd = `ssh -o StrictHostKeyChecking=accept-new ${connection.user}@${connection.ip}`;
/** Execute a shell command and resolve/reject on process close/error */
function runInteractiveCommand(
cmd: string,
args: string[],
failureMsg: string,
manualCmd: string
): Promise<void> {
return new Promise<void>((resolve, reject) => {
const child = spawn("ssh", [
"-o", "StrictHostKeyChecking=accept-new",
`${connection.user}@${connection.ip}`
], {
stdio: "inherit",
});
const child = spawn(cmd, args, { stdio: "inherit" });
child.on("close", (code: number | null) => {
if (code === 0 || code === null) {
resolve();
} else {
reject(new Error(`SSH connection failed with exit code ${code}`));
reject(new Error(`${failureMsg} with exit code ${code}`));
}
});
child.on("error", (err) => {
p.log.error(`Failed to connect: ${getErrorMessage(err)}`);
p.log.info(`Try manually: ${pc.cyan(sshCmd)}`);
p.log.info(`Try manually: ${pc.cyan(manualCmd)}`);
reject(err);
});
});
}
/** Connect to an existing VM via SSH */
async function cmdConnect(connection: VMConnection): Promise<void> {
// Handle Sprite console connections
if (connection.ip === "sprite-console" && connection.server_name) {
p.log.step(`Connecting to sprite ${pc.bold(connection.server_name)}...`);
return runInteractiveCommand(
"sprite",
["console", "-s", connection.server_name],
"Sprite console connection failed",
`sprite console -s ${connection.server_name}`
);
}
// Handle SSH connections
p.log.step(`Connecting to ${pc.bold(connection.ip)}...`);
const sshCmd = `ssh -o StrictHostKeyChecking=accept-new ${connection.user}@${connection.ip}`;
return runInteractiveCommand(
"ssh",
["-o", "StrictHostKeyChecking=accept-new", `${connection.user}@${connection.ip}`],
"SSH connection failed",
sshCmd
);
}
// ── Agents ─────────────────────────────────────────────────────────────────────
export function getImplementedAgents(manifest: Manifest, cloud: string): string[] {

View file

@ -2432,21 +2432,13 @@ upload_config_file() {
# setup_claude_code_config "$OPENROUTER_API_KEY" \
# "upload_file_sprite $SPRITE_NAME" \
# "run_sprite $SPRITE_NAME"
setup_claude_code_config() {
# Generate Claude Code settings.json with API key
_generate_claude_code_settings() {
local openrouter_key="${1}"
local upload_callback="${2}"
local run_callback="${3}"
log_step "Configuring Claude Code..."
# Create ~/.claude directory
${run_callback} "mkdir -p ~/.claude"
# Create settings.json
local escaped_key
escaped_key=$(json_escape "${openrouter_key}")
local settings_json
settings_json=$(cat << EOF
cat << EOF
{
"theme": "dark",
"editor": "vim",
@ -2461,18 +2453,36 @@ setup_claude_code_config() {
}
}
EOF
)
upload_config_file "${upload_callback}" "${run_callback}" "${settings_json}" "\$HOME/.claude/settings.json"
}
# Create .claude.json global state
local global_state_json
global_state_json=$(cat << EOF
# Generate Claude Code global state JSON
_generate_claude_code_state() {
cat << EOF
{
"hasCompletedOnboarding": true,
"bypassPermissionsModeAccepted": true
}
EOF
)
}
setup_claude_code_config() {
local openrouter_key="${1}"
local upload_callback="${2}"
local run_callback="${3}"
log_step "Configuring Claude Code..."
# Create ~/.claude directory
${run_callback} "mkdir -p ~/.claude"
# Create settings.json
local settings_json
settings_json=$(_generate_claude_code_settings "${openrouter_key}")
upload_config_file "${upload_callback}" "${run_callback}" "${settings_json}" "\$HOME/.claude/settings.json"
# Create .claude.json global state
local global_state_json
global_state_json=$(_generate_claude_code_state)
upload_config_file "${upload_callback}" "${run_callback}" "${global_state_json}" "\$HOME/.claude.json"
# Create empty CLAUDE.md