Add marketplace feed entries command (#96158)

Merged via squash.

Prepared head SHA: 1289a25156
Co-authored-by: giodl73-repo <235387111+giodl73-repo@users.noreply.github.com>
Co-authored-by: giodl73-repo <235387111+giodl73-repo@users.noreply.github.com>
Reviewed-by: @giodl73-repo
This commit is contained in:
Gio Della-Libera
2026-06-28 14:11:29 -07:00
committed by GitHub
parent d1d0176acc
commit 9bb004359e
5 changed files with 362 additions and 1 deletions

View File

@@ -18,6 +18,7 @@ import { defaultRuntime } from "../runtime.js";
import { shortenHomeInString } from "../utils.js";
import { formatMissingPluginMessage } from "./error-format.js";
import type {
PluginMarketplaceEntriesOptions,
PluginMarketplaceListOptions,
PluginMarketplaceRefreshOptions,
PluginRegistryOptions,
@@ -468,6 +469,22 @@ type MarketplaceRefreshPayload = {
error?: string;
};
type MarketplaceEntryPayload = {
id?: string;
label: string;
kind?: string;
name?: string;
version?: string;
install?: {
defaultChoice?: string;
clawhubSpec?: string;
npmSpec?: string;
localPath?: string;
expectedIntegrity?: string;
minHostVersion?: string;
};
};
function buildMarketplaceRefreshPayload(
result: Awaited<
ReturnType<
@@ -545,6 +562,21 @@ function sanitizeMarketplaceRefreshPayload(
return sanitized;
}
function formatMarketplaceEntryInstall(entry: MarketplaceEntryPayload): string | undefined {
if (entry.install?.defaultChoice === "npm") {
return entry.install.npmSpec ?? entry.install.clawhubSpec ?? entry.install.localPath;
}
return entry.install?.clawhubSpec ?? entry.install?.npmSpec ?? entry.install?.localPath;
}
function formatMarketplaceEntryLine(entry: MarketplaceEntryPayload): string {
const id = entry.id ?? entry.name ?? entry.label;
const install = formatMarketplaceEntryInstall(entry);
const suffix = install ? " " + theme.muted(install) : "";
const label = entry.label !== id ? " " + theme.muted(entry.label) : "";
return theme.command(id) + label + suffix;
}
function formatMarketplaceRefreshSource(source: MarketplaceRefreshPayload["source"]): string {
if (source === "hosted") {
return theme.success("hosted");
@@ -581,6 +613,80 @@ function formatPinnedMarketplaceRefreshFailure(payload: MarketplaceRefreshPayloa
return `Pinned marketplace feed refresh did not accept a fresh hosted payload (source: ${payload.source}).`;
}
/** List entries from the configured OpenClaw marketplace feed. */
export async function runPluginMarketplaceEntriesCommand(
opts: PluginMarketplaceEntriesOptions,
): Promise<void> {
const catalog = await import("../plugins/official-external-plugin-catalog.js");
const cfg = getRuntimeConfig();
const result = await catalog.loadConfiguredHostedOfficialExternalPluginCatalogEntries(cfg, {
...(opts.feedProfile ? { feedProfile: opts.feedProfile } : {}),
...(opts.feedUrl ? { feedUrl: opts.feedUrl } : {}),
...(opts.offline ? { offline: true } : {}),
});
const summary = sanitizeMarketplaceRefreshPayload(buildMarketplaceRefreshPayload(result), {
feedUrl: opts.feedUrl,
});
const entries: MarketplaceEntryPayload[] = result.entries.map((entry) => {
const id = catalog.resolveOfficialExternalPluginId(entry);
const install =
catalog.resolveOfficialExternalPluginInstall(entry, { catalogConfig: cfg.marketplaces }) ??
undefined;
const payload: MarketplaceEntryPayload = {
label: catalog.resolveOfficialExternalPluginLabel(entry),
};
if (id) {
payload.id = id;
}
if (entry.kind) {
payload.kind = entry.kind;
}
if (entry.name) {
payload.name = entry.name;
}
if (entry.version) {
payload.version = entry.version;
}
if (install) {
payload.install = install;
}
return payload;
});
if (opts.json) {
defaultRuntime.writeJson({ ...summary, entries, entryCount: entries.length });
return;
}
const lines = [
theme.muted("Source:") + " " + formatMarketplaceRefreshSource(summary.source),
theme.muted("Entries:") + " " + String(entries.length),
];
if (summary.feed) {
lines.push(
theme.muted("Feed:") +
" " +
summary.feed.id +
" " +
theme.muted("sequence " + String(summary.feed.sequence)),
);
}
if (summary.metadata?.url) {
lines.push(theme.muted("URL:") + " " + summary.metadata.url);
}
if (summary.snapshot?.savedAt) {
lines.push(theme.muted("Snapshot:") + " " + summary.snapshot.savedAt);
}
if (summary.error) {
lines.push(theme.muted("Fallback reason:") + " " + summary.error);
}
if (entries.length > 0) {
lines.push("");
lines.push(...entries.map(formatMarketplaceEntryLine));
}
defaultRuntime.log(lines.join("\n"));
}
/** Refresh the configured OpenClaw marketplace feed snapshot. */
export async function runPluginMarketplaceRefreshCommand(
opts: PluginMarketplaceRefreshOptions,