@gguf/claw
Version:
Multi-channel AI gateway with extensible messaging integrations
420 lines (416 loc) • 11.7 kB
JavaScript
import { t as runCommandWithTimeout } from "./exec-DYqRzFbo.js";
import { n as fetchWithTimeout } from "./fetch-timeout-DoySHqfO.js";
import { r as parseSemver } from "./runtime-guard-DlT3NecY.js";
import path from "node:path";
import fs from "node:fs/promises";
//#region src/infra/update-channels.ts
const DEFAULT_PACKAGE_CHANNEL = "stable";
const DEFAULT_GIT_CHANNEL = "dev";
const DEV_BRANCH = "main";
function normalizeUpdateChannel(value) {
if (!value) return null;
const normalized = value.trim().toLowerCase();
if (normalized === "stable" || normalized === "beta" || normalized === "dev") return normalized;
return null;
}
function channelToNpmTag(channel) {
if (channel === "beta") return "beta";
if (channel === "dev") return "dev";
return "latest";
}
function isBetaTag(tag) {
return tag.toLowerCase().includes("-beta");
}
function isStableTag(tag) {
return !isBetaTag(tag);
}
function resolveEffectiveUpdateChannel(params) {
if (params.configChannel) return {
channel: params.configChannel,
source: "config"
};
if (params.installKind === "git") {
const tag = params.git?.tag;
if (tag) return {
channel: isBetaTag(tag) ? "beta" : "stable",
source: "git-tag"
};
const branch = params.git?.branch;
if (branch && branch !== "HEAD") return {
channel: "dev",
source: "git-branch"
};
return {
channel: DEFAULT_GIT_CHANNEL,
source: "default"
};
}
if (params.installKind === "package") return {
channel: DEFAULT_PACKAGE_CHANNEL,
source: "default"
};
return {
channel: DEFAULT_PACKAGE_CHANNEL,
source: "default"
};
}
function formatUpdateChannelLabel(params) {
if (params.source === "config") return `${params.channel} (config)`;
if (params.source === "git-tag") return params.gitTag ? `${params.channel} (${params.gitTag})` : `${params.channel} (tag)`;
if (params.source === "git-branch") return params.gitBranch ? `${params.channel} (${params.gitBranch})` : `${params.channel} (branch)`;
return `${params.channel} (default)`;
}
function resolveUpdateChannelDisplay(params) {
const channelInfo = resolveEffectiveUpdateChannel({
configChannel: params.configChannel,
installKind: params.installKind,
git: params.gitTag || params.gitBranch ? {
tag: params.gitTag ?? null,
branch: params.gitBranch ?? null
} : void 0
});
return {
channel: channelInfo.channel,
source: channelInfo.source,
label: formatUpdateChannelLabel({
channel: channelInfo.channel,
source: channelInfo.source,
gitTag: params.gitTag ?? null,
gitBranch: params.gitBranch ?? null
})
};
}
//#endregion
//#region src/infra/detect-package-manager.ts
async function detectPackageManager$1(root) {
try {
const raw = await fs.readFile(path.join(root, "package.json"), "utf-8");
const pm = JSON.parse(raw)?.packageManager?.split("@")[0]?.trim();
if (pm === "pnpm" || pm === "bun" || pm === "npm") return pm;
} catch {}
const files = await fs.readdir(root).catch(() => []);
if (files.includes("pnpm-lock.yaml")) return "pnpm";
if (files.includes("bun.lockb")) return "bun";
if (files.includes("package-lock.json")) return "npm";
return null;
}
//#endregion
//#region src/infra/update-check.ts
function formatGitInstallLabel(update) {
if (update.installKind !== "git") return null;
const shortSha = update.git?.sha ? update.git.sha.slice(0, 8) : null;
const branch = update.git?.branch && update.git.branch !== "HEAD" ? update.git.branch : null;
const tag = update.git?.tag ?? null;
return [
branch ?? (tag ? "detached" : "git"),
tag ? `tag ${tag}` : null,
shortSha ? `@ ${shortSha}` : null
].filter(Boolean).join(" · ");
}
async function exists(p) {
try {
await fs.access(p);
return true;
} catch {
return false;
}
}
async function detectPackageManager(root) {
return await detectPackageManager$1(root) ?? "unknown";
}
async function detectGitRoot(root) {
const res = await runCommandWithTimeout([
"git",
"-C",
root,
"rev-parse",
"--show-toplevel"
], { timeoutMs: 4e3 }).catch(() => null);
if (!res || res.code !== 0) return null;
const top = res.stdout.trim();
return top ? path.resolve(top) : null;
}
async function checkGitUpdateStatus(params) {
const timeoutMs = params.timeoutMs ?? 6e3;
const root = path.resolve(params.root);
const base = {
root,
sha: null,
tag: null,
branch: null,
upstream: null,
dirty: null,
ahead: null,
behind: null,
fetchOk: null
};
const branchRes = await runCommandWithTimeout([
"git",
"-C",
root,
"rev-parse",
"--abbrev-ref",
"HEAD"
], { timeoutMs }).catch(() => null);
if (!branchRes || branchRes.code !== 0) return {
...base,
error: branchRes?.stderr?.trim() || "git unavailable"
};
const branch = branchRes.stdout.trim() || null;
const shaRes = await runCommandWithTimeout([
"git",
"-C",
root,
"rev-parse",
"HEAD"
], { timeoutMs }).catch(() => null);
const sha = shaRes && shaRes.code === 0 ? shaRes.stdout.trim() : null;
const tagRes = await runCommandWithTimeout([
"git",
"-C",
root,
"describe",
"--tags",
"--exact-match"
], { timeoutMs }).catch(() => null);
const tag = tagRes && tagRes.code === 0 ? tagRes.stdout.trim() : null;
const upstreamRes = await runCommandWithTimeout([
"git",
"-C",
root,
"rev-parse",
"--abbrev-ref",
"@{upstream}"
], { timeoutMs }).catch(() => null);
const upstream = upstreamRes && upstreamRes.code === 0 ? upstreamRes.stdout.trim() : null;
const dirtyRes = await runCommandWithTimeout([
"git",
"-C",
root,
"status",
"--porcelain",
"--",
":!dist/control-ui/"
], { timeoutMs }).catch(() => null);
const dirty = dirtyRes && dirtyRes.code === 0 ? dirtyRes.stdout.trim().length > 0 : null;
const fetchOk = params.fetch ? await runCommandWithTimeout([
"git",
"-C",
root,
"fetch",
"--quiet",
"--prune"
], { timeoutMs }).then((r) => r.code === 0).catch(() => false) : null;
const counts = upstream && upstream.length > 0 ? await runCommandWithTimeout([
"git",
"-C",
root,
"rev-list",
"--left-right",
"--count",
`HEAD...${upstream}`
], { timeoutMs }).catch(() => null) : null;
const parseCounts = (raw) => {
const parts = raw.trim().split(/\s+/);
if (parts.length < 2) return null;
const ahead = Number.parseInt(parts[0] ?? "", 10);
const behind = Number.parseInt(parts[1] ?? "", 10);
if (!Number.isFinite(ahead) || !Number.isFinite(behind)) return null;
return {
ahead,
behind
};
};
const parsed = counts && counts.code === 0 ? parseCounts(counts.stdout) : null;
return {
root,
sha,
tag,
branch,
upstream,
dirty,
ahead: parsed?.ahead ?? null,
behind: parsed?.behind ?? null,
fetchOk
};
}
async function statMtimeMs(p) {
try {
return (await fs.stat(p)).mtimeMs;
} catch {
return null;
}
}
function resolveDepsMarker(params) {
const root = params.root;
if (params.manager === "pnpm") return {
lockfilePath: path.join(root, "pnpm-lock.yaml"),
markerPath: path.join(root, "node_modules", ".modules.yaml")
};
if (params.manager === "bun") return {
lockfilePath: path.join(root, "bun.lockb"),
markerPath: path.join(root, "node_modules")
};
if (params.manager === "npm") return {
lockfilePath: path.join(root, "package-lock.json"),
markerPath: path.join(root, "node_modules")
};
return {
lockfilePath: null,
markerPath: null
};
}
async function checkDepsStatus(params) {
const { lockfilePath, markerPath } = resolveDepsMarker({
root: path.resolve(params.root),
manager: params.manager
});
if (!lockfilePath || !markerPath) return {
manager: params.manager,
status: "unknown",
lockfilePath,
markerPath,
reason: "unknown package manager"
};
const lockExists = await exists(lockfilePath);
const markerExists = await exists(markerPath);
if (!lockExists) return {
manager: params.manager,
status: "unknown",
lockfilePath,
markerPath,
reason: "lockfile missing"
};
if (!markerExists) return {
manager: params.manager,
status: "missing",
lockfilePath,
markerPath,
reason: "node_modules marker missing"
};
const lockMtime = await statMtimeMs(lockfilePath);
const markerMtime = await statMtimeMs(markerPath);
if (!lockMtime || !markerMtime) return {
manager: params.manager,
status: "unknown",
lockfilePath,
markerPath
};
if (lockMtime > markerMtime + 1e3) return {
manager: params.manager,
status: "stale",
lockfilePath,
markerPath,
reason: "lockfile newer than install marker"
};
return {
manager: params.manager,
status: "ok",
lockfilePath,
markerPath
};
}
async function fetchNpmLatestVersion(params) {
const res = await fetchNpmTagVersion({
tag: "latest",
timeoutMs: params?.timeoutMs
});
return {
latestVersion: res.version,
error: res.error
};
}
async function fetchNpmTagVersion(params) {
const timeoutMs = params?.timeoutMs ?? 3500;
const tag = params.tag;
try {
const res = await fetchWithTimeout(`https://registry.npmjs.org/openclaw/${encodeURIComponent(tag)}`, {}, Math.max(250, timeoutMs));
if (!res.ok) return {
tag,
version: null,
error: `HTTP ${res.status}`
};
const json = await res.json();
return {
tag,
version: typeof json?.version === "string" ? json.version : null
};
} catch (err) {
return {
tag,
version: null,
error: String(err)
};
}
}
async function resolveNpmChannelTag(params) {
const channelTag = channelToNpmTag(params.channel);
const channelStatus = await fetchNpmTagVersion({
tag: channelTag,
timeoutMs: params.timeoutMs
});
if (params.channel !== "beta") return {
tag: channelTag,
version: channelStatus.version
};
const latestStatus = await fetchNpmTagVersion({
tag: "latest",
timeoutMs: params.timeoutMs
});
if (!latestStatus.version) return {
tag: channelTag,
version: channelStatus.version
};
if (!channelStatus.version) return {
tag: "latest",
version: latestStatus.version
};
const cmp = compareSemverStrings(channelStatus.version, latestStatus.version);
if (cmp != null && cmp < 0) return {
tag: "latest",
version: latestStatus.version
};
return {
tag: channelTag,
version: channelStatus.version
};
}
function compareSemverStrings(a, b) {
const pa = parseSemver(a);
const pb = parseSemver(b);
if (!pa || !pb) return null;
if (pa.major !== pb.major) return pa.major < pb.major ? -1 : 1;
if (pa.minor !== pb.minor) return pa.minor < pb.minor ? -1 : 1;
if (pa.patch !== pb.patch) return pa.patch < pb.patch ? -1 : 1;
return 0;
}
async function checkUpdateStatus(params) {
const timeoutMs = params.timeoutMs ?? 6e3;
const root = params.root ? path.resolve(params.root) : null;
if (!root) return {
root: null,
installKind: "unknown",
packageManager: "unknown",
registry: params.includeRegistry ? await fetchNpmLatestVersion({ timeoutMs }) : void 0
};
const pm = await detectPackageManager(root);
const gitRoot = await detectGitRoot(root);
const isGit = gitRoot && path.resolve(gitRoot) === root;
return {
root,
installKind: isGit ? "git" : "package",
packageManager: pm,
git: isGit ? await checkGitUpdateStatus({
root,
timeoutMs,
fetch: Boolean(params.fetchGit)
}) : void 0,
deps: await checkDepsStatus({
root,
manager: pm
}),
registry: params.includeRegistry ? await fetchNpmLatestVersion({ timeoutMs }) : void 0
};
}
//#endregion
export { resolveNpmChannelTag as a, DEFAULT_PACKAGE_CHANNEL as c, formatUpdateChannelLabel as d, isBetaTag as f, resolveUpdateChannelDisplay as g, resolveEffectiveUpdateChannel as h, formatGitInstallLabel as i, DEV_BRANCH as l, normalizeUpdateChannel as m, compareSemverStrings as n, detectPackageManager$1 as o, isStableTag as p, fetchNpmTagVersion as r, DEFAULT_GIT_CHANNEL as s, checkUpdateStatus as t, channelToNpmTag as u };