From 9a35845ceb7707b2e95c2ac084caa97d353e287f Mon Sep 17 00:00:00 2001 From: Badri Narayanan S Date: Sun, 21 Dec 2025 15:05:45 +0530 Subject: [PATCH] add ability to remove accounts. ensure accounts are not / added / removed when server is running --- package.json | 1 + src/accounts-cli.js | 112 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 108 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2917fc5..3571ff1 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "accounts": "node src/accounts-cli.js", "accounts:add": "node src/accounts-cli.js add", "accounts:list": "node src/accounts-cli.js list", + "accounts:remove": "node src/accounts-cli.js remove", "accounts:verify": "node src/accounts-cli.js verify", "test": "node tests/run-all.cjs", "test:signatures": "node tests/test-thinking-signatures.cjs", diff --git a/src/accounts-cli.js b/src/accounts-cli.js index 551f151..0c094bb 100644 --- a/src/accounts-cli.js +++ b/src/accounts-cli.js @@ -18,7 +18,8 @@ import { stdin, stdout } from 'process'; import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; import { dirname } from 'path'; import { exec } from 'child_process'; -import { ACCOUNT_CONFIG_PATH } from './constants.js'; +import net from 'net'; +import { ACCOUNT_CONFIG_PATH, DEFAULT_PORT } from './constants.js'; import { getAuthorizationUrl, startCallbackServer, @@ -28,6 +29,51 @@ import { } from './oauth.js'; const MAX_ACCOUNTS = 10; +const SERVER_PORT = process.env.PORT || DEFAULT_PORT; + +/** + * Check if the Antigravity Proxy server is running + * Returns true if port is occupied + */ +function isServerRunning() { + return new Promise((resolve) => { + const socket = new net.Socket(); + socket.setTimeout(1000); + + socket.on('connect', () => { + socket.destroy(); + resolve(true); // Server is running + }); + + socket.on('timeout', () => { + socket.destroy(); + resolve(false); + }); + + socket.on('error', (err) => { + socket.destroy(); + resolve(false); // Port free + }); + + socket.connect(SERVER_PORT, 'localhost'); + }); +} + +/** + * Enforce that server is stopped before proceeding + */ +async function ensureServerStopped() { + const isRunning = await isServerRunning(); + if (isRunning) { + console.error(` +\x1b[31mError: Antigravity Proxy server is currently running on port ${SERVER_PORT}.\x1b[0m + +Please stop the server (Ctrl+C) before adding or managing accounts. +This ensures that your account changes are loaded correctly when you restart the server. +`); + process.exit(1); + } +} /** * Create readline interface @@ -183,7 +229,51 @@ async function addAccount(existingAccounts) { } /** - * Interactive add accounts flow + * Interactive remove accounts flow + */ +async function interactiveRemove(rl) { + while (true) { + const accounts = loadAccounts(); + if (accounts.length === 0) { + console.log('\nNo accounts to remove.'); + return; + } + + displayAccounts(accounts); + console.log('\nEnter account number to remove (or 0 to cancel)'); + + const answer = await rl.question('> '); + const index = parseInt(answer, 10); + + if (isNaN(index) || index < 0 || index > accounts.length) { + console.log('\n❌ Invalid selection.'); + continue; + } + + if (index === 0) { + return; // Exit + } + + const removed = accounts[index - 1]; // 1-based to 0-based + const confirm = await rl.question(`\nAre you sure you want to remove ${removed.email}? [y/N]: `); + + if (confirm.toLowerCase() === 'y') { + accounts.splice(index - 1, 1); + saveAccounts(accounts); + console.log(`\n✓ Removed ${removed.email}`); + } else { + console.log('\nCancelled.'); + } + + const removeMore = await rl.question('\nRemove another account? [y/N]: '); + if (removeMore.toLowerCase() !== 'y') { + break; + } + } +} + +/** + * Interactive add accounts flow (Main Menu) */ async function interactiveAdd(rl) { const accounts = loadAccounts(); @@ -191,13 +281,19 @@ async function interactiveAdd(rl) { if (accounts.length > 0) { displayAccounts(accounts); - const choice = await rl.question('\n(a)dd new account(s) or (f)resh start? [a/f]: '); + const choice = await rl.question('\n(a)dd new, (r)emove existing, or (f)resh start? [a/r/f]: '); + const c = choice.toLowerCase(); - if (choice.toLowerCase() === 'f') { + if (c === 'r') { + await interactiveRemove(rl); + return; // Return to main or exit? Given this is "add", we probably exit after sub-task. + } else if (c === 'f') { console.log('\nStarting fresh - existing accounts will be replaced.'); accounts.length = 0; - } else { + } else if (c === 'a') { console.log('\nAdding to existing accounts.'); + } else { + console.log('\nInvalid choice, defaulting to add.'); } } @@ -305,12 +401,14 @@ async function main() { try { switch (command) { case 'add': + await ensureServerStopped(); await interactiveAdd(rl); break; case 'list': await listAccounts(); break; case 'clear': + await ensureServerStopped(); await clearAccounts(rl); break; case 'verify': @@ -324,6 +422,10 @@ async function main() { console.log(' node src/accounts-cli.js clear Remove all accounts'); console.log(' node src/accounts-cli.js help Show this help'); break; + case 'remove': + await ensureServerStopped(); + await interactiveRemove(rl); + break; default: console.log(`Unknown command: ${command}`); console.log('Run with "help" for usage information.');