mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 02:11:28 +00:00
fix(auth): keep resume lane warning non-blocking
- extract account resume lane guidance into a dedicated auth helper - swallow diagnostic failures so account launches still reach execClaude - add focused tests for the warning success and failure paths
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import * as path from 'path';
|
||||
import { info, warn } from '../utils/ui';
|
||||
import {
|
||||
parseResumeFlagIntent,
|
||||
resolveRuntimePlainCcsResumeLane,
|
||||
type ResumeLaneSummary,
|
||||
} from './resume-lane-diagnostics';
|
||||
|
||||
interface ResumeLaneWarningDependencies {
|
||||
resolvePlainLane?: () => Promise<ResumeLaneSummary>;
|
||||
log?: (message: string) => void;
|
||||
debug?: boolean;
|
||||
}
|
||||
|
||||
export async function maybeWarnAboutResumeLaneMismatch(
|
||||
profileName: string,
|
||||
accountConfigDir: string,
|
||||
args: string[],
|
||||
deps: ResumeLaneWarningDependencies = {}
|
||||
): Promise<void> {
|
||||
const resumeIntent = parseResumeFlagIntent(args);
|
||||
if (!resumeIntent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const log = deps.log ?? console.error;
|
||||
|
||||
try {
|
||||
const plainLane = await (deps.resolvePlainLane ?? resolveRuntimePlainCcsResumeLane)();
|
||||
if (path.resolve(plainLane.configDir) === path.resolve(accountConfigDir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
log(
|
||||
warn(
|
||||
`Resume for account "${profileName}" will search that account lane, not the current plain ccs lane.`
|
||||
)
|
||||
);
|
||||
log(info(` Account lane: ${accountConfigDir}`));
|
||||
log(info(` Plain ccs lane: ${plainLane.label} (${plainLane.configDir})`));
|
||||
if (resumeIntent.explicitSessionId) {
|
||||
log(
|
||||
info(
|
||||
' This explicit session ID may have been created in a different lane, so Claude may not find it here.'
|
||||
)
|
||||
);
|
||||
}
|
||||
log(info(' Recover the original lane first: ccs -r'));
|
||||
log(info(' Back it up before changing setup: ccs auth backup default'));
|
||||
log(
|
||||
info(` For future work, align plain ccs with this account: ccs auth default ${profileName}`)
|
||||
);
|
||||
log('');
|
||||
} catch (error) {
|
||||
log(
|
||||
warn(
|
||||
'Resume lane guidance skipped because diagnostics failed; continuing with the account lane.'
|
||||
)
|
||||
);
|
||||
if (deps.debug ?? Boolean(process.env.CCS_DEBUG)) {
|
||||
log(info(` Diagnostic error: ${(error as Error).message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-42
@@ -1,7 +1,6 @@
|
||||
import './utils/fetch-proxy-setup';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { detectClaudeCli } from './utils/claude-detector';
|
||||
import {
|
||||
getSettingsPath,
|
||||
@@ -65,10 +64,7 @@ import { tryHandleRootCommand } from './commands/root-command-router';
|
||||
// Import extracted utility functions
|
||||
import { execClaude } from './utils/shell-executor';
|
||||
import { isDeprecatedGlmtProfileName, normalizeDeprecatedGlmtEnv } from './utils/glmt-deprecation';
|
||||
import {
|
||||
parseResumeFlagIntent,
|
||||
resolveRuntimePlainCcsResumeLane,
|
||||
} from './auth/resume-lane-diagnostics';
|
||||
import { maybeWarnAboutResumeLaneMismatch } from './auth/resume-lane-warning';
|
||||
|
||||
// Import target adapter system
|
||||
import {
|
||||
@@ -264,43 +260,6 @@ function shouldPassthroughNativeCodexFlagCommand(args: string[]): boolean {
|
||||
return getNativeCodexPassthroughArgs(args) !== null;
|
||||
}
|
||||
|
||||
async function maybeWarnAboutResumeLaneMismatch(
|
||||
profileName: string,
|
||||
accountConfigDir: string,
|
||||
args: string[]
|
||||
): Promise<void> {
|
||||
const resumeIntent = parseResumeFlagIntent(args);
|
||||
if (!resumeIntent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const plainLane = await resolveRuntimePlainCcsResumeLane();
|
||||
if (path.resolve(plainLane.configDir) === path.resolve(accountConfigDir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(
|
||||
warn(
|
||||
`Resume for account "${profileName}" will search that account lane, not the current plain ccs lane.`
|
||||
)
|
||||
);
|
||||
console.error(info(` Account lane: ${accountConfigDir}`));
|
||||
console.error(info(` Plain ccs lane: ${plainLane.label} (${plainLane.configDir})`));
|
||||
if (resumeIntent.explicitSessionId) {
|
||||
console.error(
|
||||
info(
|
||||
` This explicit session ID may have been created in a different lane, so Claude may not find it here.`
|
||||
)
|
||||
);
|
||||
}
|
||||
console.error(info(` Recover the original lane first: ccs -r`));
|
||||
console.error(info(` Back it up before changing setup: ccs auth backup default`));
|
||||
console.error(
|
||||
info(` For future work, align plain ccs with this account: ccs auth default ${profileName}`)
|
||||
);
|
||||
console.error('');
|
||||
}
|
||||
|
||||
function getNativeCodexPassthroughArgs(args: string[]): string[] | null {
|
||||
const targetArgs = stripTargetFlag(args);
|
||||
if (resolveTargetType(args) !== 'codex' || targetArgs.length === 0) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { maybeWarnAboutResumeLaneMismatch } from '../../../src/auth/resume-lane-warning';
|
||||
|
||||
describe('resume lane warning', () => {
|
||||
it('prints guidance when the plain ccs lane differs from the account lane', async () => {
|
||||
const logs: string[] = [];
|
||||
|
||||
await maybeWarnAboutResumeLaneMismatch('work', '/tmp/account-lane', ['--resume'], {
|
||||
log: (message) => logs.push(message),
|
||||
resolvePlainLane: async () => ({
|
||||
kind: 'native',
|
||||
label: 'native Claude lane',
|
||||
configDir: '/tmp/native-lane',
|
||||
projectCount: 12,
|
||||
}),
|
||||
});
|
||||
|
||||
expect(logs[0]).toContain('Resume for account "work" will search that account lane');
|
||||
expect(logs).toContain('[i] Account lane: /tmp/account-lane');
|
||||
expect(logs).toContain('[i] Plain ccs lane: native Claude lane (/tmp/native-lane)');
|
||||
expect(logs).toContain('[i] Recover the original lane first: ccs -r');
|
||||
});
|
||||
|
||||
it('does not log anything when resume is not requested', async () => {
|
||||
const logs: string[] = [];
|
||||
|
||||
await maybeWarnAboutResumeLaneMismatch('work', '/tmp/account-lane', ['hello'], {
|
||||
log: (message) => logs.push(message),
|
||||
resolvePlainLane: async () => {
|
||||
throw new Error('should not be called');
|
||||
},
|
||||
});
|
||||
|
||||
expect(logs).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('swallows diagnostic failures and keeps the warning path non-fatal', async () => {
|
||||
const logs: string[] = [];
|
||||
|
||||
await expect(
|
||||
maybeWarnAboutResumeLaneMismatch('work', '/tmp/account-lane', ['-r'], {
|
||||
debug: true,
|
||||
log: (message) => logs.push(message),
|
||||
resolvePlainLane: async () => {
|
||||
throw new Error('broken config');
|
||||
},
|
||||
})
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
expect(logs[0]).toContain('Resume lane guidance skipped because diagnostics failed');
|
||||
expect(logs[1]).toContain('Diagnostic error: broken config');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user