mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-28 01:21:36 +00:00
fix(ci): align skills api and trim status startup
This commit is contained in:
@@ -66,15 +66,10 @@ describe("tryRouteCli", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("passes suppressDoctorStdout=true for routed --json commands", async () => {
|
||||
it("skips config guard for routed status --json commands", async () => {
|
||||
await expect(tryRouteCli(["node", "openclaw", "status", "--json"])).resolves.toBe(true);
|
||||
|
||||
expect(ensureConfigReadyMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
commandPath: ["status"],
|
||||
suppressDoctorStdout: true,
|
||||
}),
|
||||
);
|
||||
expect(ensureConfigReadyMock).not.toHaveBeenCalled();
|
||||
expect(ensurePluginRegistryLoadedMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { isTruthyEnvValue } from "../infra/env.js";
|
||||
import { loggingState } from "../logging/state.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { VERSION } from "../version.js";
|
||||
import { getCommandPathWithRootOptions, hasFlag, hasHelpOrVersion } from "./argv.js";
|
||||
import { emitCliBanner } from "./banner.js";
|
||||
import { findRoutedCommand } from "./program/routes.js";
|
||||
|
||||
async function prepareRoutedCommand(params: {
|
||||
@@ -12,13 +10,22 @@ async function prepareRoutedCommand(params: {
|
||||
loadPlugins?: boolean | ((argv: string[]) => boolean);
|
||||
}) {
|
||||
const suppressDoctorStdout = hasFlag(params.argv, "--json");
|
||||
emitCliBanner(VERSION, { argv: params.argv });
|
||||
const { ensureConfigReady } = await import("./program/config-guard.js");
|
||||
await ensureConfigReady({
|
||||
runtime: defaultRuntime,
|
||||
commandPath: params.commandPath,
|
||||
...(suppressDoctorStdout ? { suppressDoctorStdout: true } : {}),
|
||||
});
|
||||
const skipConfigGuard = params.commandPath[0] === "status" && suppressDoctorStdout;
|
||||
if (!suppressDoctorStdout && process.stdout.isTTY) {
|
||||
const [{ emitCliBanner }, { VERSION }] = await Promise.all([
|
||||
import("./banner.js"),
|
||||
import("../version.js"),
|
||||
]);
|
||||
emitCliBanner(VERSION, { argv: params.argv });
|
||||
}
|
||||
if (!skipConfigGuard) {
|
||||
const { ensureConfigReady } = await import("./program/config-guard.js");
|
||||
await ensureConfigReady({
|
||||
runtime: defaultRuntime,
|
||||
commandPath: params.commandPath,
|
||||
...(suppressDoctorStdout ? { suppressDoctorStdout: true } : {}),
|
||||
});
|
||||
}
|
||||
const shouldLoadPlugins =
|
||||
typeof params.loadPlugins === "function" ? params.loadPlugins(params.argv) : params.loadPlugins;
|
||||
if (shouldLoadPlugins) {
|
||||
|
||||
@@ -7,6 +7,7 @@ const normalizeEnvMock = vi.hoisted(() => vi.fn());
|
||||
const ensurePathMock = vi.hoisted(() => vi.fn());
|
||||
const assertRuntimeMock = vi.hoisted(() => vi.fn());
|
||||
const closeActiveMemorySearchManagersMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const hasMemoryRuntimeMock = vi.hoisted(() => vi.fn(() => false));
|
||||
const outputRootHelpMock = vi.hoisted(() => vi.fn());
|
||||
const buildProgramMock = vi.hoisted(() => vi.fn());
|
||||
const maybeRunCliInContainerMock = vi.hoisted(() =>
|
||||
@@ -44,6 +45,10 @@ vi.mock("../plugins/memory-runtime.js", () => ({
|
||||
closeActiveMemorySearchManagers: closeActiveMemorySearchManagersMock,
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/memory-state.js", () => ({
|
||||
hasMemoryRuntime: hasMemoryRuntimeMock,
|
||||
}));
|
||||
|
||||
vi.mock("./program/root-help.js", () => ({
|
||||
outputRootHelp: outputRootHelpMock,
|
||||
}));
|
||||
@@ -57,6 +62,7 @@ const { runCli } = await import("./run-main.js");
|
||||
describe("runCli exit behavior", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
hasMemoryRuntimeMock.mockReturnValue(false);
|
||||
});
|
||||
|
||||
it("does not force process.exit after successful routed command", async () => {
|
||||
@@ -69,7 +75,7 @@ describe("runCli exit behavior", () => {
|
||||
|
||||
expect(maybeRunCliInContainerMock).toHaveBeenCalledWith(["node", "openclaw", "status"]);
|
||||
expect(tryRouteCliMock).toHaveBeenCalledWith(["node", "openclaw", "status"]);
|
||||
expect(closeActiveMemorySearchManagersMock).toHaveBeenCalledTimes(1);
|
||||
expect(closeActiveMemorySearchManagersMock).not.toHaveBeenCalled();
|
||||
expect(exitSpy).not.toHaveBeenCalled();
|
||||
exitSpy.mockRestore();
|
||||
});
|
||||
@@ -85,11 +91,20 @@ describe("runCli exit behavior", () => {
|
||||
expect(tryRouteCliMock).not.toHaveBeenCalled();
|
||||
expect(outputRootHelpMock).toHaveBeenCalledTimes(1);
|
||||
expect(buildProgramMock).not.toHaveBeenCalled();
|
||||
expect(closeActiveMemorySearchManagersMock).toHaveBeenCalledTimes(1);
|
||||
expect(closeActiveMemorySearchManagersMock).not.toHaveBeenCalled();
|
||||
expect(exitSpy).not.toHaveBeenCalled();
|
||||
exitSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("closes memory managers when a runtime was registered", async () => {
|
||||
tryRouteCliMock.mockResolvedValueOnce(true);
|
||||
hasMemoryRuntimeMock.mockReturnValue(true);
|
||||
|
||||
await runCli(["node", "openclaw", "status"]);
|
||||
|
||||
expect(closeActiveMemorySearchManagersMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("returns after a handled container-target invocation", async () => {
|
||||
maybeRunCliInContainerMock.mockReturnValueOnce({ handled: true, exitCode: 0 });
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { existsSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { resolveStateDir } from "../config/paths.js";
|
||||
import { normalizeEnv } from "../infra/env.js";
|
||||
import { formatUncaughtError } from "../infra/errors.js";
|
||||
import { isMainModule } from "../infra/is-main.js";
|
||||
import { ensureOpenClawCliOnPath } from "../infra/path-env.js";
|
||||
import { assertSupportedRuntime } from "../infra/runtime-guard.js";
|
||||
import { enableConsoleCapture } from "../logging.js";
|
||||
import { hasMemoryRuntime } from "../plugins/memory-state.js";
|
||||
import {
|
||||
getCommandPathWithRootOptions,
|
||||
getPrimaryCommand,
|
||||
@@ -13,12 +17,14 @@ import {
|
||||
isRootHelpInvocation,
|
||||
} from "./argv.js";
|
||||
import { maybeRunCliInContainer, parseCliContainerArgs } from "./container-target.js";
|
||||
import { loadCliDotEnv } from "./dotenv.js";
|
||||
import { applyCliProfileEnv, parseCliProfileArgs } from "./profile.js";
|
||||
import { tryRouteCli } from "./route.js";
|
||||
import { normalizeWindowsArgv } from "./windows-argv.js";
|
||||
|
||||
async function closeCliMemoryManagers(): Promise<void> {
|
||||
if (!hasMemoryRuntime()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { closeActiveMemorySearchManagers } = await import("../plugins/memory-runtime.js");
|
||||
await closeActiveMemorySearchManagers();
|
||||
@@ -80,6 +86,13 @@ export function shouldUseRootHelpFastPath(argv: string[]): boolean {
|
||||
return isRootHelpInvocation(argv);
|
||||
}
|
||||
|
||||
function shouldLoadCliDotEnv(env: NodeJS.ProcessEnv = process.env): boolean {
|
||||
if (existsSync(path.join(process.cwd(), ".env"))) {
|
||||
return true;
|
||||
}
|
||||
return existsSync(path.join(resolveStateDir(env), ".env"));
|
||||
}
|
||||
|
||||
export async function runCli(argv: string[] = process.argv) {
|
||||
const originalArgv = normalizeWindowsArgv(argv);
|
||||
const parsedContainer = parseCliContainerArgs(originalArgv);
|
||||
@@ -108,7 +121,10 @@ export async function runCli(argv: string[] = process.argv) {
|
||||
}
|
||||
let normalizedArgv = parsedProfile.argv;
|
||||
|
||||
loadCliDotEnv({ quiet: true });
|
||||
if (shouldLoadCliDotEnv()) {
|
||||
const { loadCliDotEnv } = await import("./dotenv.js");
|
||||
loadCliDotEnv({ quiet: true });
|
||||
}
|
||||
normalizeEnv();
|
||||
if (shouldEnsureCliPath(normalizedArgv)) {
|
||||
ensureOpenClawCliOnPath();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { createSyntheticSourceInfo } from "@mariozechner/pi-coding-agent";
|
||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
||||
import type { SkillEntry } from "../agents/skills.js";
|
||||
@@ -38,7 +39,11 @@ describe("skills-cli (e2e)", () => {
|
||||
description: "Capture UI screenshots",
|
||||
filePath: path.join(baseDir, "SKILL.md"),
|
||||
baseDir,
|
||||
source: "openclaw-bundled",
|
||||
sourceInfo: createSyntheticSourceInfo(path.join(baseDir, "SKILL.md"), {
|
||||
source: "openclaw-bundled",
|
||||
scope: "project",
|
||||
baseDir,
|
||||
}),
|
||||
disableModelInvocation: false,
|
||||
},
|
||||
frontmatter: {},
|
||||
|
||||
Reference in New Issue
Block a user