Show full dashboard room content

This commit is contained in:
ejclaw
2026-04-28 19:53:00 +09:00
parent f7f4888f92
commit 38ba5b87fa
5 changed files with 57 additions and 28 deletions

View File

@@ -208,4 +208,42 @@ describe('RoomCardV2', () => {
); );
expect(html).toContain('bar-chart-label-fit-playwright.png'); expect(html).toContain('bar-chart-label-fit-playwright.png');
}); });
it('renders expanded watcher messages without truncating content', () => {
const watcherTail = 'WATCHER_TAIL_VISIBLE';
const html = renderToStaticMarkup(
createElement(RoomCardV2, {
activity: activity({
messages: [
{
id: 'watcher-1',
sender: 'bot-1',
senderName: 'reviewer',
content: `[Watcher] ${'검증 로그 '.repeat(40)} ${watcherTail}`,
timestamp: '2026-04-28T02:00:00.000Z',
isFromMe: false,
isBotMessage: true,
sourceKind: 'bot',
},
],
}),
activityLoading: false,
busy: false,
draft: '',
entry,
expanded: true,
inboxItems: [],
locale: 'ko',
onDraftChange: () => {},
onSendMessage: () => {},
onToggle: () => {},
pinned: true,
t,
...formatters,
}),
);
expect(html).toContain('class="room-watcher-fold"');
expect(html).toContain(watcherTail);
});
}); });

View File

@@ -870,7 +870,7 @@ function RoomWatcherFold({
</strong> </strong>
<time>{formatDate(message.timestamp, locale)}</time> <time>{formatDate(message.timestamp, locale)}</time>
</header> </header>
<ParsedBody text={message.content} truncate={200} /> <ParsedBody text={message.content} />
</li> </li>
))} ))}
</ul> </ul>

View File

@@ -249,6 +249,7 @@ body:has(.view-stack.view-rooms) .error-card {
background: var(--panel); background: var(--panel);
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
scroll-padding-bottom: 96px;
} }
.view-stack.view-rooms .rooms-detail .room-card-v2.is-pinned { .view-stack.view-rooms .rooms-detail .room-card-v2.is-pinned {
@@ -290,7 +291,7 @@ body:has(.view-stack.view-rooms) .error-card {
} }
.view-stack.view-rooms .rooms-detail .room-thread-section { .view-stack.view-rooms .rooms-detail .room-thread-section {
padding: 4px 12px 4px 0; padding: 4px 12px 96px 0;
} }
.view-stack.view-rooms .rooms-detail .room-card-head { .view-stack.view-rooms .rooms-detail .room-card-head {

View File

@@ -238,6 +238,8 @@ describe('web dashboard data', () => {
}); });
it('builds redacted room activity from messages and paired turn output', () => { it('builds redacted room activity from messages and paired turn output', () => {
const longMessageTail = 'TAIL_MESSAGE_VISIBLE';
const longOutputTail = 'TAIL_OUTPUT_VISIBLE';
const task = makePairedTask({ const task = makePairedTask({
id: 'paired-room-1', id: 'paired-room-1',
chat_jid: 'dc:ops', chat_jid: 'dc:ops',
@@ -281,21 +283,12 @@ describe('web dashboard data', () => {
last_error: 'OPENAI_API_KEY=plain-secret-value', last_error: 'OPENAI_API_KEY=plain-secret-value',
}; };
const outputs: PairedTurnOutput[] = [ const outputs: PairedTurnOutput[] = [
{
id: 1,
task_id: task.id,
turn_number: 1,
role: 'owner',
output_text: 'owner output',
verdict: 'step_done',
created_at: '2026-04-26T05:25:00.000Z',
},
{ {
id: 2, id: 2,
task_id: task.id, task_id: task.id,
turn_number: 2, turn_number: 2,
role: 'reviewer', role: 'reviewer',
output_text: 'reviewer output with BOT_TOKEN=plain-secret-value', output_text: `reviewer output with BOT_TOKEN=plain-secret-value ${'x'.repeat(1900)} ${longOutputTail}`,
verdict: null, verdict: null,
created_at: '2026-04-26T05:30:00.000Z', created_at: '2026-04-26T05:30:00.000Z',
}, },
@@ -306,7 +299,7 @@ describe('web dashboard data', () => {
chat_jid: 'dc:ops', chat_jid: 'dc:ops',
sender: 'user-1', sender: 'user-1',
sender_name: '눈쟁이', sender_name: '눈쟁이',
content: 'latest message', content: `latest message ${'y'.repeat(950)} ${longMessageTail}`,
timestamp: '2026-04-26T05:29:00.000Z', timestamp: '2026-04-26T05:29:00.000Z',
is_from_me: false, is_from_me: false,
is_bot_message: false, is_bot_message: false,
@@ -347,12 +340,17 @@ describe('web dashboard data', () => {
{ {
turnNumber: 2, turnNumber: 2,
role: 'reviewer', role: 'reviewer',
outputText: 'reviewer output with BOT_TOKEN=<redacted>', outputText: expect.stringMatching(
new RegExp(`BOT_TOKEN=<redacted>[\\s\\S]*${longOutputTail}`),
),
}, },
], ],
}); });
expect(activity.messages).toMatchObject([ expect(activity.messages).toMatchObject([
{ senderName: '눈쟁이', content: 'latest message' }, {
senderName: '눈쟁이',
content: expect.stringContaining(longMessageTail),
},
]); ]);
}); });

View File

@@ -163,8 +163,6 @@ const SECRET_ASSIGNMENT_RE =
/\b([A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|AUTH|PRIVATE_KEY)[A-Z0-9_]*)\s*=\s*([^\s"'`]+)/gi; /\b([A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|AUTH|PRIVATE_KEY)[A-Z0-9_]*)\s*=\s*([^\s"'`]+)/gi;
const SECRET_VALUE_RE = const SECRET_VALUE_RE =
/\b(?:sk-[A-Za-z0-9_-]{12,}|gh[pousr]_[A-Za-z0-9_]{12,}|xox[baprs]-[A-Za-z0-9-]{12,}|eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{10,})\b/g; /\b(?:sk-[A-Za-z0-9_-]{12,}|gh[pousr]_[A-Za-z0-9_]{12,}|xox[baprs]-[A-Za-z0-9-]{12,}|eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{10,})\b/g;
const ROOM_MESSAGE_PREVIEW_MAX_LENGTH = 900;
const ROOM_OUTPUT_PREVIEW_MAX_LENGTH = 1800;
function redactSensitiveText(value: string): string { function redactSensitiveText(value: string): string {
return value return value
@@ -185,8 +183,8 @@ function buildInboxPreview(value: string): string {
return truncateText(redactSensitiveText(value).replace(/\s+/g, ' ').trim()); return truncateText(redactSensitiveText(value).replace(/\s+/g, ' ').trim());
} }
function buildRoomPreview(value: string, maxLength: number): string { function buildRoomBody(value: string): string {
return truncateText(redactSensitiveText(value).trim(), maxLength); return redactSensitiveText(value).trim();
} }
function decodeCommonHtmlEntities(value: string): string { function decodeCommonHtmlEntities(value: string): string {
@@ -485,7 +483,7 @@ function sanitizeRoomMessage(
id: message.id, id: message.id,
sender: message.sender, sender: message.sender,
senderName: message.sender_name || message.sender, senderName: message.sender_name || message.sender,
content: buildRoomPreview(normalized.text, ROOM_MESSAGE_PREVIEW_MAX_LENGTH), content: buildRoomBody(normalized.text),
attachments: normalized.attachments, attachments: normalized.attachments,
timestamp: message.timestamp, timestamp: message.timestamp,
isFromMe: !!message.is_from_me, isFromMe: !!message.is_from_me,
@@ -576,10 +574,7 @@ function sanitizeRoomTurn(
completedAt, completedAt,
lastError: lastError:
(attempt?.last_error ?? turn.last_error) (attempt?.last_error ?? turn.last_error)
? buildRoomPreview( ? buildRoomBody(attempt?.last_error ?? turn.last_error ?? '')
attempt?.last_error ?? turn.last_error ?? '',
ROOM_MESSAGE_PREVIEW_MAX_LENGTH,
)
: null, : null,
progressText, progressText,
progressUpdatedAt, progressUpdatedAt,
@@ -598,10 +593,7 @@ function sanitizeRoomTurnOutput(
role: output.role, role: output.role,
verdict: output.verdict ?? null, verdict: output.verdict ?? null,
createdAt: output.created_at, createdAt: output.created_at,
outputText: buildRoomPreview( outputText: buildRoomBody(normalized.text),
normalized.text,
ROOM_OUTPUT_PREVIEW_MAX_LENGTH,
),
attachments: normalized.attachments, attachments: normalized.attachments,
}; };
} }