fix: address CodeRabbit Major findings on install.ts

- Cancel of API-key / Gateway-URL prompts no longer wipes existing
  credentials by switching to subscription auth and emptying
  ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN. Cancel
  now leaves the prior config untouched.
- Empty gateway-token input preserves the existing token instead of
  clearing it. The new prompt copy explains that blank keeps the
  current token.
- Interactive install no longer hard-locks to Claude when
  --provider is unset. Prompt now asks for provider
  (claude/gemini/openrouter) up front, then runs the Claude auth flow
  only when the user picks Claude.
- Claude auth-mode prompt now seeds initialValue from the stored
  CLAUDE_MEM_CLAUDE_AUTH_METHOD setting, so reruns honor existing
  configuration instead of always defaulting to subscription.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2026-05-04 20:51:00 -07:00
parent 0cc45c6e7f
commit 7f3686fd2c
+50 -22
View File
@@ -634,8 +634,7 @@ async function promptProvider(options: InstallOptions): Promise<ProviderId> {
}); });
if (p.isCancel(apiKeyResult)) { if (p.isCancel(apiKeyResult)) {
log.warn('API key prompt cancelled — using your logged-in Claude SDK account.'); log.warn('API key prompt cancelled — leaving existing configuration untouched.');
useSubscriptionAuth();
return; return;
} }
@@ -667,27 +666,31 @@ async function promptProvider(options: InstallOptions): Promise<ProviderId> {
}); });
if (p.isCancel(baseUrlResult)) { if (p.isCancel(baseUrlResult)) {
log.warn('Gateway setup cancelled — using your logged-in Claude SDK account.'); log.warn('Gateway setup cancelled — leaving existing configuration untouched.');
useSubscriptionAuth();
return; return;
} }
const tokenResult = await p.password({ const tokenResult = await p.password({
message: 'Gateway key/token (leave blank if your local gateway does not require one):', message: 'Gateway key/token (leave blank to keep current token, or type a new one):',
mask: '*', mask: '*',
}); });
if (p.isCancel(tokenResult)) { const tokenCancelled = p.isCancel(tokenResult);
log.warn('Gateway key prompt cancelled — continuing without a gateway token.'); const tokenInput = tokenCancelled ? '' : String(tokenResult).trim();
} const env: Record<string, string> = {
saveClaudeMemEnv({
ANTHROPIC_API_KEY: '', ANTHROPIC_API_KEY: '',
ANTHROPIC_BASE_URL: String(baseUrlResult).trim(), ANTHROPIC_BASE_URL: String(baseUrlResult).trim(),
ANTHROPIC_AUTH_TOKEN: p.isCancel(tokenResult) ? '' : String(tokenResult).trim(), };
}); if (!tokenCancelled && tokenInput.length > 0) {
env.ANTHROPIC_AUTH_TOKEN = tokenInput;
}
saveClaudeMemEnv(env);
persistClaudeProvider('gateway'); persistClaudeProvider('gateway');
log.info('Configured Claude Agent SDK gateway in ~/.claude-mem/.env.'); if (tokenCancelled || tokenInput.length === 0) {
log.info('Gateway URL saved; existing gateway token preserved.');
} else {
log.info('Configured Claude Agent SDK gateway in ~/.claude-mem/.env.');
}
}; };
if (!isInteractive) { if (!isInteractive) {
@@ -704,17 +707,23 @@ async function promptProvider(options: InstallOptions): Promise<ProviderId> {
return initialProvider; return initialProvider;
} }
let selectedProvider: ProviderId; const runClaudeAuthFlow = async (): Promise<void> => {
if (options.provider) { const storedAuthMethod = getSetting('CLAUDE_MEM_CLAUDE_AUTH_METHOD') as
selectedProvider = options.provider; | 'subscription'
} else { | 'api-key'
| 'gateway'
| undefined;
const initialAccessMode: ClaudeAccessMode = storedAuthMethod === 'subscription' || !storedAuthMethod
? 'subscription'
: 'api-key';
const result = await p.select<ClaudeAccessMode>({ const result = await p.select<ClaudeAccessMode>({
message: 'Do you use a subscription plan or an API key/gateway for the memory agent?', message: 'Do you use a subscription plan or an API key/gateway for the memory agent?',
options: [ options: [
{ value: 'subscription', label: 'Subscription plan (recommended — uses your logged-in Claude SDK account)' }, { value: 'subscription', label: 'Subscription plan (recommended — uses your logged-in Claude SDK account)' },
{ value: 'api-key', label: 'API key or gateway (Anthropic, LiteLLM, or compatible proxy)' }, { value: 'api-key', label: 'API key or gateway (Anthropic, LiteLLM, or compatible proxy)' },
], ],
initialValue: initialProvider === 'claude' ? 'subscription' : 'api-key', initialValue: initialAccessMode,
}); });
if (p.isCancel(result)) { if (p.isCancel(result)) {
@@ -723,7 +732,7 @@ async function promptProvider(options: InstallOptions): Promise<ProviderId> {
} }
if (result === 'subscription') { if (result === 'subscription') {
useSubscriptionAuth(); useSubscriptionAuth();
return 'claude'; return;
} }
const apiModeResult = await p.select<ClaudeApiMode>({ const apiModeResult = await p.select<ClaudeApiMode>({
@@ -732,7 +741,7 @@ async function promptProvider(options: InstallOptions): Promise<ProviderId> {
{ value: 'direct', label: 'Anthropic API key' }, { value: 'direct', label: 'Anthropic API key' },
{ value: 'gateway', label: 'LiteLLM or custom gateway' }, { value: 'gateway', label: 'LiteLLM or custom gateway' },
], ],
initialValue: loadClaudeMemEnv().ANTHROPIC_BASE_URL ? 'gateway' : 'direct', initialValue: storedAuthMethod === 'gateway' || loadClaudeMemEnv().ANTHROPIC_BASE_URL ? 'gateway' : 'direct',
}); });
if (p.isCancel(apiModeResult)) { if (p.isCancel(apiModeResult)) {
@@ -745,11 +754,30 @@ async function promptProvider(options: InstallOptions): Promise<ProviderId> {
} else { } else {
await configureDirectApiKey(); await configureDirectApiKey();
} }
return 'claude'; };
let selectedProvider: ProviderId;
if (options.provider) {
selectedProvider = options.provider;
} else {
const providerResult = await p.select<ProviderId>({
message: 'Which memory provider do you want to use?',
options: [
{ value: 'claude', label: 'Claude Agent SDK (recommended)' },
{ value: 'gemini', label: 'Gemini' },
{ value: 'openrouter', label: 'OpenRouter' },
],
initialValue: initialProvider,
});
if (p.isCancel(providerResult)) {
p.cancel('Installation cancelled.');
process.exit(0);
}
selectedProvider = providerResult;
} }
if (selectedProvider === 'claude') { if (selectedProvider === 'claude') {
persistClaudeProvider(); await runClaudeAuthFlow();
return 'claude'; return 'claude';
} }