fix(logs): restore member process log source
This commit is contained in:
parent
5513531053
commit
7e0520cb4c
2 changed files with 72 additions and 26 deletions
|
|
@ -1,13 +1,15 @@
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { useAppTranslation } from '@features/localization/renderer';
|
import { useAppTranslation } from '@features/localization/renderer';
|
||||||
|
import { api } from '@renderer/api';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { selectResolvedMembersForTeamName } from '@renderer/store/slices/teamSlice';
|
import { selectResolvedMembersForTeamName } from '@renderer/store/slices/teamSlice';
|
||||||
|
|
||||||
import { useMemberLogStream } from '../hooks/useMemberLogStream';
|
import { useMemberLogStream } from '../hooks/useMemberLogStream';
|
||||||
import { ExecutionLogStreamView } from '../ui/ExecutionLogStreamView';
|
import { ExecutionLogStreamView } from '../ui/ExecutionLogStreamView';
|
||||||
|
import { MemberRuntimeProcessLogsPanel } from '../ui/MemberRuntimeProcessLogsPanel';
|
||||||
|
|
||||||
import type { MemberLogStreamSegment } from '../../contracts';
|
import type { MemberLogStreamSegment, MemberRuntimeLogKind } from '../../contracts';
|
||||||
import type { ResolvedTeamMember } from '@shared/types';
|
import type { ResolvedTeamMember } from '@shared/types';
|
||||||
|
|
||||||
interface MemberLogStreamSectionProps {
|
interface MemberLogStreamSectionProps {
|
||||||
|
|
@ -17,6 +19,10 @@ interface MemberLogStreamSectionProps {
|
||||||
onInitialLoadErrorChange?: (hasError: boolean) => void;
|
onInitialLoadErrorChange?: (hasError: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function describeMemberStream(): string {
|
||||||
|
return 'Member-scoped transcript and runtime logs rendered with the same execution-log components used in Task Log Stream.';
|
||||||
|
}
|
||||||
|
|
||||||
function getSegmentMetaLabel(segment: MemberLogStreamSegment): string {
|
function getSegmentMetaLabel(segment: MemberLogStreamSegment): string {
|
||||||
const details = [segment.source.label];
|
const details = [segment.source.label];
|
||||||
if (segment.source.laneId) {
|
if (segment.source.laneId) {
|
||||||
|
|
@ -39,8 +45,17 @@ export const MemberLogStreamSection = ({
|
||||||
onInitialLoadErrorChange,
|
onInitialLoadErrorChange,
|
||||||
}: Readonly<MemberLogStreamSectionProps>): React.JSX.Element => {
|
}: Readonly<MemberLogStreamSectionProps>): React.JSX.Element => {
|
||||||
const { t } = useAppTranslation('team');
|
const { t } = useAppTranslation('team');
|
||||||
|
const [selectedLogView, setSelectedLogView] = useState<'execution' | 'process'>('execution');
|
||||||
const teamMembers = useStore((s) => selectResolvedMembersForTeamName(s, teamName));
|
const teamMembers = useStore((s) => selectResolvedMembersForTeamName(s, teamName));
|
||||||
const { stream, loading, error } = useMemberLogStream({ teamName, member, enabled });
|
const { stream, loading, error } = useMemberLogStream({ teamName, member, enabled });
|
||||||
|
const loadRuntimeLogTail = useCallback(
|
||||||
|
(input: {
|
||||||
|
readonly kind: MemberRuntimeLogKind;
|
||||||
|
readonly maxBytes: number;
|
||||||
|
readonly forceRefresh?: boolean;
|
||||||
|
}) => api.memberLogStream.getMemberRuntimeLogTail(teamName, member.name, input),
|
||||||
|
[member.name, teamName]
|
||||||
|
);
|
||||||
const hasInitialLoadError = Boolean(error && !stream && !loading);
|
const hasInitialLoadError = Boolean(error && !stream && !loading);
|
||||||
const boundedHistoryNote = useMemo(() => {
|
const boundedHistoryNote = useMemo(() => {
|
||||||
if (!stream) return null;
|
if (!stream) return null;
|
||||||
|
|
@ -55,24 +70,57 @@ export const MemberLogStreamSection = ({
|
||||||
}, [hasInitialLoadError, onInitialLoadErrorChange]);
|
}, [hasInitialLoadError, onInitialLoadErrorChange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExecutionLogStreamView
|
<div className="space-y-3">
|
||||||
title=""
|
<div className="inline-flex rounded-md bg-[var(--color-surface-subtle)] p-0.5">
|
||||||
description=""
|
<button
|
||||||
stream={stream}
|
type="button"
|
||||||
loading={loading}
|
className={`rounded px-2.5 py-1 text-xs font-medium transition-colors ${
|
||||||
error={error}
|
selectedLogView === 'execution'
|
||||||
teamName={teamName}
|
? 'bg-[var(--color-surface)] text-[var(--color-text)] shadow-sm'
|
||||||
teamMembers={teamMembers}
|
: 'text-[var(--color-text-muted)] hover:text-[var(--color-text)]'
|
||||||
loadingText={t('memberLogStream.logs.loading')}
|
}`}
|
||||||
emptyTitle={t('memberLogStream.logs.emptyTitle')}
|
onClick={() => setSelectedLogView('execution')}
|
||||||
emptyDescription={t('memberLogStream.logs.emptyDescription')}
|
>
|
||||||
selectionResetKey={`${teamName}:${member.name}`}
|
{t('memberLogStream.tabs.execution')}
|
||||||
boundedHistoryNote={boundedHistoryNote}
|
</button>
|
||||||
forceSegmentHeaders
|
<button
|
||||||
showIntro={false}
|
type="button"
|
||||||
showSegmentParticipantBadge={false}
|
className={`rounded px-2.5 py-1 text-xs font-medium transition-colors ${
|
||||||
buildSegmentRenderKey={buildMemberSegmentRenderKey}
|
selectedLogView === 'process'
|
||||||
getSegmentMetaLabel={getSegmentMetaLabel}
|
? 'bg-[var(--color-surface)] text-[var(--color-text)] shadow-sm'
|
||||||
/>
|
: 'text-[var(--color-text-muted)] hover:text-[var(--color-text)]'
|
||||||
|
}`}
|
||||||
|
onClick={() => setSelectedLogView('process')}
|
||||||
|
>
|
||||||
|
{t('memberLogStream.tabs.process')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selectedLogView === 'execution' ? (
|
||||||
|
<ExecutionLogStreamView
|
||||||
|
title={t('memberLogStream.logs.title')}
|
||||||
|
description={describeMemberStream()}
|
||||||
|
stream={stream}
|
||||||
|
loading={loading}
|
||||||
|
error={error}
|
||||||
|
teamName={teamName}
|
||||||
|
teamMembers={teamMembers}
|
||||||
|
loadingText={t('memberLogStream.logs.loading')}
|
||||||
|
emptyTitle={t('memberLogStream.logs.emptyTitle')}
|
||||||
|
emptyDescription={t('memberLogStream.logs.emptyDescription')}
|
||||||
|
selectionResetKey={`${teamName}:${member.name}`}
|
||||||
|
boundedHistoryNote={boundedHistoryNote}
|
||||||
|
forceSegmentHeaders
|
||||||
|
showSegmentParticipantBadge={false}
|
||||||
|
buildSegmentRenderKey={buildMemberSegmentRenderKey}
|
||||||
|
getSegmentMetaLabel={getSegmentMetaLabel}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<MemberRuntimeProcessLogsPanel
|
||||||
|
enabled={enabled && selectedLogView === 'process'}
|
||||||
|
loadRuntimeLogTail={loadRuntimeLogTail}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -733,11 +733,9 @@ describe('CLI status visibility during completed install state', () => {
|
||||||
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||||
storeState.cliInstallerState = 'idle';
|
storeState.cliInstallerState = 'idle';
|
||||||
storeState.codexRuntimeStatus = {
|
storeState.codexRuntimeStatus = {
|
||||||
installed: true,
|
installed: false,
|
||||||
source: 'path',
|
source: 'missing',
|
||||||
state: 'ready',
|
state: 'idle',
|
||||||
binaryPath: '/usr/local/bin/codex',
|
|
||||||
version: 'codex-cli 0.125.0',
|
|
||||||
};
|
};
|
||||||
storeState.cliStatus = createInstalledCliStatus({
|
storeState.cliStatus = createInstalledCliStatus({
|
||||||
flavor: 'agent_teams_orchestrator',
|
flavor: 'agent_teams_orchestrator',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue