Download src/plugins/bundle-commands.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 6.12 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/plugins/bundle-commands.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/plugins/bundle-commands.ts
-
curl -L -o bundle-commands.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/plugins/bundle-commands.ts
6.12 kB
| // Bundles plugin command metadata for package output. | |
| import fs from "node:fs"; | |
| import path from "node:path"; | |
| import { | |
| normalizeOptionalLowercaseString, | |
| normalizeOptionalString, | |
| } from "@openclaw/normalization-core/string-coerce"; | |
| import { | |
| parseFrontmatterBlock, | |
| stripFrontmatterBlock, | |
| } from "../../packages/markdown-core/src/frontmatter.js"; | |
| import type { OpenClawConfig } from "../config/types.openclaw.js"; | |
| import { formatErrorMessage } from "../infra/errors.js"; | |
| import { readRootJsonObjectSync } from "../infra/json-files.js"; | |
| import { readRegularFileSync } from "../infra/regular-file.js"; | |
| import { createSubsystemLogger } from "../logging/subsystem.js"; | |
| import { isPathInsideWithRealpath } from "../security/scan-paths.js"; | |
| import { parseFrontmatterBool } from "../shared/frontmatter.js"; | |
| import { | |
| CLAUDE_BUNDLE_MANIFEST_RELATIVE_PATH, | |
| mergeBundlePathLists, | |
| normalizeBundlePathList, | |
| } from "./bundle-manifest.js"; | |
| import { | |
| hasExplicitPluginConfig, | |
| normalizePluginsConfig, | |
| resolveEffectivePluginActivationState, | |
| } from "./config-state.js"; | |
| import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry-contributions.js"; | |
| type ClaudeBundleCommandSpec = { | |
| pluginId: string; | |
| rawName: string; | |
| description: string; | |
| promptTemplate: string; | |
| sourceFilePath: string; | |
| }; | |
| const BUNDLE_COMMAND_MAX_BYTES = 1 * 1024 * 1024; | |
| const log = createSubsystemLogger("plugins/bundle-commands"); | |
| function readClaudeBundleManifest(rootDir: string): Record<string, unknown> { | |
| const result = readRootJsonObjectSync({ | |
| rootDir, | |
| relativePath: CLAUDE_BUNDLE_MANIFEST_RELATIVE_PATH, | |
| boundaryLabel: "plugin root", | |
| rejectHardlinks: true, | |
| }); | |
| return result.ok ? result.value : {}; | |
| } | |
| function resolveClaudeCommandRootDirs(rootDir: string): string[] { | |
| const raw = readClaudeBundleManifest(rootDir); | |
| const declared = normalizeBundlePathList(raw.commands); | |
| const defaults = fs.existsSync(path.join(rootDir, "commands")) ? ["commands"] : []; | |
| return mergeBundlePathLists(defaults, declared); | |
| } | |
| function listMarkdownFilesRecursive(rootDir: string): string[] { | |
| const pending = [rootDir]; | |
| const files: string[] = []; | |
| while (pending.length > 0) { | |
| const current = pending.pop(); | |
| if (!current) { | |
| continue; | |
| } | |
| let entries: fs.Dirent[]; | |
| try { | |
| entries = fs.readdirSync(current, { withFileTypes: true }); | |
| } catch { | |
| continue; | |
| } | |
| for (const entry of entries) { | |
| if (entry.name.startsWith(".")) { | |
| continue; | |
| } | |
| const fullPath = path.join(current, entry.name); | |
| if (entry.isDirectory()) { | |
| pending.push(fullPath); | |
| continue; | |
| } | |
| if (entry.isFile() && normalizeOptionalLowercaseString(entry.name)?.endsWith(".md")) { | |
| files.push(fullPath); | |
| } | |
| } | |
| } | |
| return files.toSorted((a, b) => a.localeCompare(b)); | |
| } | |
| function toDefaultCommandName(rootDir: string, filePath: string): string { | |
| const relativePath = path.relative(rootDir, filePath); | |
| const withoutExt = relativePath.replace(/\.[^.]+$/u, ""); | |
| return withoutExt.split(path.sep).join(":"); | |
| } | |
| function toDefaultDescription(promptTemplate: string): string { | |
| // stripFrontmatterBlock already normalized line endings and trimmed the nonempty body. | |
| const lineEnd = promptTemplate.indexOf("\n"); | |
| return (lineEnd < 0 ? promptTemplate : promptTemplate.slice(0, lineEnd)).trimEnd(); | |
| } | |
| function loadBundleCommandsFromRoot(params: { | |
| pluginId: string; | |
| commandRoot: string; | |
| }): ClaudeBundleCommandSpec[] { | |
| const entries: ClaudeBundleCommandSpec[] = []; | |
| for (const filePath of listMarkdownFilesRecursive(params.commandRoot)) { | |
| let raw: string; | |
| try { | |
| raw = readRegularFileSync({ filePath, maxBytes: BUNDLE_COMMAND_MAX_BYTES }).buffer.toString( | |
| "utf-8", | |
| ); | |
| } catch (error) { | |
| log.warn(`skipping unreadable bundle command file ${filePath}: ${formatErrorMessage(error)}`); | |
| continue; | |
| } | |
| const frontmatter = parseFrontmatterBlock(raw); | |
| if (!parseFrontmatterBool(frontmatter["user-invocable"], true)) { | |
| continue; | |
| } | |
| const promptTemplate = stripFrontmatterBlock(raw); | |
| if (!promptTemplate) { | |
| continue; | |
| } | |
| const rawName = | |
| normalizeOptionalString(frontmatter.name) || | |
| toDefaultCommandName(params.commandRoot, filePath); | |
| if (!rawName) { | |
| continue; | |
| } | |
| const description = | |
| normalizeOptionalString(frontmatter.description) || toDefaultDescription(promptTemplate); | |
| entries.push({ | |
| pluginId: params.pluginId, | |
| rawName, | |
| description, | |
| promptTemplate, | |
| sourceFilePath: filePath, | |
| }); | |
| } | |
| return entries; | |
| } | |
| export function loadEnabledClaudeBundleCommands(params: { | |
| workspaceDir: string; | |
| cfg?: OpenClawConfig; | |
| }): ClaudeBundleCommandSpec[] { | |
| if (!hasExplicitPluginConfig(params.cfg?.plugins)) { | |
| return []; | |
| } | |
| const registry = loadPluginManifestRegistryForPluginRegistry({ | |
| workspaceDir: params.workspaceDir, | |
| config: params.cfg, | |
| includeDisabled: true, | |
| }); | |
| const normalizedPlugins = normalizePluginsConfig(params.cfg?.plugins); | |
| const commands: ClaudeBundleCommandSpec[] = []; | |
| for (const record of registry.plugins) { | |
| if ( | |
| record.format !== "bundle" || | |
| record.bundleFormat !== "claude" || | |
| !(record.bundleCapabilities ?? []).includes("commands") | |
| ) { | |
| continue; | |
| } | |
| const activationState = resolveEffectivePluginActivationState({ | |
| id: record.id, | |
| origin: record.origin, | |
| channelIds: record.channels, | |
| config: normalizedPlugins, | |
| rootConfig: params.cfg, | |
| }); | |
| if (!activationState.activated) { | |
| continue; | |
| } | |
| for (const relativeRoot of resolveClaudeCommandRootDirs(record.rootDir)) { | |
| const commandRoot = path.resolve(record.rootDir, relativeRoot); | |
| if (!fs.existsSync(commandRoot)) { | |
| continue; | |
| } | |
| if (!isPathInsideWithRealpath(record.rootDir, commandRoot, { requireRealpath: true })) { | |
| continue; | |
| } | |
| commands.push(...loadBundleCommandsFromRoot({ pluginId: record.id, commandRoot })); | |
| } | |
| } | |
| return commands; | |
| } | |