refactor: extract helpers from execScript and _cloud_api_retry_loop (#821)

Reduce cyclomatic complexity in the two highest-scoring functions:

- cli/src/commands.ts: Extract `handleUserInterrupt` and `runWithRetries`
  from `execScript` (complexity score 6 -> 2 for execScript, retry logic
  now independently testable)

- shared/common.sh: Extract `_classify_api_result` and `_report_api_failure`
  from `_cloud_api_retry_loop` (complexity score 9 -> 4, removes duplicated
  error-classification logic from loop body)

Agent: complexity-hunter

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
A 2026-02-12 23:57:20 -08:00 committed by GitHub
parent e73d6b9793
commit 4d3c54a11e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 67 additions and 45 deletions

View file

@ -657,6 +657,37 @@ export function isRetryableExitCode(errMsg: string): boolean {
return code === 255;
}
function handleUserInterrupt(errMsg: string): void {
if (!errMsg.includes("interrupted by user")) return;
console.error();
p.log.warn("Script interrupted (Ctrl+C).");
p.log.warn("If a server was already created, it may still be running.");
p.log.warn(` Check your cloud provider dashboard to stop or delete any unused servers.`);
process.exit(130);
}
async function runWithRetries(script: string, prompt?: string): Promise<string | undefined> {
for (let attempt = 1; attempt <= MAX_RETRIES + 1; attempt++) {
try {
await runBash(script, prompt);
return undefined; // success
} catch (err) {
const errMsg = getErrorMessage(err);
handleUserInterrupt(errMsg);
if (attempt <= MAX_RETRIES && isRetryableExitCode(errMsg)) {
const delay = RETRY_DELAYS[attempt - 1];
p.log.warn(`Script failed (${errMsg}). Retrying in ${delay}s (attempt ${attempt + 1}/${MAX_RETRIES + 1})...`);
await new Promise(r => setTimeout(r, delay * 1000));
continue;
}
return errMsg;
}
}
return "Script failed after all retries";
}
async function execScript(cloud: string, agent: string, prompt?: string, authHint?: string): Promise<void> {
const url = `https://openrouter.ai/labs/spawn/${cloud}/${agent}.sh`;
const ghUrl = `${RAW_BASE}/${cloud}/${agent}.sh`;
@ -680,36 +711,10 @@ async function execScript(cloud: string, agent: string, prompt?: string, authHin
// Non-fatal: don't block the spawn if history write fails
}
let lastErr: string | undefined;
for (let attempt = 1; attempt <= MAX_RETRIES + 1; attempt++) {
try {
await runBash(scriptContent, prompt);
return; // success
} catch (err) {
const errMsg = getErrorMessage(err);
if (errMsg.includes("interrupted by user")) {
console.error();
p.log.warn("Script interrupted (Ctrl+C).");
p.log.warn("If a server was already created, it may still be running.");
p.log.warn(` Check your cloud provider dashboard to stop or delete any unused servers.`);
process.exit(130);
}
lastErr = errMsg;
// Only retry for potentially transient failures
if (attempt <= MAX_RETRIES && isRetryableExitCode(errMsg)) {
const delay = RETRY_DELAYS[attempt - 1];
p.log.warn(`Script failed (${errMsg}). Retrying in ${delay}s (attempt ${attempt + 1}/${MAX_RETRIES + 1})...`);
await new Promise(r => setTimeout(r, delay * 1000));
continue;
}
// Non-retryable or out of retries
break;
}
const lastErr = await runWithRetries(scriptContent, prompt);
if (lastErr) {
reportScriptFailure(lastErr, cloud, agent, authHint, prompt);
}
reportScriptFailure(lastErr!, cloud, agent, authHint, prompt);
}
function runBash(script: string, prompt?: string): Promise<void> {