feat: enhance project directory resolution and auto-refresh in MemberLogsTab

- Updated `TeamMemberLogsFinder` to improve project directory resolution by falling back to `leadSessionId` if the encoded directory does not exist, enhancing reliability in locating project logs.
- Refactored `MemberLogsTab` to implement an initial loading state and auto-refresh functionality for ongoing tasks, improving user experience during log retrieval.
- Adjusted error handling and loading indicators to provide clearer feedback based on task status.
This commit is contained in:
iliya 2026-02-23 15:12:39 +02:00
parent c024f5bc78
commit d60ac09925
2 changed files with 58 additions and 9 deletions

View file

@ -257,9 +257,45 @@ export class TeamMemberLogsFinder {
}
const normalizedProjectPath = trimTrailingSlashes(config.projectPath);
const projectId = encodePath(normalizedProjectPath);
const baseDir = extractBaseDir(projectId);
const projectDir = path.join(getProjectsBasePath(), baseDir);
let projectId = encodePath(normalizedProjectPath);
let baseDir = extractBaseDir(projectId);
let projectDir = path.join(getProjectsBasePath(), baseDir);
// If the encoded directory doesn't exist (symlink/cwd mismatch), fall back to locating
// the project directory by leadSessionId which is unique and reliable.
try {
const stat = await fs.stat(projectDir);
if (!stat.isDirectory()) {
throw new Error('not a directory');
}
} catch {
const leadSessionId =
typeof config.leadSessionId === 'string' && config.leadSessionId.trim().length > 0
? config.leadSessionId.trim()
: null;
if (leadSessionId) {
const projectsBase = getProjectsBasePath();
try {
const entries = await fs.readdir(projectsBase, { withFileTypes: true });
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const candidateDir = path.join(projectsBase, entry.name);
const leadPath = path.join(candidateDir, `${leadSessionId}.jsonl`);
try {
await fs.access(leadPath);
projectDir = candidateDir;
projectId = entry.name;
baseDir = entry.name;
break;
} catch {
// not this project
}
}
} catch {
// ignore
}
}
}
const knownSessionIds = new Set<string>();
if (config.leadSessionId) {

View file

@ -41,15 +41,20 @@ export const MemberLogsTab = ({
useEffect(() => {
let cancelled = false;
setLoading(true);
setError(null);
let isInitial = true;
const shouldAutoRefresh = taskId != null && taskStatus === 'in_progress';
void (async () => {
const load = async (): Promise<void> => {
try {
if (taskId == null && !memberName) {
if (!cancelled) setLogs([]);
return;
}
if (isInitial) {
setLoading(true);
}
setError(null);
const result =
taskId != null
? await api.teams.getLogsForTask(teamName, taskId, {
@ -65,14 +70,20 @@ export const MemberLogsTab = ({
setError(e instanceof Error ? e.message : 'Unknown error');
}
} finally {
if (!cancelled) {
if (!cancelled && isInitial) {
setLoading(false);
}
isInitial = false;
}
})();
};
void load();
const interval = shouldAutoRefresh ? setInterval(() => void load(), 5000) : null;
return () => {
cancelled = true;
if (interval) clearInterval(interval);
};
}, [teamName, memberName, taskId, taskOwner, taskStatus]);
@ -133,7 +144,9 @@ export const MemberLogsTab = ({
No logs found
<p className="mt-1 text-[10px] opacity-60">
{taskId != null
? 'No session activity for this task yet'
? taskStatus === 'in_progress'
? 'Task is in progress — waiting for session activity (auto-refreshing)...'
: 'No session activity for this task yet'
: 'This member has no recorded session activity yet'}
</p>
</div>