mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-17 13:00:48 +00:00
Status: route JSON through lean command
This commit is contained in:
@@ -6,6 +6,7 @@ const runConfigUnsetMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const modelsListCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const modelsStatusCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const gatewayStatusCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const statusJsonCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
|
||||
vi.mock("../config-cli.js", () => ({
|
||||
runConfigGet: runConfigGetMock,
|
||||
@@ -21,6 +22,10 @@ vi.mock("../../commands/gateway-status.js", () => ({
|
||||
gatewayStatusCommand: gatewayStatusCommandMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../commands/status-json.js", () => ({
|
||||
statusJsonCommand: statusJsonCommandMock,
|
||||
}));
|
||||
|
||||
describe("program routes", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
@@ -124,6 +129,26 @@ describe("program routes", () => {
|
||||
await expectRunFalse(["status"], ["node", "openclaw", "status", "--timeout"]);
|
||||
});
|
||||
|
||||
it("routes status --json through the lean JSON command", async () => {
|
||||
const route = expectRoute(["status"]);
|
||||
await expect(
|
||||
route?.run([
|
||||
"node",
|
||||
"openclaw",
|
||||
"status",
|
||||
"--json",
|
||||
"--deep",
|
||||
"--usage",
|
||||
"--timeout",
|
||||
"5000",
|
||||
]),
|
||||
).resolves.toBe(true);
|
||||
expect(statusJsonCommandMock).toHaveBeenCalledWith(
|
||||
{ deep: true, all: false, usage: true, timeoutMs: 5000 },
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns false for sessions route when --store value is missing", async () => {
|
||||
await expectRunFalse(["sessions"], ["node", "openclaw", "sessions", "--store"]);
|
||||
});
|
||||
|
||||
@@ -47,6 +47,11 @@ const routeStatus: RouteSpec = {
|
||||
if (timeoutMs === null) {
|
||||
return false;
|
||||
}
|
||||
if (json) {
|
||||
const { statusJsonCommand } = await import("../../commands/status-json.js");
|
||||
await statusJsonCommand({ deep, all, usage, timeoutMs }, defaultRuntime);
|
||||
return true;
|
||||
}
|
||||
const { statusCommand } = await import("../../commands/status.js");
|
||||
await statusCommand({ json, deep, all, usage, timeoutMs, verbose }, defaultRuntime);
|
||||
return true;
|
||||
|
||||
100
src/commands/status-json.ts
Normal file
100
src/commands/status-json.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { callGateway } from "../gateway/call.js";
|
||||
import type { HeartbeatEventPayload } from "../infra/heartbeat-events.js";
|
||||
import { normalizeUpdateChannel, resolveUpdateChannelDisplay } from "../infra/update-channels.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { runSecurityAudit } from "../security/audit.js";
|
||||
import { getDaemonStatusSummary, getNodeDaemonStatusSummary } from "./status.daemon.js";
|
||||
import { scanStatus } from "./status.scan.js";
|
||||
|
||||
let providerUsagePromise: Promise<typeof import("../infra/provider-usage.js")> | undefined;
|
||||
|
||||
function loadProviderUsage() {
|
||||
providerUsagePromise ??= import("../infra/provider-usage.js");
|
||||
return providerUsagePromise;
|
||||
}
|
||||
|
||||
export async function statusJsonCommand(
|
||||
opts: {
|
||||
deep?: boolean;
|
||||
usage?: boolean;
|
||||
timeoutMs?: number;
|
||||
all?: boolean;
|
||||
},
|
||||
runtime: RuntimeEnv,
|
||||
) {
|
||||
const scan = await scanStatus({ json: true, timeoutMs: opts.timeoutMs, all: opts.all }, runtime);
|
||||
const securityAudit = await runSecurityAudit({
|
||||
config: scan.cfg,
|
||||
sourceConfig: scan.sourceConfig,
|
||||
deep: false,
|
||||
includeFilesystem: true,
|
||||
includeChannelSecurity: true,
|
||||
});
|
||||
|
||||
const usage = opts.usage
|
||||
? await loadProviderUsage().then(({ loadProviderUsageSummary }) =>
|
||||
loadProviderUsageSummary({ timeoutMs: opts.timeoutMs }),
|
||||
)
|
||||
: undefined;
|
||||
const health = opts.deep
|
||||
? await callGateway({
|
||||
method: "health",
|
||||
params: { probe: true },
|
||||
timeoutMs: opts.timeoutMs,
|
||||
config: scan.cfg,
|
||||
}).catch(() => undefined)
|
||||
: undefined;
|
||||
const lastHeartbeat =
|
||||
opts.deep && scan.gatewayReachable
|
||||
? await callGateway<HeartbeatEventPayload | null>({
|
||||
method: "last-heartbeat",
|
||||
params: {},
|
||||
timeoutMs: opts.timeoutMs,
|
||||
config: scan.cfg,
|
||||
}).catch(() => null)
|
||||
: null;
|
||||
|
||||
const [daemon, nodeDaemon] = await Promise.all([
|
||||
getDaemonStatusSummary(),
|
||||
getNodeDaemonStatusSummary(),
|
||||
]);
|
||||
const channelInfo = resolveUpdateChannelDisplay({
|
||||
configChannel: normalizeUpdateChannel(scan.cfg.update?.channel),
|
||||
installKind: scan.update.installKind,
|
||||
gitTag: scan.update.git?.tag ?? null,
|
||||
gitBranch: scan.update.git?.branch ?? null,
|
||||
});
|
||||
|
||||
runtime.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
...scan.summary,
|
||||
os: scan.osSummary,
|
||||
update: scan.update,
|
||||
updateChannel: channelInfo.channel,
|
||||
updateChannelSource: channelInfo.source,
|
||||
memory: scan.memory,
|
||||
memoryPlugin: scan.memoryPlugin,
|
||||
gateway: {
|
||||
mode: scan.gatewayMode,
|
||||
url: scan.gatewayConnection.url,
|
||||
urlSource: scan.gatewayConnection.urlSource,
|
||||
misconfigured: scan.remoteUrlMissing,
|
||||
reachable: scan.gatewayReachable,
|
||||
connectLatencyMs: scan.gatewayProbe?.connectLatencyMs ?? null,
|
||||
self: scan.gatewaySelf,
|
||||
error: scan.gatewayProbe?.error ?? null,
|
||||
authWarning: scan.gatewayProbeAuthWarning ?? null,
|
||||
},
|
||||
gatewayService: daemon,
|
||||
nodeService: nodeDaemon,
|
||||
agents: scan.agentStatus,
|
||||
securityAudit,
|
||||
secretDiagnostics: scan.secretDiagnostics,
|
||||
...(health || usage || lastHeartbeat ? { health, usage, lastHeartbeat } : {}),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user