mirror of
https://github.com/xmanrui/OpenClaw-bot-review.git
synced 2026-08-14 00:47:49 +00:00
Merge PR #38 with conflict resolution
This commit is contained in:
+30
-6
@@ -71,7 +71,7 @@ interface AllStats {
|
||||
monthly: DayStat[];
|
||||
}
|
||||
|
||||
type TimeRange = "daily" | "weekly" | "monthly";
|
||||
type TimeRange = "daily" | "weekly" | "monthly" | "total";
|
||||
|
||||
interface SubagentActivityEvent {
|
||||
key: string;
|
||||
@@ -238,7 +238,7 @@ export default function Home() {
|
||||
const [agentStates, setAgentStates] = useState<Record<string, string>>(cachedHomeAgentStates);
|
||||
const [agentActivity, setAgentActivity] = useState<AgentActivityData[] | null>(null);
|
||||
|
||||
const RANGE_LABELS: Record<TimeRange, string> = { daily: t("range.daily"), weekly: t("range.weekly"), monthly: t("range.monthly") };
|
||||
const RANGE_LABELS: Record<TimeRange, string> = { daily: t("range.daily"), weekly: t("range.weekly"), monthly: t("range.monthly"), total: t("range.total") };
|
||||
|
||||
const REFRESH_OPTIONS = [
|
||||
{ label: t("refresh.manual"), value: 0 },
|
||||
@@ -780,10 +780,34 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
{(() => {
|
||||
const currentData = allStats[statsRange];
|
||||
const totalInput = currentData.reduce((s, d) => s + d.inputTokens, 0);
|
||||
const totalOutput = currentData.reduce((s, d) => s + d.outputTokens, 0);
|
||||
const totalMsgs = currentData.reduce((s, d) => s + d.messageCount, 0);
|
||||
const currentData = statsRange === "total" ? allStats.monthly : allStats[statsRange];
|
||||
let totalInput: number, totalOutput: number, totalMsgs: number;
|
||||
if (statsRange === "total") {
|
||||
// Sum all data points (use daily for accurate totals, chart uses monthly)
|
||||
const allDaily = allStats.daily;
|
||||
totalInput = allDaily.reduce((s, d) => s + d.inputTokens, 0);
|
||||
totalOutput = allDaily.reduce((s, d) => s + d.outputTokens, 0);
|
||||
totalMsgs = allDaily.reduce((s, d) => s + d.messageCount, 0);
|
||||
} else {
|
||||
// Show current period only (today/this week/this month)
|
||||
const now = new Date();
|
||||
const getCurrentPeriodKey = () => {
|
||||
if (statsRange === "daily") return now.toISOString().slice(0, 10);
|
||||
if (statsRange === "weekly") {
|
||||
const day = now.getUTCDay();
|
||||
const mondayOffset = day === 0 ? -6 : 1 - day;
|
||||
const monday = new Date(now.getTime() + mondayOffset * 86400000);
|
||||
return monday.toISOString().slice(0, 10);
|
||||
}
|
||||
return now.toISOString().slice(0, 7); // monthly
|
||||
};
|
||||
const periodKey = getCurrentPeriodKey();
|
||||
const latestEntry = currentData.length > 0 ? currentData[currentData.length - 1] : null;
|
||||
const currentEntry = latestEntry?.date === periodKey ? latestEntry : null;
|
||||
totalInput = currentEntry?.inputTokens ?? 0;
|
||||
totalOutput = currentEntry?.outputTokens ?? 0;
|
||||
totalMsgs = currentEntry?.messageCount ?? 0;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-4">
|
||||
|
||||
+20
-3
@@ -360,9 +360,26 @@ function StatsDetail({ agentId }: { agentId: string }) {
|
||||
if (!stats) return null;
|
||||
|
||||
const currentData = stats[range];
|
||||
const totalInput = currentData.reduce((s, d) => s + d.inputTokens, 0);
|
||||
const totalOutput = currentData.reduce((s, d) => s + d.outputTokens, 0);
|
||||
const totalMessages = currentData.reduce((s, d) => s + d.messageCount, 0);
|
||||
|
||||
// Summary cards show the current period only (latest entry = today/this week/this month)
|
||||
const latestEntry = currentData.length > 0 ? currentData[currentData.length - 1] : null;
|
||||
const now = new Date();
|
||||
const isCurrentPeriod = (entry: typeof latestEntry) => {
|
||||
if (!entry) return false;
|
||||
if (range === "daily") return entry.date === now.toISOString().slice(0, 10);
|
||||
if (range === "weekly") {
|
||||
const day = now.getUTCDay();
|
||||
const mondayOffset = day === 0 ? -6 : 1 - day;
|
||||
const monday = new Date(now.getTime() + mondayOffset * 86400000);
|
||||
return entry.date === monday.toISOString().slice(0, 10);
|
||||
}
|
||||
if (range === "monthly") return entry.date === now.toISOString().slice(0, 7);
|
||||
return false;
|
||||
};
|
||||
const currentEntry = isCurrentPeriod(latestEntry) ? latestEntry : null;
|
||||
const totalInput = currentEntry?.inputTokens ?? 0;
|
||||
const totalOutput = currentEntry?.outputTokens ?? 0;
|
||||
const totalMessages = currentEntry?.messageCount ?? 0;
|
||||
|
||||
return (
|
||||
<main className="min-h-screen p-4 md:p-8 max-w-6xl mx-auto">
|
||||
|
||||
@@ -174,6 +174,7 @@ const translations: Record<Locale, Record<string, string>> = {
|
||||
"range.daily": "按日",
|
||||
"range.weekly": "按週",
|
||||
"range.monthly": "按月",
|
||||
"range.total": "總計",
|
||||
|
||||
// refresh options
|
||||
"refresh.manual": "手動重新整理",
|
||||
@@ -499,6 +500,7 @@ const translations: Record<Locale, Record<string, string>> = {
|
||||
"range.daily": "按天",
|
||||
"range.weekly": "按周",
|
||||
"range.monthly": "按月",
|
||||
"range.total": "总计",
|
||||
|
||||
// refresh options
|
||||
"refresh.manual": "手动刷新",
|
||||
@@ -828,6 +830,7 @@ const translations: Record<Locale, Record<string, string>> = {
|
||||
"range.daily": "Daily",
|
||||
"range.weekly": "Weekly",
|
||||
"range.monthly": "Monthly",
|
||||
"range.total": "Total",
|
||||
|
||||
// refresh options
|
||||
"refresh.manual": "Manual Refresh",
|
||||
|
||||
+16
-10
@@ -71,17 +71,21 @@ function scanSkillsDir(dir: string, source: string): SkillInfo[] {
|
||||
return skills;
|
||||
}
|
||||
|
||||
function getConfiguredAgentWorkspaces(): Array<{ id: string; workspace?: string }> {
|
||||
function getConfiguredAgentWorkspaces(): Array<{ id: string; workspace?: string; agentDir?: string }> {
|
||||
if (!fs.existsSync(OPENCLAW_CONFIG_PATH)) return [];
|
||||
|
||||
try {
|
||||
const config = JSON.parse(fs.readFileSync(OPENCLAW_CONFIG_PATH, "utf-8"));
|
||||
const agentList = Array.isArray(config.agents?.list) ? config.agents.list : [];
|
||||
return agentList
|
||||
.filter((agent: unknown): agent is { id: string; workspace?: string } => {
|
||||
.filter((agent: unknown): agent is { id: string; workspace?: string; agentDir?: string } => {
|
||||
return Boolean(agent && typeof agent === "object" && typeof (agent as { id?: string }).id === "string");
|
||||
})
|
||||
.map((agent: { id: string; workspace?: string }) => ({ id: agent.id, workspace: agent.workspace }));
|
||||
.map((agent: { id: string; workspace?: string; agentDir?: string }) => ({
|
||||
id: agent.id,
|
||||
workspace: agent.workspace,
|
||||
agentDir: agent.agentDir,
|
||||
}));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@@ -102,8 +106,8 @@ function getWorkspaceSkillSources(): Array<{ dir: string; source: string }> {
|
||||
addSource(path.join(OPENCLAW_HOME, "workspace", "skills"), "workspace:main");
|
||||
|
||||
for (const agent of getConfiguredAgentWorkspaces()) {
|
||||
if (!agent.workspace) continue;
|
||||
addSource(path.join(agent.workspace, "skills"), `workspace:${agent.id}`);
|
||||
addSource(agent.workspace ? path.join(agent.workspace, "skills") : undefined, `workspace:${agent.id}`);
|
||||
addSource(agent.agentDir ? path.join(agent.agentDir, "skills") : undefined, `workspace:${agent.id}`);
|
||||
}
|
||||
|
||||
return sources;
|
||||
@@ -180,9 +184,12 @@ export function listOpenclawSkills(): { skills: SkillInfo[]; agents: Record<stri
|
||||
}
|
||||
}
|
||||
|
||||
const legacyCustomSkills = scanSkillsDir(path.join(OPENCLAW_HOME, "skills"), "custom");
|
||||
const customSkills = scanSkillsDir(path.join(OPENCLAW_HOME, "skills"), "custom");
|
||||
const workspaceSkills = getWorkspaceSkillSources().flatMap(({ dir, source }) => scanSkillsDir(dir, source));
|
||||
const allSkills = mergeSkillsByLocation([...builtinSkills, ...extSkills, ...legacyCustomSkills, ...workspaceSkills]);
|
||||
const allSkills = mergeSkillsByLocation([...builtinSkills, ...extSkills, ...customSkills, ...workspaceSkills]);
|
||||
|
||||
let config: any = null;
|
||||
try { config = JSON.parse(fs.readFileSync(OPENCLAW_CONFIG_PATH, "utf-8")); } catch { /* config unavailable */ }
|
||||
|
||||
const agentSkills = getAgentSkillsFromSessions();
|
||||
for (const skill of allSkills) {
|
||||
@@ -194,10 +201,9 @@ export function listOpenclawSkills(): { skills: SkillInfo[]; agents: Record<stri
|
||||
skill.usedBy = Array.from(new Set(skill.usedBy)).sort();
|
||||
}
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(OPENCLAW_CONFIG_PATH, "utf-8"));
|
||||
const agentList = config.agents?.list || [];
|
||||
const agentListForInfo = (config || {}).agents?.list || [];
|
||||
const agents: Record<string, SkillAgentInfo> = {};
|
||||
for (const agent of agentList) {
|
||||
for (const agent of agentListForInfo) {
|
||||
agents[agent.id] = {
|
||||
name: agent.identity?.name || agent.name || agent.id,
|
||||
emoji: agent.identity?.emoji || "🤖",
|
||||
|
||||
Reference in New Issue
Block a user