import { useEffect, useState } from 'react';
import {
fetchRoomSkillSettings,
fetchRuntimeInventory,
updateRoomSkillSetting,
type RuntimeAgentInventory,
type RuntimeInventorySnapshot,
type RuntimePathSnapshot,
type RuntimeSkillDirSnapshot,
type RoomSkillCatalogItem,
type RoomSkillSettingUpdateInput,
type RoomSkillSettingsSnapshot,
} from './api';
import { SettingsSectionHeading } from './SettingsPanelChrome';
import './RuntimeInventorySettings.css';
function ExistsBadge({ exists }: { exists: boolean }) {
return (
{exists ? '감지됨' : '없음'}
);
}
function PathRow({ item }: { item: RuntimePathSnapshot }) {
return (
{item.label}
{item.path}
);
}
function SkillDirCard({ dir }: { dir: RuntimeSkillDirSnapshot }) {
const preview = dir.skills.slice(0, 6);
return (
{dir.label}
{dir.path}
{dir.count} skills
{preview.length === 0 ? (
표시할 SKILL.md 없음
) : (
{preview.map((skill) => (
-
{skill.name}
{skill.description ? {skill.description} : null}
))}
)}
{dir.count > preview.length ? (
외 {dir.count - preview.length}개 더 있음
) : null}
);
}
function AgentInventoryCard({
title,
inventory,
}: {
title: string;
inventory: RuntimeAgentInventory;
}) {
return (
{title}
MCP {inventory.mcp.ejclawConfigured ? '연결' : '미감지'}
{inventory.configFiles.map((item) => (
))}
MCP servers {inventory.mcp.serverCount}개 · EJClaw section{' '}
{inventory.mcp.ejclawConfigured ? '있음' : '없음'}
{inventory.skillDirs.map((dir) => (
))}
);
}
function agentLabel(agentType: 'claude-code' | 'codex') {
return agentType === 'codex' ? 'Codex' : 'Claude Code';
}
function RoomSkillPolicyCard({
onToggle,
savingKey,
snapshot,
}: {
onToggle: (input: RoomSkillSettingUpdateInput) => void;
savingKey: string | null;
snapshot: RoomSkillSettingsSnapshot | null;
}) {
if (!snapshot) {
return (
방별 스킬 정책을 불러오는 중…
);
}
const catalogById = new Map(
snapshot.catalog.map((skill) => [skill.id, skill]),
);
const roomPreview = snapshot.rooms.slice(0, 8);
return (
{roomPreview.length === 0 ? (
등록된 방이 없습니다.
) : (
{roomPreview.map((room) => (
{room.agents.map((agent) => {
const disabledNames = agent.disabledSkillIds
.map((id) => catalogById.get(id)?.displayName ?? id)
.slice(0, 3);
const enabledIds = new Set(agent.effectiveEnabledSkillIds);
return (
{agentLabel(agent.agentType)}
{agent.mode === 'all-enabled'
? '기본 전체 ON'
: `${agent.disabledSkillIds.length}개 OFF`}
사용 {agent.effectiveEnabledSkillIds.length} / 가능{' '}
{agent.availableSkillIds.length}
{disabledNames.length > 0 ? (
OFF: {disabledNames.join(', ')}
) : null}
{agent.availableSkillIds.map((skillId) => {
const skill = catalogById.get(skillId);
const key = `${room.jid}:${agent.agentType}:${skillId}`;
return (
);
})}
);
})}
))}
)}
{snapshot.rooms.length > roomPreview.length ? (
외 {snapshot.rooms.length - roomPreview.length}개 방 더 있음
) : null}
);
}
export function RuntimeInventorySettings() {
const [snapshot, setSnapshot] = useState(
null,
);
const [roomSkillSnapshot, setRoomSkillSnapshot] =
useState(null);
const [error, setError] = useState(null);
const [roomSkillError, setRoomSkillError] = useState(null);
const [savingRoomSkillKey, setSavingRoomSkillKey] = useState(
null,
);
useEffect(() => {
let cancelled = false;
Promise.all([fetchRuntimeInventory(), fetchRoomSkillSettings()])
.then(([value, roomSkills]) => {
if (cancelled) return;
setSnapshot(value);
setRoomSkillSnapshot(roomSkills);
setError(null);
})
.catch((err) => {
if (cancelled) return;
setError(err instanceof Error ? err.message : String(err));
});
return () => {
cancelled = true;
};
}, []);
async function handleRoomSkillToggle(input: RoomSkillSettingUpdateInput) {
const key = `${input.roomJid}:${input.agentType}:${input.skillId}`;
setSavingRoomSkillKey(key);
setRoomSkillError(null);
try {
const next = await updateRoomSkillSetting(input);
setRoomSkillSnapshot(next);
} catch (err) {
setRoomSkillError(err instanceof Error ? err.message : String(err));
} finally {
setSavingRoomSkillKey(null);
}
}
return (
{error ? {error}
: null}
{!snapshot ? (
런타임 정보를 불러오는 중…
) : (
Current service
{snapshot.service.id}
{snapshot.service.agentType} · session{' '}
{snapshot.service.sessionScope}
Project
{snapshot.projectRoot}
data {snapshot.dataDir}
{roomSkillError ? (
{roomSkillError}
) : null}
{
void handleRoomSkillToggle(input);
}}
savingKey={savingRoomSkillKey}
snapshot={roomSkillSnapshot}
/>
)}
);
}