mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-28 10:22:32 +00:00
check: restore bundled channel config metadata gate
This commit is contained in:
@@ -682,17 +682,17 @@
|
||||
"canon:check:json": "node scripts/canon.mjs check --json",
|
||||
"canon:enforce": "node scripts/canon.mjs enforce --json",
|
||||
"canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh",
|
||||
"check": "pnpm check:no-conflict-markers && pnpm check:host-env-policy:swift && pnpm check:base-config-schema && pnpm check:bundled-plugin-metadata && pnpm check:bundled-provider-auth-env-vars && pnpm format:check && pnpm tsgo && pnpm plugin-sdk:check-exports && pnpm lint && pnpm lint:tmp:no-random-messaging && pnpm lint:tmp:channel-agnostic-boundaries && pnpm lint:tmp:no-raw-channel-fetch && pnpm lint:agent:ingress-owner && pnpm lint:plugins:no-register-http-handler && pnpm lint:plugins:no-monolithic-plugin-sdk-entry-imports && pnpm lint:plugins:no-extension-src-imports && pnpm lint:plugins:no-extension-test-core-imports && pnpm lint:plugins:no-extension-imports && pnpm lint:plugins:plugin-sdk-subpaths-exported && pnpm lint:extensions:no-src-outside-plugin-sdk && pnpm lint:extensions:no-plugin-sdk-internal && pnpm lint:extensions:no-relative-outside-package && pnpm lint:web-search-provider-boundaries && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope",
|
||||
"check": "pnpm check:no-conflict-markers && pnpm check:host-env-policy:swift && pnpm check:bundled-channel-config-metadata && pnpm check:base-config-schema && pnpm check:bundled-plugin-metadata && pnpm check:bundled-provider-auth-env-vars && pnpm format:check && pnpm tsgo && pnpm plugin-sdk:check-exports && pnpm lint && pnpm lint:tmp:no-random-messaging && pnpm lint:tmp:channel-agnostic-boundaries && pnpm lint:tmp:no-raw-channel-fetch && pnpm lint:agent:ingress-owner && pnpm lint:plugins:no-register-http-handler && pnpm lint:plugins:no-monolithic-plugin-sdk-entry-imports && pnpm lint:plugins:no-extension-src-imports && pnpm lint:plugins:no-extension-test-core-imports && pnpm lint:plugins:no-extension-imports && pnpm lint:plugins:plugin-sdk-subpaths-exported && pnpm lint:extensions:no-src-outside-plugin-sdk && pnpm lint:extensions:no-plugin-sdk-internal && pnpm lint:extensions:no-relative-outside-package && pnpm lint:web-search-provider-boundaries && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope",
|
||||
"check:base-config-schema": "node --import tsx scripts/generate-base-config-schema.ts --check",
|
||||
"check:bundled-channel-config-metadata": "node scripts/generate-bundled-plugin-metadata.mjs --check",
|
||||
"check:bundled-channel-config-metadata": "node --import tsx scripts/generate-bundled-channel-config-metadata.ts --check",
|
||||
"check:bundled-plugin-metadata": "node scripts/generate-bundled-plugin-metadata.mjs --check",
|
||||
"check:bundled-provider-auth-env-vars": "node scripts/generate-bundled-provider-auth-env-vars.mjs --check",
|
||||
"check:docs": "pnpm format:docs:check && pnpm lint:docs && pnpm docs:check-i18n-glossary && pnpm docs:check-links",
|
||||
"check:host-env-policy:swift": "node scripts/generate-host-env-security-policy-swift.mjs --check",
|
||||
"check:loc": "node --import tsx scripts/check-ts-max-loc.ts --max 500",
|
||||
"check:no-conflict-markers": "node scripts/check-no-conflict-markers.mjs",
|
||||
"config:channels:check": "node scripts/generate-bundled-plugin-metadata.mjs --check",
|
||||
"config:channels:gen": "node scripts/generate-bundled-plugin-metadata.mjs",
|
||||
"config:channels:check": "node --import tsx scripts/generate-bundled-channel-config-metadata.ts --check",
|
||||
"config:channels:gen": "node --import tsx scripts/generate-bundled-channel-config-metadata.ts --write",
|
||||
"config:docs:check": "node --import tsx scripts/generate-config-doc-baseline.ts --check",
|
||||
"config:docs:gen": "node --import tsx scripts/generate-config-doc-baseline.ts --write",
|
||||
"config:schema:check": "node --import tsx scripts/generate-base-config-schema.ts --check",
|
||||
|
||||
212
scripts/generate-bundled-channel-config-metadata.ts
Normal file
212
scripts/generate-bundled-channel-config-metadata.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env node
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { loadChannelConfigSurfaceModule } from "./load-channel-config-surface.ts";
|
||||
|
||||
const GENERATED_BY = "scripts/generate-bundled-channel-config-metadata.ts";
|
||||
const DEFAULT_OUTPUT_PATH = "src/config/bundled-channel-config-metadata.generated.ts";
|
||||
|
||||
type BundledPluginSource = {
|
||||
dirName: string;
|
||||
pluginDir: string;
|
||||
manifestPath: string;
|
||||
manifest: {
|
||||
id: string;
|
||||
channels?: unknown;
|
||||
name?: string;
|
||||
description?: string;
|
||||
} & Record<string, unknown>;
|
||||
packageJson?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
const { collectBundledPluginSources } = (await import(
|
||||
new URL("./lib/bundled-plugin-source-utils.mjs", import.meta.url).href
|
||||
)) as {
|
||||
collectBundledPluginSources: (params?: {
|
||||
repoRoot?: string;
|
||||
requirePackageJson?: boolean;
|
||||
}) => BundledPluginSource[];
|
||||
};
|
||||
|
||||
const { formatGeneratedModule } = (await import(
|
||||
new URL("./lib/format-generated-module.mjs", import.meta.url).href
|
||||
)) as {
|
||||
formatGeneratedModule: (
|
||||
source: string,
|
||||
options: {
|
||||
repoRoot: string;
|
||||
outputPath: string;
|
||||
errorLabel: string;
|
||||
},
|
||||
) => string;
|
||||
};
|
||||
|
||||
const { writeGeneratedOutput } = (await import(
|
||||
new URL("./lib/generated-output-utils.mjs", import.meta.url).href
|
||||
)) as {
|
||||
writeGeneratedOutput: (params: {
|
||||
repoRoot: string;
|
||||
outputPath: string;
|
||||
next: string;
|
||||
check?: boolean;
|
||||
}) => {
|
||||
changed: boolean;
|
||||
wrote: boolean;
|
||||
outputPath: string;
|
||||
};
|
||||
};
|
||||
|
||||
type BundledChannelConfigMetadata = {
|
||||
pluginId: string;
|
||||
channelId: string;
|
||||
label?: string;
|
||||
description?: string;
|
||||
schema: Record<string, unknown>;
|
||||
uiHints?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
function resolveChannelConfigSchemaModulePath(rootDir: string): string | null {
|
||||
const candidates = [
|
||||
path.join(rootDir, "src", "config-schema.ts"),
|
||||
path.join(rootDir, "src", "config-schema.js"),
|
||||
path.join(rootDir, "src", "config-schema.mts"),
|
||||
path.join(rootDir, "src", "config-schema.mjs"),
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function resolvePackageChannelMeta(source: BundledPluginSource) {
|
||||
const openclawMeta =
|
||||
source.packageJson &&
|
||||
typeof source.packageJson === "object" &&
|
||||
!Array.isArray(source.packageJson) &&
|
||||
"openclaw" in source.packageJson
|
||||
? (source.packageJson.openclaw as Record<string, unknown> | undefined)
|
||||
: undefined;
|
||||
const channelMeta =
|
||||
openclawMeta &&
|
||||
typeof openclawMeta.channel === "object" &&
|
||||
openclawMeta.channel &&
|
||||
!Array.isArray(openclawMeta.channel)
|
||||
? (openclawMeta.channel as Record<string, unknown>)
|
||||
: undefined;
|
||||
return channelMeta;
|
||||
}
|
||||
|
||||
function resolveRootLabel(source: BundledPluginSource, channelId: string): string | undefined {
|
||||
const channelMeta = resolvePackageChannelMeta(source);
|
||||
if (channelMeta?.id === channelId && typeof channelMeta.label === "string") {
|
||||
return channelMeta.label.trim();
|
||||
}
|
||||
if (typeof source.manifest?.name === "string" && source.manifest.name.trim()) {
|
||||
return source.manifest.name.trim();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveRootDescription(
|
||||
source: BundledPluginSource,
|
||||
channelId: string,
|
||||
): string | undefined {
|
||||
const channelMeta = resolvePackageChannelMeta(source);
|
||||
if (channelMeta?.id === channelId && typeof channelMeta.blurb === "string") {
|
||||
return channelMeta.blurb.trim();
|
||||
}
|
||||
if (typeof source.manifest?.description === "string" && source.manifest.description.trim()) {
|
||||
return source.manifest.description.trim();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function formatTypeScriptModule(source: string, outputPath: string, repoRoot: string): string {
|
||||
return formatGeneratedModule(source, {
|
||||
repoRoot,
|
||||
outputPath,
|
||||
errorLabel: "bundled channel config metadata",
|
||||
});
|
||||
}
|
||||
|
||||
export async function collectBundledChannelConfigMetadata(params?: { repoRoot?: string }) {
|
||||
const repoRoot = path.resolve(params?.repoRoot ?? process.cwd());
|
||||
const sources = collectBundledPluginSources({ repoRoot, requirePackageJson: true });
|
||||
const entries: BundledChannelConfigMetadata[] = [];
|
||||
|
||||
for (const source of sources) {
|
||||
const channelIds = Array.isArray(source.manifest?.channels)
|
||||
? source.manifest.channels.filter(
|
||||
(entry: unknown): entry is string => typeof entry === "string" && entry.trim().length > 0,
|
||||
)
|
||||
: [];
|
||||
if (channelIds.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const modulePath = resolveChannelConfigSchemaModulePath(source.pluginDir);
|
||||
if (!modulePath) {
|
||||
continue;
|
||||
}
|
||||
const surface = await loadChannelConfigSurfaceModule(modulePath, { repoRoot });
|
||||
if (!surface?.schema) {
|
||||
continue;
|
||||
}
|
||||
for (const channelId of channelIds) {
|
||||
const label = resolveRootLabel(source, channelId);
|
||||
const description = resolveRootDescription(source, channelId);
|
||||
entries.push({
|
||||
pluginId: String(source.manifest.id),
|
||||
channelId,
|
||||
...(label ? { label } : {}),
|
||||
...(description ? { description } : {}),
|
||||
schema: surface.schema,
|
||||
...(Object.keys(surface.uiHints ?? {}).length > 0 ? { uiHints: surface.uiHints } : {}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return entries.toSorted((left, right) => left.channelId.localeCompare(right.channelId));
|
||||
}
|
||||
|
||||
export async function writeBundledChannelConfigMetadataModule(params?: {
|
||||
repoRoot?: string;
|
||||
outputPath?: string;
|
||||
check?: boolean;
|
||||
}) {
|
||||
const repoRoot = path.resolve(params?.repoRoot ?? process.cwd());
|
||||
const outputPath = params?.outputPath ?? DEFAULT_OUTPUT_PATH;
|
||||
const entries = await collectBundledChannelConfigMetadata({ repoRoot });
|
||||
const next = formatTypeScriptModule(
|
||||
`// Auto-generated by ${GENERATED_BY}. Do not edit directly.
|
||||
|
||||
export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = ${JSON.stringify(entries, null, 2)} as const;
|
||||
`,
|
||||
outputPath,
|
||||
repoRoot,
|
||||
);
|
||||
return writeGeneratedOutput({
|
||||
repoRoot,
|
||||
outputPath,
|
||||
next,
|
||||
check: params?.check,
|
||||
});
|
||||
}
|
||||
|
||||
if (import.meta.url === new URL(process.argv[1] ?? "", "file://").href) {
|
||||
const check = process.argv.includes("--check");
|
||||
const result = await writeBundledChannelConfigMetadataModule({ check });
|
||||
if (!result.changed) {
|
||||
process.exitCode = 0;
|
||||
} else if (check) {
|
||||
console.error(
|
||||
`[bundled-channel-config-metadata] stale generated output at ${path.relative(process.cwd(), result.outputPath)}`,
|
||||
);
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
console.log(
|
||||
`[bundled-channel-config-metadata] wrote ${path.relative(process.cwd(), result.outputPath)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
14583
src/config/bundled-channel-config-metadata.generated.ts
Normal file
14583
src/config/bundled-channel-config-metadata.generated.ts
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user