mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-29 02:41:07 +00:00
* Config: fail closed invalid config loads * CLI: keep diagnostics on explicit best-effort config * Tests: cover invalid config best-effort diagnostics * Changelog: note invalid config fail-closed fix * Status: pass best-effort config through status-all gateway RPCs * CLI: pass config through gateway secret RPC * CLI: skip plugin loading from invalid config * Tests: align daemon token drift env precedence
93 lines
2.7 KiB
TypeScript
93 lines
2.7 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
|
|
import type { OpenClawConfig } from "../config/config.js";
|
|
import { loadConfig } from "../config/config.js";
|
|
import { loadSessionStore, resolveStorePath } from "../config/sessions.js";
|
|
import { listAgentsForGateway } from "../gateway/session-utils.js";
|
|
|
|
export type AgentLocalStatus = {
|
|
id: string;
|
|
name?: string;
|
|
workspaceDir: string | null;
|
|
bootstrapPending: boolean | null;
|
|
sessionsPath: string;
|
|
sessionsCount: number;
|
|
lastUpdatedAt: number | null;
|
|
lastActiveAgeMs: number | null;
|
|
};
|
|
|
|
type AgentLocalStatusesResult = {
|
|
defaultId: string;
|
|
agents: AgentLocalStatus[];
|
|
totalSessions: number;
|
|
bootstrapPendingCount: number;
|
|
};
|
|
|
|
async function fileExists(p: string): Promise<boolean> {
|
|
try {
|
|
await fs.access(p);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export async function getAgentLocalStatuses(
|
|
cfg: OpenClawConfig = loadConfig(),
|
|
): Promise<AgentLocalStatusesResult> {
|
|
const agentList = listAgentsForGateway(cfg);
|
|
const now = Date.now();
|
|
|
|
const statuses: AgentLocalStatus[] = [];
|
|
for (const agent of agentList.agents) {
|
|
const agentId = agent.id;
|
|
const workspaceDir = (() => {
|
|
try {
|
|
return resolveAgentWorkspaceDir(cfg, agentId);
|
|
} catch {
|
|
return null;
|
|
}
|
|
})();
|
|
|
|
const bootstrapPath = workspaceDir != null ? path.join(workspaceDir, "BOOTSTRAP.md") : null;
|
|
const bootstrapPending = bootstrapPath != null ? await fileExists(bootstrapPath) : null;
|
|
|
|
const sessionsPath = resolveStorePath(cfg.session?.store, { agentId });
|
|
const store = (() => {
|
|
try {
|
|
return loadSessionStore(sessionsPath);
|
|
} catch {
|
|
return {};
|
|
}
|
|
})();
|
|
const sessions = Object.entries(store)
|
|
.filter(([key]) => key !== "global" && key !== "unknown")
|
|
.map(([, entry]) => entry);
|
|
const sessionsCount = sessions.length;
|
|
const lastUpdatedAt = sessions.reduce((max, e) => Math.max(max, e?.updatedAt ?? 0), 0);
|
|
const resolvedLastUpdatedAt = lastUpdatedAt > 0 ? lastUpdatedAt : null;
|
|
const lastActiveAgeMs = resolvedLastUpdatedAt ? now - resolvedLastUpdatedAt : null;
|
|
|
|
statuses.push({
|
|
id: agentId,
|
|
name: agent.name,
|
|
workspaceDir,
|
|
bootstrapPending,
|
|
sessionsPath,
|
|
sessionsCount,
|
|
lastUpdatedAt: resolvedLastUpdatedAt,
|
|
lastActiveAgeMs,
|
|
});
|
|
}
|
|
|
|
const totalSessions = statuses.reduce((sum, s) => sum + s.sessionsCount, 0);
|
|
const bootstrapPendingCount = statuses.reduce((sum, s) => sum + (s.bootstrapPending ? 1 : 0), 0);
|
|
return {
|
|
defaultId: agentList.defaultId,
|
|
agents: statuses,
|
|
totalSessions,
|
|
bootstrapPendingCount,
|
|
};
|
|
}
|