refactor: add no-try-catch + no-try-finally grit rules, eliminate all violations (#2481)

Add two new GritQL biome plugins (matching ori repo patterns) that ban
all try/catch and try/finally in TypeScript code. Convert all remaining
blocks across production and test files to use tryCatch/asyncTryCatch
from @openrouter/spawn-shared.

no-try-catch.grit covers all 4 variants:
- try/catch with binding, try/catch without binding
- try/catch/finally with binding, try/catch/finally without binding

no-try-finally.grit covers bare try/finally.

Both exclude shared/result.ts and shared/parse.ts (the implementation layer).

Production files (18): aws, hetzner, digitalocean, gcp, sprite, index,
update-check, ui, ssh, agent-setup, picker, agent-tarball, shared,
run, connect, delete, list

Test files (12): cmdlast, cmd-interactive, cmdrun-happy-path,
commands-resolve-run, commands-swap-resolve, commands-error-paths,
download-and-failure, preload, ssh-keys, update-check, orchestrate,
fs-sandbox, prompt-file-security, security, script-failure-guidance

Bumps CLI version to 0.16.6

Co-authored-by: lab <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-03-10 21:27:25 -07:00 committed by GitHub
parent 9a1dad7fcb
commit 46b1e9d42c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 505 additions and 635 deletions

View file

@ -543,21 +543,22 @@ export async function waitForCloudInit(ip?: string, maxAttempts = 60): Promise<v
// can continue and the user isn't left with a hung CLI.
const timer = setTimeout(() => killWithTimeout(proc), 30_000);
// Drain both pipes before awaiting exit to prevent pipe buffer deadlock
let stdout: string;
let exitCode: number;
try {
[stdout] = await Promise.all([
const pipeResult = await asyncTryCatch(async () => {
const [stdout] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
]);
exitCode = await proc.exited;
} finally {
clearTimeout(timer);
const exitCode = await proc.exited;
return {
stdout,
exitCode,
};
});
clearTimeout(timer);
if (!pipeResult.ok) {
throw pipeResult.error;
}
return {
stdout,
exitCode,
};
return pipeResult.data;
});
if (pollResult.ok && pollResult.data.exitCode === 0 && pollResult.data.stdout.includes("done")) {
logStepDone();
@ -598,13 +599,13 @@ export async function runServer(cmd: string, timeoutSecs?: number, ip?: string):
const timeout = (timeoutSecs || 300) * 1000;
const timer = setTimeout(() => killWithTimeout(proc), timeout);
try {
const exitCode = await proc.exited;
if (exitCode !== 0) {
throw new Error(`run_server failed (exit ${exitCode}): ${cmd}`);
}
} finally {
clearTimeout(timer);
const runResult = await asyncTryCatch(() => proc.exited);
clearTimeout(timer);
if (!runResult.ok) {
throw runResult.error;
}
if (runResult.data !== 0) {
throw new Error(`run_server failed (exit ${runResult.data}): ${cmd}`);
}
}
@ -638,13 +639,13 @@ export async function uploadFile(localPath: string, remotePath: string, ip?: str
},
);
const timer = setTimeout(() => killWithTimeout(proc), 120_000);
try {
const exitCode = await proc.exited;
if (exitCode !== 0) {
throw new Error(`upload_file failed for ${remotePath}`);
}
} finally {
clearTimeout(timer);
const uploadResult = await asyncTryCatch(() => proc.exited);
clearTimeout(timer);
if (!uploadResult.ok) {
throw uploadResult.error;
}
if (uploadResult.data !== 0) {
throw new Error(`upload_file failed for ${remotePath}`);
}
}