openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
163 lines (162 loc) • 7.49 kB
JavaScript
import { t as formatCliCommand } from "./command-format-CKGmlpAQ.js";
import "./agent-scope-MrLta7Pq.js";
import { c as resolveDefaultAgentId, o as resolveAgentWorkspaceDir } from "./agent-scope-config-CgCYpZfK.js";
import { t as buildWorkspaceSkillStatus } from "./status-Dlj2eT9o.js";
import { t as note } from "./note-BRSJp0UF.js";
import { n as disableUnavailableSkillsInConfig, t as collectUnavailableAgentSkills } from "./doctor-skills-core-N-TJqicN.js";
import { existsSync } from "node:fs";
import { posix, win32 } from "node:path";
//#region src/skills/lifecycle/gh-config-discovery.ts
function pathFor(platform) {
return platform === "win32" ? win32 : posix;
}
const HOSTS_FILE = "hosts.yml";
function resolveEffectiveGhConfigDir(input) {
const env = input.env;
if (env.GH_CONFIG_DIR && env.GH_CONFIG_DIR.trim()) return env.GH_CONFIG_DIR.trim();
const xdg = env.XDG_CONFIG_HOME?.trim();
if (xdg) return pathFor(input.platform).join(xdg, "gh");
if (input.platform === "win32") {
const appData = env.APPDATA?.trim();
if (appData) return pathFor(input.platform).join(appData, "GitHub CLI");
const profile = env.USERPROFILE?.trim();
if (profile) return pathFor(input.platform).join(profile, "AppData", "Roaming", "GitHub CLI");
}
const home = env.HOME?.trim();
if (!home) return;
return pathFor(input.platform).join(home, ".config", "gh");
}
function defaultCandidateOperatorHomes(input) {
const env = input.env;
const homes = /* @__PURE__ */ new Set();
if (input.platform !== "win32") homes.add("/root");
if (env.SUDO_USER?.trim()) {
const sudoUser = env.SUDO_USER.trim();
homes.add(pathFor(input.platform).join("/home", sudoUser));
if (input.platform === "darwin") homes.add(pathFor(input.platform).join("/Users", sudoUser));
}
if (env.USER?.trim()) {
const user = env.USER.trim();
if (user !== "root") {
if (input.platform === "darwin") homes.add(pathFor(input.platform).join("/Users", user));
else if (input.platform !== "win32") homes.add(pathFor(input.platform).join("/home", user));
}
}
const processHome = env.HOME?.trim();
if (processHome) homes.delete(processHome);
return [...homes];
}
function ghConfigDirForHome(home, platform) {
return pathFor(platform).join(home, ".config", "gh");
}
function detectGhConfigDirMismatch(input) {
const env = input.env;
if (env.GH_CONFIG_DIR && env.GH_CONFIG_DIR.trim()) return {
kind: "explicit-gh-config-dir-set",
ghConfigDir: env.GH_CONFIG_DIR.trim()
};
const effective = resolveEffectiveGhConfigDir(input);
if (!effective) return { kind: "no-process-home" };
const effectiveHosts = pathFor(input.platform).join(effective, HOSTS_FILE);
if (input.fileExists(effectiveHosts)) return {
kind: "auth-discoverable",
effectiveConfigDir: effective
};
const candidates = input.candidateOperatorHomes ?? defaultCandidateOperatorHomes(input);
for (const home of candidates) {
const candidateDir = ghConfigDirForHome(home, input.platform);
if (candidateDir === effective) continue;
const candidateHosts = pathFor(input.platform).join(candidateDir, HOSTS_FILE);
if (input.fileExists(candidateHosts)) return {
kind: "mismatch",
effectiveConfigDir: effective,
alternateConfigDir: candidateDir,
alternateHostsFile: candidateHosts,
alternateHomeHint: home,
suggestedEnvValue: candidateDir
};
}
return {
kind: "no-known-auth",
effectiveConfigDir: effective
};
}
function formatGhConfigDirMismatchHint(mismatch) {
const lines = [
"GitHub CLI auth was found at a different HOME than the one this OpenClaw process uses.",
` Process gh config dir: ${mismatch.effectiveConfigDir}`,
` Authenticated config: ${mismatch.alternateConfigDir} (contains ${HOSTS_FILE})`
];
if (mismatch.alternateHomeHint) lines.push(` Authenticated HOME: ${mismatch.alternateHomeHint}`);
lines.push(` Fix: set GH_CONFIG_DIR=${mismatch.suggestedEnvValue} on the OpenClaw service environment, then restart the gateway.`);
return lines;
}
//#endregion
//#region src/commands/doctor-skills.ts
/** Doctor checks and repair prompts for unavailable configured skills. */
function formatMissingSummary(skill) {
const missing = [];
if (skill.missing.bins.length > 0) missing.push(`bins: ${skill.missing.bins.join(", ")}`);
if (skill.missing.anyBins.length > 0) missing.push(`any bins: ${skill.missing.anyBins.join(", ")}`);
if (skill.missing.env.length > 0) missing.push(`env: ${skill.missing.env.join(", ")}`);
if (skill.missing.config.length > 0) missing.push(`config: ${skill.missing.config.join(", ")}`);
if (skill.missing.os.length > 0) missing.push(`os: ${skill.missing.os.join(", ")}`);
return missing.join("; ") || "unknown requirement";
}
function formatInstallHints(skill) {
if (skill.install.length === 0) return [];
return skill.install.slice(0, 2).map((entry) => ` install option: ${entry.label}`);
}
function defaultGhConfigDiscoveryInput() {
return {
platform: process.platform,
env: process.env,
fileExists: (absolutePath) => existsSync(absolutePath)
};
}
/** Builds a GitHub CLI config-dir hint for eligible GitHub skill setups. */
function describeGhConfigDirHint(skills) {
return describeGhConfigDirHintFromDiscovery(skills, defaultGhConfigDiscoveryInput());
}
/** Builds a GitHub CLI config-dir hint from injected discovery inputs for tests. */
function describeGhConfigDirHintFromDiscovery(skills, discoveryInput) {
const githubSkill = skills.find((skill) => skill.name === "github");
if (!githubSkill) return [];
if (!githubSkill.eligible || githubSkill.blockedByAgentFilter || githubSkill.disabled || githubSkill.blockedByAllowlist) return [];
const result = detectGhConfigDirMismatch(discoveryInput);
if (result.kind !== "mismatch") return [];
return formatGhConfigDirMismatchHint(result);
}
/** Formats doctor note lines for skills that are allowed but unavailable. */
function formatUnavailableSkillDoctorLines(skills) {
const lines = ["Some skills are allowed for this agent but are not usable in the current runtime environment."];
for (const skill of skills) {
lines.push(`- ${skill.name}: ${formatMissingSummary(skill)}`);
lines.push(...formatInstallHints(skill));
}
lines.push(`Disable unused skills: ${formatCliCommand("openclaw doctor --fix")}`);
lines.push(`Inspect details: ${formatCliCommand("openclaw skills check --agent <id>")} or ${formatCliCommand("openclaw skills info <name> --agent <id>")}`);
return lines;
}
/** Checks default-agent skill readiness and optionally disables unavailable skills in config. */
async function maybeRepairSkillReadiness(params) {
const agentId = resolveDefaultAgentId(params.cfg);
const report = buildWorkspaceSkillStatus(resolveAgentWorkspaceDir(params.cfg, agentId), {
config: params.cfg,
agentId
});
const githubHint = describeGhConfigDirHint(report.skills);
if (githubHint.length > 0) note(githubHint.join("\n"), "GitHub CLI");
const unavailable = collectUnavailableAgentSkills(report);
if (unavailable.length === 0) return params.cfg;
note(formatUnavailableSkillDoctorLines(unavailable).join("\n"), "Skills");
if (!await params.prompter.confirmAutoFix({
message: `Disable ${unavailable.length} unavailable skill${unavailable.length === 1 ? "" : "s"} in config?`,
initialValue: false
})) return params.cfg;
const next = disableUnavailableSkillsInConfig(params.cfg, unavailable);
note(unavailable.map((skill) => `- Disabled ${skill.name}`).join("\n"), "Doctor changes");
return next;
}
//#endregion
export { maybeRepairSkillReadiness };