cli: add orca plugin create, validate and inspect (#36280)

* cli: add orca plugin create, validate and inspect

* build: ship orca plugin validate's main checks as a CLI main entry
This commit is contained in:
Neil
2026-10-30 01:00:15 -07:00
committed by GitHub
parent 5dd43d0146
commit 1963ac0ea8
27 changed files with 1154 additions and 77 deletions
@@ -197,6 +197,7 @@ describe('Electron Vite output contract', () => {
'persistence/profile-state/legacy-json/profile-state-recovery',
'persistence/profile-state/profile-state-recovery-command',
'persistence/profile-state/profile-state-storage-classification',
'plugins/plugin-authoring-checks',
'skills/bundled-skill-install/cli-entry',
'startup/http1-compatibility-marker'
]) {
+9 -69
View File
@@ -20,77 +20,21 @@ const { materializePluginGitTree } = loadPluginSchemaModule(
)
const { hashPluginTree } = loadPluginSchemaModule('../../main/plugins/plugin-content-hash.ts')
const { gitExecFileAsync } = loadPluginSchemaModule('../../main/git/runner.ts')
const { formatPluginSchemaIssues } = loadPluginSchemaModule('manifest/plugin-schema-issues.ts')
const { isPluginRepositoryPath } = loadPluginSchemaModule('installation/plugin-path-safety.ts')
const { parseGitRepositoryIdentity } = loadPluginSchemaModule('catalog/plugin-marketplace.ts')
const { catalogImagesFromManifest } = loadPluginSchemaModule(
'images/plugin-manifest-catalog-images.ts'
)
const ARTIFACT_PARSERS = {
themes: loadPluginSchemaModule('artifacts/plugin-theme-artifact.ts').parsePluginAppThemeArtifact,
terminalThemes: loadPluginSchemaModule('artifacts/plugin-terminal-theme-artifact.ts')
.parsePluginTerminalThemeArtifact,
iconThemes: loadPluginSchemaModule('artifacts/plugin-icon-theme-artifact.ts')
.parsePluginIconThemeArtifact
}
const { parsePluginLanguagePackArtifact } = loadPluginSchemaModule(
'artifacts/plugin-language-pack-artifact.ts'
)
const { parsePluginLanguageConfiguration } = loadPluginSchemaModule(
'artifacts/plugin-syntax-artifact.ts'
// Why shared: `orca plugin validate` runs these same strict artifact checks.
const { strictPluginArtifactErrors } = loadPluginSchemaModule(
'authoring/plugin-strict-artifact-checks.ts'
)
// Why strict here: Orca skips unknown theme tokens and icon slots at runtime, so authors only
// learn about a typo from this check.
function artifactErrors(pluginDir, manifest) {
return Object.entries(ARTIFACT_PARSERS).flatMap(([point, parse]) =>
manifest.contributes[point].flatMap((contribution) => {
let text = ''
try {
text = readFileSync(path.join(pluginDir, ...contribution.path.split('/')), 'utf8')
parse(text, 'strict')
return []
} catch (error) {
const detail = error.issues
? formatPluginSchemaIssues(error.issues, JSON.parse(text))
: error.message
return [`contributes.${point} "${contribution.id}" (${contribution.path}): ${detail}`]
}
})
)
}
// Why: Orca drops protected security copy from a pack at load, which authors would never notice.
function languagePackErrors(pluginDir, manifest) {
return manifest.contributes.languagePacks.flatMap((pack) => {
const file = path.join(pluginDir, ...pack.path.split('/'))
const parsed = existsSync(file)
? parsePluginLanguagePackArtifact(readFileSync(file, 'utf8'), 'strict')
: { ok: false, error: 'file not found' }
return parsed.ok
? []
: [`contributes.languagePacks "${pack.locale}" (${pack.path}): ${parsed.error}`]
})
}
// Why: Orca drops an unsafe auto-closing pair or comment marker at load instead of failing.
function syntaxConfigurationErrors(pluginDir, manifest) {
return manifest.contributes.syntaxes.flatMap((syntax) => {
if (!syntax.configuration) {
return []
}
let text = ''
try {
text = readFileSync(path.join(pluginDir, ...syntax.configuration.split('/')), 'utf8')
parsePluginLanguageConfiguration(text, 'strict')
return []
} catch (error) {
const detail = error.issues
? formatPluginSchemaIssues(error.issues, JSON.parse(text))
: error.message
return [`contributes.syntaxes "${syntax.id}" (${syntax.configuration}): ${detail}`]
}
})
function readPluginText(pluginDir) {
return (manifestPath) => {
const file = path.join(pluginDir, ...manifestPath.split('/'))
return existsSync(file) ? readFileSync(file, 'utf8') : null
}
}
async function git(cwd, args) {
@@ -206,11 +150,7 @@ for (const target of targets) {
const errors = validatePluginFile(path.basename(file), raw, readCatalogFile)
if (errors.length === 0 && path.basename(file) === 'orca-plugin.json') {
const { manifest } = parsePluginManifest(raw)
errors.push(
...artifactErrors(root, manifest),
...languagePackErrors(root, manifest),
...syntaxConfigurationErrors(root, manifest)
)
errors.push(...strictPluginArtifactErrors(manifest, readPluginText(root)))
}
for (const error of errors) {
console.error(`${file}: ${error}`)