mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-13 15:40:55 +00:00
fix(ux): stop spinner before credential prompts during delete (#2144)
When credentials expire during server deletion, the spinner was running simultaneously with interactive credential prompts, creating confusing overlapping UI. Extract ensureDeleteCredentials() to run all credential checks (which may prompt the user) before starting the deletion spinner. All 6 cloud providers are covered: AWS, Hetzner, DigitalOcean, GCP, Daytona, and Sprite. Fixes #2141 Co-authored-by: B <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
931fbed8b3
commit
0aea348b8f
1 changed files with 53 additions and 0 deletions
|
|
@ -19,6 +19,55 @@ import { destroyServer as spriteDestroyServer, ensureSpriteCli, ensureSpriteAuth
|
|||
import { getErrorMessage, isInteractiveTTY } from "./shared.js";
|
||||
import { resolveListFilters, activeServerPicker } from "./list.js";
|
||||
|
||||
/**
|
||||
* Ensure credentials are available for a record's cloud provider.
|
||||
* This may prompt the user interactively and must be called BEFORE
|
||||
* starting any spinner to avoid overlapping UI elements.
|
||||
*/
|
||||
export async function ensureDeleteCredentials(record: SpawnRecord): Promise<void> {
|
||||
const conn = record.connection;
|
||||
if (!conn?.cloud || conn.cloud === "local") {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (conn.cloud) {
|
||||
case "hetzner":
|
||||
await ensureHcloudToken();
|
||||
break;
|
||||
case "digitalocean":
|
||||
await ensureDoToken();
|
||||
break;
|
||||
case "gcp": {
|
||||
const zone = conn.metadata?.zone || "us-central1-a";
|
||||
const project = conn.metadata?.project || "";
|
||||
validateMetadataValue(zone, "GCP zone");
|
||||
if (project) {
|
||||
validateMetadataValue(project, "GCP project");
|
||||
}
|
||||
process.env.GCP_ZONE = zone;
|
||||
if (project) {
|
||||
process.env.GCP_PROJECT = project;
|
||||
}
|
||||
await gcpEnsureGcloudCli();
|
||||
await gcpAuthenticate();
|
||||
break;
|
||||
}
|
||||
case "aws":
|
||||
await ensureAwsCli();
|
||||
await awsAuthenticate();
|
||||
break;
|
||||
case "daytona":
|
||||
await ensureDaytonaToken();
|
||||
break;
|
||||
case "sprite":
|
||||
await ensureSpriteCli();
|
||||
await ensureSpriteAuthenticated();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Execute server deletion for a given record using TypeScript cloud modules */
|
||||
export async function execDeleteServer(record: SpawnRecord): Promise<boolean> {
|
||||
const conn = record.connection;
|
||||
|
|
@ -148,6 +197,10 @@ export async function confirmAndDelete(record: SpawnRecord, manifest: Manifest |
|
|||
return false;
|
||||
}
|
||||
|
||||
// Ensure credentials before starting the spinner so interactive
|
||||
// prompts (e.g. expired API key entry) don't overlap with it.
|
||||
await ensureDeleteCredentials(record);
|
||||
|
||||
const s = p.spinner();
|
||||
s.start(`Deleting ${label}...`);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue