add ability to remove accounts. ensure accounts are not / added / removed when server is running

This commit is contained in:
Badri Narayanan S
2025-12-21 15:05:45 +05:30
parent 95c08f9d55
commit 9a35845ceb
2 changed files with 108 additions and 5 deletions

View File

@@ -10,6 +10,7 @@
"accounts": "node src/accounts-cli.js", "accounts": "node src/accounts-cli.js",
"accounts:add": "node src/accounts-cli.js add", "accounts:add": "node src/accounts-cli.js add",
"accounts:list": "node src/accounts-cli.js list", "accounts:list": "node src/accounts-cli.js list",
"accounts:remove": "node src/accounts-cli.js remove",
"accounts:verify": "node src/accounts-cli.js verify", "accounts:verify": "node src/accounts-cli.js verify",
"test": "node tests/run-all.cjs", "test": "node tests/run-all.cjs",
"test:signatures": "node tests/test-thinking-signatures.cjs", "test:signatures": "node tests/test-thinking-signatures.cjs",

View File

@@ -18,7 +18,8 @@ import { stdin, stdout } from 'process';
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
import { dirname } from 'path'; import { dirname } from 'path';
import { exec } from 'child_process'; 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 { import {
getAuthorizationUrl, getAuthorizationUrl,
startCallbackServer, startCallbackServer,
@@ -28,6 +29,51 @@ import {
} from './oauth.js'; } from './oauth.js';
const MAX_ACCOUNTS = 10; 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 * 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) { async function interactiveAdd(rl) {
const accounts = loadAccounts(); const accounts = loadAccounts();
@@ -191,13 +281,19 @@ async function interactiveAdd(rl) {
if (accounts.length > 0) { if (accounts.length > 0) {
displayAccounts(accounts); 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.'); console.log('\nStarting fresh - existing accounts will be replaced.');
accounts.length = 0; accounts.length = 0;
} else { } else if (c === 'a') {
console.log('\nAdding to existing accounts.'); console.log('\nAdding to existing accounts.');
} else {
console.log('\nInvalid choice, defaulting to add.');
} }
} }
@@ -305,12 +401,14 @@ async function main() {
try { try {
switch (command) { switch (command) {
case 'add': case 'add':
await ensureServerStopped();
await interactiveAdd(rl); await interactiveAdd(rl);
break; break;
case 'list': case 'list':
await listAccounts(); await listAccounts();
break; break;
case 'clear': case 'clear':
await ensureServerStopped();
await clearAccounts(rl); await clearAccounts(rl);
break; break;
case 'verify': 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 clear Remove all accounts');
console.log(' node src/accounts-cli.js help Show this help'); console.log(' node src/accounts-cli.js help Show this help');
break; break;
case 'remove':
await ensureServerStopped();
await interactiveRemove(rl);
break;
default: default:
console.log(`Unknown command: ${command}`); console.log(`Unknown command: ${command}`);
console.log('Run with "help" for usage information.'); console.log('Run with "help" for usage information.');