自动同步: 2026-07-01 02:10

This commit is contained in:
2026-07-01 02:10:07 +08:00
parent d20bd4cfab
commit 489a0835e9
4162 changed files with 1010609 additions and 12590 deletions
+822
View File
@@ -0,0 +1,822 @@
#!/usr/bin/env node
import { stat } from "node:fs/promises";
import { join, resolve } from "node:path";
import { Command, Option } from "commander";
import { getCliBuildLabel, getCliVersion } from "./cli/buildInfo.js";
import { resolveClawdbotDefaultWorkspace } from "./cli/clawdbotConfig.js";
import { cmdLoginFlow, cmdLogout, cmdToken, cmdWhoami } from "./cli/commands/auth.js";
import { cmdDeleteSkill, cmdHideSkill, cmdUndeleteSkill, cmdUnhideSkill, } from "./cli/commands/delete.js";
import { cmdInspect, cmdVerifySkill } from "./cli/commands/inspect.js";
import { cmdMergeSkill, cmdRenameSkill } from "./cli/commands/ownership.js";
import { cmdDeletePackage, cmdDeletePackageTrustedPublisher, cmdDownloadPackage, cmdExplorePackages, cmdGetPackageTrustedPublisher, cmdInspectPackage, cmdPackageModerationStatus, cmdPackageMigrationStatus, cmdPackageReadiness, cmdPackPackage, cmdPublishPackage, cmdReportPackage, cmdSetPackageTrustedPublisher, cmdTransferPackage, cmdUndeletePackage, cmdValidatePackage, cmdVerifyPackage, } from "./cli/commands/packages.js";
import { cmdPublish } from "./cli/commands/publish.js";
import { cmdCreatePublisher } from "./cli/commands/publishers.js";
import { cmdScan, cmdScanDownload } from "./cli/commands/scan.js";
import { cmdExplore, cmdInstall, cmdList, cmdPin, cmdSearch, cmdUninstall, cmdUnpin, cmdUpdate, } from "./cli/commands/skills.js";
import { cmdStarSkill } from "./cli/commands/star.js";
import { cmdSync } from "./cli/commands/sync.js";
import { cmdTransferAccept, cmdTransferCancel, cmdTransferList, cmdTransferReject, cmdTransferRequest, } from "./cli/commands/transfer.js";
import { cmdUnstarSkill } from "./cli/commands/unstar.js";
import { configureCommanderHelp, styleEnvBlock, styleError, styleTitle } from "./cli/helpStyle.js";
import { DEFAULT_REGISTRY, DEFAULT_SITE } from "./cli/registry.js";
import { fail, formatError } from "./cli/ui.js";
const CLI_HELP_HEADER = styleTitle(`🦞 ClawHub CLI ${getCliBuildLabel()}`);
const HELP_DESCRIPTION = "Display help for command";
const program = new Command()
.name("clawhub")
.description(styleEnvBlock("install, update, search, and publish skills plus OpenClaw packages."))
.version(getCliVersion(), "-V, --cli-version", "Show CLI version")
.helpOption("-h, --help", HELP_DESCRIPTION)
.option("--workdir <dir>", "Working directory (default: cwd)")
.option("--dir <dir>", "Skills directory (relative to workdir, default: skills)")
.option("--site <url>", "Site base URL (for browser login)")
.option("--registry <url>", "Registry API base URL")
.option("--no-input", "Disable prompts")
.showHelpAfterError()
.showSuggestionAfterError()
.addHelpCommand("help [command]", HELP_DESCRIPTION)
.addHelpText("after", styleEnvBlock("\nEnv:\n CLAWHUB_SITE\n CLAWHUB_REGISTRY\n CLAWHUB_WORKDIR\n (CLAWDHUB_* supported)\n"));
configureCommanderHelp(program);
addCliHelpHeader(program);
program.configureOutput({
outputError: (message, write) => write(styleError(message)),
});
function addCliHelpHeader(command) {
command.addHelpText("beforeAll", `${CLI_HELP_HEADER}\n`);
}
function registerCommand(parent, path) {
const command = parent.command(path.at(-1) ?? "").helpOption("-h, --help", HELP_DESCRIPTION);
configureCommanderHelp(command);
return command;
}
function registerCommandGroup(parent, path) {
const command = parent.command(path.at(-1) ?? "").helpOption("-h, --help", HELP_DESCRIPTION);
configureCommanderHelp(command);
return command;
}
function applyCommandHelpGroups(parent, groups) {
for (const command of parent.commands) {
const group = groups[command.name()];
if (group)
command.helpGroup(group);
}
}
function validateTopLevelCommand(args) {
if (hasTerminalGlobalFlag(args))
return;
const commandName = findFirstTopLevelOperand(args);
if (!commandName)
return;
const knownCommands = new Set([
"help",
...program.commands.flatMap((command) => [command.name(), ...command.aliases()]),
]);
if (knownCommands.has(commandName))
return;
program.error(`error: unknown command '${commandName}'`, { code: "commander.unknownCommand" });
}
function hasTerminalGlobalFlag(args) {
return args.some((arg) => arg === "--help" || arg === "-h" || arg === "--cli-version" || arg === "-V");
}
function findFirstTopLevelOperand(args) {
for (let index = 0; index < args.length; index += 1) {
const arg = args[index];
if (!arg)
continue;
if (arg === "--")
return args[index + 1];
if (arg.startsWith("--")) {
if (arg === "--workdir" || arg === "--dir" || arg === "--site" || arg === "--registry") {
index += 1;
continue;
}
if (arg.startsWith("--workdir=") ||
arg.startsWith("--dir=") ||
arg.startsWith("--site=") ||
arg.startsWith("--registry=")) {
continue;
}
if (arg === "--help" || arg === "--cli-version")
return undefined;
if (arg === "--no-input")
continue;
return undefined;
}
if (arg.startsWith("-")) {
if (arg === "-h" || arg === "-V")
return undefined;
return undefined;
}
return arg;
}
return undefined;
}
async function resolveGlobalOpts() {
const raw = program.opts();
const workdir = await resolveWorkdir(raw.workdir);
const dir = resolve(workdir, raw.dir ?? "skills");
const site = raw.site ?? process.env.CLAWHUB_SITE ?? process.env.CLAWDHUB_SITE ?? DEFAULT_SITE;
const registrySource = raw.registry
? "cli"
: process.env.CLAWHUB_REGISTRY || process.env.CLAWDHUB_REGISTRY
? "env"
: "default";
const registry = raw.registry ??
process.env.CLAWHUB_REGISTRY ??
process.env.CLAWDHUB_REGISTRY ??
DEFAULT_REGISTRY;
return { workdir, dir, site, registry, registrySource };
}
function isInputAllowed() {
const globalFlags = program.opts();
return globalFlags.input !== false;
}
async function resolveWorkdir(explicit) {
if (explicit?.trim())
return resolve(explicit.trim());
const envWorkdir = process.env.CLAWHUB_WORKDIR?.trim() ?? process.env.CLAWDHUB_WORKDIR?.trim();
if (envWorkdir)
return resolve(envWorkdir);
const cwd = resolve(process.cwd());
const hasMarker = await hasClawhubMarker(cwd);
if (hasMarker)
return cwd;
const clawdbotWorkspace = await resolveClawdbotDefaultWorkspace();
return clawdbotWorkspace ? resolve(clawdbotWorkspace) : cwd;
}
async function hasClawhubMarker(workdir) {
const lockfile = join(workdir, ".clawhub", "lock.json");
if (await pathExists(lockfile))
return true;
const markerDir = join(workdir, ".clawhub");
if (await pathExists(markerDir))
return true;
const legacyLockfile = join(workdir, ".clawdhub", "lock.json");
if (await pathExists(legacyLockfile))
return true;
const legacyMarkerDir = join(workdir, ".clawdhub");
return pathExists(legacyMarkerDir);
}
async function pathExists(path) {
try {
await stat(path);
return true;
}
catch {
return false;
}
}
registerCommand(program, ["login"])
.description("Log in with device flow or store a token")
.option("--token <token>", "API token")
.option("--label <label>", "Token label", "CLI device login")
.option("--no-browser", "Do not open browser (device flow prints a verification URL)")
.option("--device", "Use device flow (default)")
.action(async (options) => {
const opts = await resolveGlobalOpts();
await cmdLoginFlow(opts, options, isInputAllowed());
});
registerCommand(program, ["logout"])
.description("Remove stored token")
.action(async () => {
const opts = await resolveGlobalOpts();
await cmdLogout(opts);
});
registerCommand(program, ["whoami"])
.description("Validate token")
.action(async () => {
const opts = await resolveGlobalOpts();
await cmdWhoami(opts);
});
registerCommand(program, ["token"])
.description("Print stored API token")
.action(async () => {
await cmdToken();
});
const auth = registerCommandGroup(program, ["auth"])
.description("Authentication commands")
.showHelpAfterError()
.showSuggestionAfterError();
registerCommand(auth, ["auth", "login"])
.description("Log in with device flow or store a token")
.option("--token <token>", "API token")
.option("--label <label>", "Token label", "CLI device login")
.option("--no-browser", "Do not open browser (device flow prints a verification URL)")
.option("--device", "Use device flow (default)")
.action(async (options) => {
const opts = await resolveGlobalOpts();
await cmdLoginFlow(opts, options, isInputAllowed());
});
registerCommand(auth, ["auth", "logout"])
.description("Remove stored token")
.action(async () => {
const opts = await resolveGlobalOpts();
await cmdLogout(opts);
});
registerCommand(auth, ["auth", "whoami"])
.description("Validate token")
.action(async () => {
const opts = await resolveGlobalOpts();
await cmdWhoami(opts);
});
registerCommand(program, ["search"])
.description("Vector search skills")
.argument("<query...>", "Query string")
.option("--limit <n>", "Max results", (value) => Number.parseInt(value, 10))
.action(async (queryParts, options) => {
const opts = await resolveGlobalOpts();
const query = queryParts.join(" ").trim();
await cmdSearch(opts, query, options.limit);
});
registerCommand(program, ["install"])
.description("Install a skill into <dir>")
.argument("<skill>", "Skill to install, e.g. @openclaw/demo")
.option("--version <version>", "Version to install")
.option("--force", "Overwrite existing folder")
.option("--force-install", "Install a pending GitHub-backed skill before ClawHub scan completes")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdInstall(opts, slug, options.version, options.force, options.forceInstall);
});
registerCommand(program, ["update"])
.description("Update installed skills")
.argument("[skill]", "Skill to update, e.g. @openclaw/demo")
.option("--all", "Update all installed skills")
.option("--version <version>", "Update to specific version (single slug only)")
.option("--force", "Overwrite when local files do not match any version")
.option("--force-install", "Install a pending GitHub-backed skill before ClawHub scan completes")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdUpdate(opts, slug, options, isInputAllowed());
});
registerCommand(program, ["uninstall"])
.description("Uninstall a skill")
.argument("<skill>", "Installed skill to remove")
.option("--yes", "Skip confirmation")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdUninstall(opts, slug, options, isInputAllowed());
});
registerCommand(program, ["list"])
.description("List installed skills (tracked and manually installed)")
.action(async () => {
const opts = await resolveGlobalOpts();
await cmdList(opts);
});
registerCommand(program, ["pin"])
.description("Pin an installed skill so update commands skip it")
.argument("<skill>", "Installed skill to pin")
.option("--reason <text>", "Optional pin reason")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdPin(opts, slug, options);
});
registerCommand(program, ["unpin"])
.description("Remove a skill pin so updates can change it again")
.argument("<skill>", "Installed skill to unpin")
.action(async (slug) => {
const opts = await resolveGlobalOpts();
await cmdUnpin(opts, slug);
});
registerCommand(program, ["explore"])
.description("Browse latest updated skills from the registry")
.option("--limit <n>", "Number of skills to show (max 200)", (value) => Number.parseInt(value, 10), 25)
.option("--sort <order>", "Sort by newest, rating, downloads, or trending", "newest")
.option("--json", "Output JSON")
.action(async (options) => {
const opts = await resolveGlobalOpts();
const limit = typeof options.limit === "number" && Number.isFinite(options.limit) ? options.limit : 25;
await cmdExplore(opts, { limit, sort: options.sort, json: options.json });
});
registerCommand(program, ["inspect"])
.description("Fetch skill metadata and files without installing")
.argument("<skill>", "Skill to inspect, e.g. @openclaw/demo")
.option("--version <version>", "Version to inspect")
.option("--tag <tag>", "Tag to inspect (default: latest)")
.option("--versions", "List version history (first page)")
.option("--limit <n>", "Max versions to list (1-200)", (value) => Number.parseInt(value, 10))
.option("--files", "List files for the selected version")
.option("--file <path>", "Fetch raw file content (text <= 200KB)")
.option("--json", "Output JSON")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdInspect(opts, slug, options);
});
registerCommand(program, ["publish"])
.description("Legacy alias: publish a skill from folder")
.argument("<path>", "Skill folder path")
.option("--slug <slug>", "Published skill URL name")
.option("--name <name>", "Display name")
.option("--owner <handle>", "Publish under an org/user publisher handle")
.option("--source-owner <handle>", "Source owner handle when migrating an existing skill")
.option("--migrate-owner", "Move an existing skill to the selected owner when republishing")
.option("--version <version>", "Explicit version (defaults to 1.0.0 or next patch)")
.option("--fork-of <slug[@version]>", "Mark as a fork of an existing skill")
.option("--changelog <text>", "Changelog text")
.option("--tags <tags>", "Comma-separated tags", "latest")
.option("--categories <slugs>", "Comma-separated category slugs")
.option("--topics <topics>", "Comma-separated topics")
.option("--dry-run", "Preview without publishing")
.option("--json", "Output JSON")
.option("--source-repo <repo>", "GitHub source repository")
.option("--source-commit <sha>", "GitHub source commit")
.option("--source-ref <ref>", "GitHub source ref")
.option("--source-path <path>", "Path to the skill within the source repository")
.action(async (folder, options) => {
const opts = await resolveGlobalOpts();
await cmdPublish(opts, folder, options);
});
const scanCmd = registerCommand(program, ["scan"])
.description("Run or download ClawHub scan reports")
.argument("[path]", "Deprecated local skill folder path")
.option("--slug <slug>", "Published skill slug to scan")
.option("--version <version>", "Published skill version to scan")
.option("--update", "Write published scan results back to the selected version")
.option("-o, --output <path>", "Write the full report ZIP to a file")
.option("--json", "Output scan report JSON")
.action(async (folder, options) => {
const opts = await resolveGlobalOpts();
await cmdScan(opts, folder, options);
});
registerCommand(scanCmd, ["scan", "download"])
.description("Download stored scan results for a submitted skill or plugin version")
.argument("<name>", "Skill slug or plugin package name")
.option("--version <version>", "Submitted version to download scan results for")
.option("--kind <kind>", "Artifact kind: skill or plugin", "skill")
.option("-o, --output <path>", "Output ZIP file")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
const parentOptions = scanCmd.opts();
await cmdScanDownload(opts, name, {
...options,
version: options.version ?? parentOptions.version,
output: options.output ?? parentOptions.output,
});
});
registerCommand(program, ["delete"])
.description("Soft-delete a skill or permanently delete one version")
.argument("<skill>", "Skill ref")
.option("--version <version>", "Permanently delete one version; cannot be restored or republished; publish a replacement first if deleting the current latest version")
.option("--reason <text>", "Whole-skill moderation note/reason")
.option("--note <text>", "Alias for --reason")
.option("--yes", "Skip confirmation")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdDeleteSkill(opts, slug, options, isInputAllowed());
});
registerCommand(program, ["hide"])
.description("Hide one of your skills")
.argument("<skill>", "Skill to hide")
.option("--reason <text>", "Moderation note/reason")
.option("--note <text>", "Alias for --reason")
.option("--yes", "Skip confirmation")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdHideSkill(opts, slug, options, isInputAllowed());
});
registerCommand(program, ["undelete"])
.description("Restore one of your hidden skills")
.argument("<skill>", "Skill to restore")
.option("--reason <text>", "Moderation note/reason")
.option("--note <text>", "Alias for --reason")
.option("--yes", "Skip confirmation")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdUndeleteSkill(opts, slug, options, isInputAllowed());
});
registerCommand(program, ["unhide"])
.description("Unhide one of your skills")
.argument("<skill>", "Skill to unhide")
.option("--reason <text>", "Moderation note/reason")
.option("--note <text>", "Alias for --reason")
.option("--yes", "Skip confirmation")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdUnhideSkill(opts, slug, options, isInputAllowed());
});
const skill = registerCommandGroup(program, ["skill"]).description("Manage published skills");
registerCommand(skill, ["skill", "publish"])
.description("Publish a skill from folder")
.argument("<path>", "Skill folder path")
.option("--slug <slug>", "Published skill URL name")
.option("--name <name>", "Display name")
.option("--owner <handle>", "Publish under an org/user publisher handle")
.option("--source-owner <handle>", "Source owner handle when migrating an existing skill")
.option("--migrate-owner", "Move an existing skill to the selected owner when republishing")
.option("--version <version>", "Explicit version (defaults to 1.0.0 or next patch)")
.option("--fork-of <slug[@version]>", "Mark as a fork of an existing skill")
.option("--changelog <text>", "Changelog text")
.option("--tags <tags>", "Comma-separated tags", "latest")
.option("--categories <slugs>", "Comma-separated category slugs")
.option("--topics <topics>", "Comma-separated topics")
.option("--dry-run", "Preview without publishing")
.option("--json", "Output JSON")
.option("--source-repo <repo>", "GitHub source repository")
.option("--source-commit <sha>", "GitHub source commit")
.option("--source-ref <ref>", "GitHub source ref")
.option("--source-path <path>", "Path to the skill within the source repository")
.action(async (folder, options) => {
const opts = await resolveGlobalOpts();
await cmdPublish(opts, folder, options);
});
registerCommand(skill, ["skill", "verify"])
.description("Verify a published skill using ClawHub security evidence")
.argument("<slug>", "Skill slug")
.option("--version <version>", "Version to verify")
.option("--tag <tag>", "Tag to verify")
.option("--card", "Output generated skill-card.md Markdown")
.addOption(new Option("--json", "Output JSON").hideHelp())
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdVerifySkill(opts, slug, options);
});
const publisherCmd = registerCommandGroup(program, ["publisher"])
.description("Publisher organization commands")
.showHelpAfterError()
.showSuggestionAfterError();
registerCommand(publisherCmd, ["publisher", "create"])
.description("Create an org publisher you own")
.argument("<handle>", "Publisher handle, for example example.tools")
.option("--display-name <name>", "Publisher display name")
.option("--json", "Output JSON")
.action(async (handle, options) => {
const opts = await resolveGlobalOpts();
await cmdCreatePublisher(opts, handle, options);
});
const packageCmd = registerCommandGroup(program, ["package"]).description("Browse and publish OpenClaw packages");
registerCommand(packageCmd, ["package", "explore"])
.description("Browse published packages and plugins")
.argument("[query...]", "Optional search query")
.option("--family <family>", "skill|code-plugin|bundle-plugin")
.option("--official", "Only official packages")
.option("--executes-code", "Only packages that execute code")
.option("--target <target>", "Filter by host target, e.g. darwin-arm64")
.option("--os <os>", "Filter by host OS, e.g. darwin, linux, win32")
.option("--arch <arch>", "Filter by host architecture, e.g. arm64 or x64")
.option("--libc <libc>", "Filter by libc, e.g. glibc or musl")
.option("--requires-browser", "Only packages that require a browser")
.option("--requires-desktop", "Only packages that require local desktop access")
.option("--requires-native-deps", "Only packages with native dependency requirements")
.option("--requires-external-service", "Only packages that require an external service")
.option("--external-service <name>", "Filter by named external service")
.option("--binary <name>", "Filter by required local binary")
.option("--os-permission <name>", "Filter by required OS permission")
.option("--artifact-kind <kind>", "legacy-zip|npm-pack")
.option("--npm-mirror", "Only packages available through the npm mirror")
.option("--limit <n>", "Number of packages to show (max 100)", (value) => Number.parseInt(value, 10), 25)
.option("--json", "Output JSON")
.action(async (queryParts, options) => {
const opts = await resolveGlobalOpts();
const query = Array.isArray(queryParts) ? queryParts.join(" ").trim() : "";
await cmdExplorePackages(opts, query, options);
});
registerCommand(packageCmd, ["package", "inspect"])
.description("Fetch package metadata and files without installing")
.argument("<name>", "Package name")
.option("--version <version>", "Version to inspect")
.option("--tag <tag>", "Tag to inspect (default: latest)")
.option("--versions", "List version history (first page)")
.option("--limit <n>", "Max versions to list (1-100)", (value) => Number.parseInt(value, 10))
.option("--files", "List files for the selected version")
.option("--file <path>", "Fetch raw file content (text only)")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdInspectPackage(opts, name, options);
});
registerCommand(packageCmd, ["package", "download"])
.description("Download a package artifact and verify its published digests")
.argument("<name>", "Package name")
.option("--version <version>", "Version to download")
.option("--tag <tag>", "Tag to download (default: latest)")
.option("-o, --output <path>", "Output file or directory")
.option("--force", "Overwrite existing output file")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdDownloadPackage(opts, name, options);
});
registerCommand(packageCmd, ["package", "verify"])
.description("Verify a local package artifact against ClawHub or expected digests")
.argument("<file>", "Artifact file")
.option("--package <name>", "Package name to resolve expected artifact metadata")
.option("--version <version>", "Package version to resolve")
.option("--tag <tag>", "Package tag to resolve")
.option("--sha256 <hex>", "Expected ClawHub SHA-256")
.option("--npm-integrity <sri>", "Expected npm sha512 integrity")
.option("--npm-shasum <sha1>", "Expected npm shasum")
.option("--json", "Output JSON")
.action(async (file, options) => {
const opts = await resolveGlobalOpts();
await cmdVerifyPackage(opts, file, {
...options,
packageName: options.package,
});
});
registerCommand(packageCmd, ["package", "validate"])
.description("Validate a local plugin package with the bundled Plugin Inspector")
.argument("<source>", "Package folder path")
.option("--out <dir>", "Directory for Plugin Inspector reports", "reports")
.option("--openclaw <path>", "Optional local OpenClaw checkout to inspect against")
.option("--runtime", "Enable runtime capture; imports plugin code")
.option("--allow-execute", "Allow runtime capture in an isolated workspace")
.option("--no-mock-sdk", "Disable mocked OpenClaw SDK during runtime capture")
.option("--json", "Output JSON")
.action(async (source, options) => {
const opts = await resolveGlobalOpts();
await cmdValidatePackage(opts, source, options);
});
registerCommand(packageCmd, ["package", "delete"])
.description("Soft-delete a package or permanently delete one version")
.argument("<name>", "Package name")
.option("--version <version>", "Permanently delete one version; cannot be restored or republished; publish a replacement first if deleting the current latest version")
.option("--yes", "Skip confirmation")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdDeletePackage(opts, name, options, isInputAllowed());
});
registerCommand(packageCmd, ["package", "undelete"])
.description("Restore a soft-deleted package and releases")
.argument("<name>", "Package name")
.option("--yes", "Skip confirmation")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdUndeletePackage(opts, name, options, isInputAllowed());
});
registerCommand(packageCmd, ["package", "transfer"])
.description("Transfer a plugin package to another publisher")
.argument("<name>", "Package name")
.requiredOption("--to <owner>", "Destination publisher handle")
.option("--reason <text>", "Audit reason")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdTransferPackage(opts, name, options);
});
registerCommand(packageCmd, ["package", "report"])
.description("Report a package for moderator review")
.argument("<name>", "Package name")
.option("--version <version>", "Package version")
.requiredOption("--reason <text>", "Report reason")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdReportPackage(opts, name, options);
});
registerCommand(packageCmd, ["package", "moderation-status"])
.description("Show package moderation status")
.argument("<name>", "Package name")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdPackageModerationStatus(opts, name, options);
});
registerCommand(packageCmd, ["package", "readiness"])
.description("Check package readiness for future OpenClaw consumption")
.argument("<name>", "Package name")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdPackageReadiness(opts, name, options);
});
registerCommand(packageCmd, ["package", "migration-status"])
.description("Show package migration status for future OpenClaw consumption")
.argument("<name>", "Package name")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdPackageMigrationStatus(opts, name, options);
});
registerCommand(packageCmd, ["package", "pack"])
.description("Create a ClawPack npm tarball from a plugin package folder")
.argument("<source>", "Package folder path")
.option("--pack-destination <dir>", "Directory for the generated .tgz (default: workdir)")
.option("--json", "Output JSON")
.action(async (source, options) => {
const opts = await resolveGlobalOpts();
await cmdPackPackage(opts, source, options);
});
registerCommand(packageCmd, ["package", "publish"])
.description("Publish a code plugin or bundle plugin from a folder or GitHub source")
.argument("<source>", "Package folder path, GitHub repo (owner/repo[@ref]), or URL")
.option("--family <family>", "code-plugin|bundle-plugin")
.option("--name <name>", "Package name")
.option("--display-name <name>", "Display name")
.option("--owner <handle>", "Publish under this owner/publisher handle")
.option("--version <version>", "Version")
.option("--changelog <text>", "Changelog text")
.option("--manual-override-reason <reason>", "Required for manual publish when trusted publisher config exists")
.option("--tags <tags>", "Comma-separated tags", "latest")
.option("--categories <slugs>", "Comma-separated category slugs")
.option("--topics <topics>", "Comma-separated topics")
.option("--bundle-format <format>", "Bundle format")
.option("--host-targets <targets>", "Comma-separated bundle host targets")
.option("--source-repo <repo>", "GitHub repo (owner/repo or URL)")
.option("--source-commit <sha>", "Git commit SHA")
.option("--source-ref <ref>", "Git ref/tag/branch")
.option("--source-path <path>", "Repo subpath")
.option("--dry-run", "Preview what would be published without uploading")
.option("--json", "Output JSON (for CI pipelines)")
.action(async (source, options) => {
const opts = await resolveGlobalOpts();
await cmdPublishPackage(opts, source, options);
});
const trustedPublisherCmd = registerCommandGroup(packageCmd, [
"package",
"trusted-publisher",
]).description("Manage package trusted publisher config");
registerCommand(trustedPublisherCmd, ["package", "trusted-publisher", "get"])
.description("Show trusted publisher config for a package")
.argument("<name>", "Package name")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdGetPackageTrustedPublisher(opts, name, options);
});
registerCommand(trustedPublisherCmd, ["package", "trusted-publisher", "set"])
.description("Set trusted publisher config for a package")
.argument("<name>", "Package name")
.requiredOption("--repository <repo>", "GitHub repository, for example openclaw/openclaw")
.requiredOption("--workflow-filename <file>", "GitHub Actions workflow filename")
.option("--environment <name>", "GitHub Actions environment name")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdSetPackageTrustedPublisher(opts, name, options);
});
registerCommand(trustedPublisherCmd, ["package", "trusted-publisher", "delete"])
.description("Delete trusted publisher config for a package")
.argument("<name>", "Package name")
.option("--json", "Output JSON")
.action(async (name, options) => {
const opts = await resolveGlobalOpts();
await cmdDeletePackageTrustedPublisher(opts, name, options);
});
registerCommand(skill, ["skill", "rename"])
.description("Rename a published skill and keep the old slug as a redirect")
.argument("<skill>", "Current skill")
.argument("<new-slug>", "New canonical slug")
.option("--yes", "Skip confirmation")
.action(async (slug, newSlug, options) => {
const opts = await resolveGlobalOpts();
await cmdRenameSkill(opts, slug, newSlug, options, isInputAllowed());
});
registerCommand(skill, ["skill", "merge"])
.description("Merge one owned skill into another and redirect the old slug")
.argument("<source>", "Source skill")
.argument("<target>", "Target skill")
.option("--yes", "Skip confirmation")
.action(async (sourceSlug, targetSlug, options) => {
const opts = await resolveGlobalOpts();
await cmdMergeSkill(opts, sourceSlug, targetSlug, options, isInputAllowed());
});
const transfer = registerCommandGroup(program, ["transfer"]).description("Transfer skill ownership");
registerCommand(transfer, ["transfer", "request"])
.description("Request skill transfer to another user")
.argument("<skill>", "Skill to transfer")
.argument("<handle>", "Recipient handle (e.g., @username)")
.option("--message <text>", "Optional message for recipient")
.option("--yes", "Skip confirmation")
.action(async (slug, handle, options) => {
const opts = await resolveGlobalOpts();
await cmdTransferRequest(opts, slug, handle, options, isInputAllowed());
});
registerCommand(transfer, ["transfer", "list"])
.description("List pending transfer requests")
.option("--outgoing", "Show outgoing transfer requests")
.action(async (options) => {
const opts = await resolveGlobalOpts();
await cmdTransferList(opts, options);
});
registerCommand(transfer, ["transfer", "accept"])
.description("Accept incoming transfer for a skill")
.argument("<skill>", "Skill to accept")
.option("--yes", "Skip confirmation")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdTransferAccept(opts, slug, options, isInputAllowed());
});
registerCommand(transfer, ["transfer", "reject"])
.description("Reject incoming transfer for a skill")
.argument("<skill>", "Skill to reject")
.option("--yes", "Skip confirmation")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdTransferReject(opts, slug, options, isInputAllowed());
});
registerCommand(transfer, ["transfer", "cancel"])
.description("Cancel outgoing transfer for a skill")
.argument("<skill>", "Skill to cancel")
.option("--yes", "Skip confirmation")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdTransferCancel(opts, slug, options, isInputAllowed());
});
registerCommand(program, ["star"])
.description("Add a skill to your highlights")
.argument("<skill>", "Skill to highlight")
.option("--yes", "Skip confirmation")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdStarSkill(opts, slug, options, isInputAllowed());
});
registerCommand(program, ["unstar"])
.description("Remove a skill from your highlights")
.argument("<skill>", "Skill to remove from highlights")
.option("--yes", "Skip confirmation")
.action(async (slug, options) => {
const opts = await resolveGlobalOpts();
await cmdUnstarSkill(opts, slug, options, isInputAllowed());
});
registerCommand(program, ["sync"])
.description("Scan local skills and publish new or changed ones")
.option("--root <dir...>", "Extra scan roots (one or more)")
.option("--all", "Publish all new or changed skills without prompting")
.option("--dry-run", "Show what would be published")
.option("--json", "Output JSON")
.option("--owner <handle>", "Publish under an org/user publisher handle")
.option("--bump <type>", "Version bump for updates (patch|minor|major)", "patch")
.option("--changelog <text>", "Changelog to use for updates")
.option("--tags <tags>", "Comma-separated tags", "latest")
.option("--concurrency <n>", "Concurrent registry/file checks", (value) => Number.parseInt(value, 10))
.option("--source-repo <repo>", "GitHub repo URL or owner/name for source provenance")
.option("--source-commit <sha>", "Git commit SHA for source provenance")
.option("--source-ref <ref>", "Git ref for source provenance")
.action(async (options) => {
const opts = await resolveGlobalOpts();
const bump = options.bump === "patch" || options.bump === "minor" || options.bump === "major"
? options.bump
: fail("--bump must be patch, minor, or major");
const concurrency = options.concurrency ?? 6;
if (concurrency < 1 || concurrency > 32)
fail("--concurrency must be between 1 and 32");
await cmdSync(opts, {
root: options.root,
all: options.all,
dryRun: options.dryRun,
json: options.json,
owner: options.owner,
bump,
changelog: options.changelog,
tags: options.tags,
concurrency,
sourceRepo: options.sourceRepo,
sourceCommit: options.sourceCommit,
sourceRef: options.sourceRef,
}, isInputAllowed());
});
applyCommandHelpGroups(program, {
login: "Auth:",
logout: "Auth:",
whoami: "Auth:",
auth: "Auth:",
search: "Skills:",
install: "Skills:",
update: "Skills:",
uninstall: "Skills:",
list: "Skills:",
pin: "Skills:",
unpin: "Skills:",
explore: "Skills:",
inspect: "Skills:",
star: "Skills:",
unstar: "Skills:",
publish: "Publishing:",
sync: "Publishing:",
skill: "Publishing:",
publisher: "Publishing:",
package: "Packages:",
delete: "Moderation:",
hide: "Moderation:",
undelete: "Moderation:",
unhide: "Moderation:",
transfer: "Moderation:",
help: "Help:",
});
applyCommandHelpGroups(packageCmd, {
explore: "Discovery:",
inspect: "Discovery:",
download: "Artifacts:",
verify: "Artifacts:",
pack: "Publishing:",
publish: "Publishing:",
"trusted-publisher": "Publishing:",
delete: "Moderation:",
undelete: "Moderation:",
transfer: "Moderation:",
report: "Moderation:",
"moderation-status": "Moderation:",
readiness: "Operations:",
"migration-status": "Operations:",
});
program.action(() => {
program.outputHelp();
process.exitCode = 0;
});
validateTopLevelCommand(process.argv.slice(2));
void program.parseAsync(process.argv).catch((error) => {
fail(formatError(error));
});
//# sourceMappingURL=cli.js.map