File size: 4,509 Bytes
fc93158 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | import type { Command } from "commander";
import { setVerbose } from "../../globals.js";
import { isTruthyEnvValue } from "../../infra/env.js";
import type { LogLevel } from "../../logging/levels.js";
import { defaultRuntime } from "../../runtime.js";
import {
getCommandPathWithRootOptions,
getVerboseFlag,
hasFlag,
hasHelpOrVersion,
} from "../argv.js";
import { emitCliBanner } from "../banner.js";
import { resolveCliName } from "../cli-name.js";
function setProcessTitleForCommand(actionCommand: Command) {
let current: Command = actionCommand;
while (current.parent && current.parent.parent) {
current = current.parent;
}
const name = current.name();
const cliName = resolveCliName();
if (!name || name === cliName) {
return;
}
process.title = `${cliName}-${name}`;
}
// Commands that need channel plugins loaded
const PLUGIN_REQUIRED_COMMANDS = new Set([
"message",
"channels",
"directory",
"agents",
"configure",
"onboard",
"status",
"health",
]);
const CONFIG_GUARD_BYPASS_COMMANDS = new Set(["backup", "doctor", "completion", "secrets"]);
const JSON_PARSE_ONLY_COMMANDS = new Set(["config set"]);
let configGuardModulePromise: Promise<typeof import("./config-guard.js")> | undefined;
let pluginRegistryModulePromise: Promise<typeof import("../plugin-registry.js")> | undefined;
function shouldBypassConfigGuard(commandPath: string[]): boolean {
const [primary, secondary] = commandPath;
if (!primary) {
return false;
}
if (CONFIG_GUARD_BYPASS_COMMANDS.has(primary)) {
return true;
}
// config validate is the explicit validation command; let it render
// validation failures directly without preflight guard output duplication.
if (primary === "config" && secondary === "validate") {
return true;
}
return false;
}
function loadConfigGuardModule() {
configGuardModulePromise ??= import("./config-guard.js");
return configGuardModulePromise;
}
function loadPluginRegistryModule() {
pluginRegistryModulePromise ??= import("../plugin-registry.js");
return pluginRegistryModulePromise;
}
function getRootCommand(command: Command): Command {
let current = command;
while (current.parent) {
current = current.parent;
}
return current;
}
function getCliLogLevel(actionCommand: Command): LogLevel | undefined {
const root = getRootCommand(actionCommand);
if (typeof root.getOptionValueSource !== "function") {
return undefined;
}
if (root.getOptionValueSource("logLevel") !== "cli") {
return undefined;
}
const logLevel = root.opts<Record<string, unknown>>().logLevel;
return typeof logLevel === "string" ? (logLevel as LogLevel) : undefined;
}
function isJsonOutputMode(commandPath: string[], argv: string[]): boolean {
if (!hasFlag(argv, "--json")) {
return false;
}
const key = `${commandPath[0] ?? ""} ${commandPath[1] ?? ""}`.trim();
if (JSON_PARSE_ONLY_COMMANDS.has(key)) {
return false;
}
return true;
}
export function registerPreActionHooks(program: Command, programVersion: string) {
program.hook("preAction", async (_thisCommand, actionCommand) => {
setProcessTitleForCommand(actionCommand);
const argv = process.argv;
if (hasHelpOrVersion(argv)) {
return;
}
const commandPath = getCommandPathWithRootOptions(argv, 2);
const hideBanner =
isTruthyEnvValue(process.env.OPENCLAW_HIDE_BANNER) ||
commandPath[0] === "update" ||
commandPath[0] === "completion" ||
(commandPath[0] === "plugins" && commandPath[1] === "update");
if (!hideBanner) {
emitCliBanner(programVersion);
}
const verbose = getVerboseFlag(argv, { includeDebug: true });
setVerbose(verbose);
const cliLogLevel = getCliLogLevel(actionCommand);
if (cliLogLevel) {
process.env.OPENCLAW_LOG_LEVEL = cliLogLevel;
}
if (!verbose) {
process.env.NODE_NO_WARNINGS ??= "1";
}
if (shouldBypassConfigGuard(commandPath)) {
return;
}
const suppressDoctorStdout = isJsonOutputMode(commandPath, argv);
const { ensureConfigReady } = await loadConfigGuardModule();
await ensureConfigReady({
runtime: defaultRuntime,
commandPath,
...(suppressDoctorStdout ? { suppressDoctorStdout: true } : {}),
});
// Load plugins for commands that need channel access
if (PLUGIN_REQUIRED_COMMANDS.has(commandPath[0])) {
const { ensurePluginRegistryLoaded } = await loadPluginRegistryModule();
ensurePluginRegistryLoaded();
}
});
}
|