Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
11 kB · 344 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345/** * CLI command wrappers for plugin operations * * This module provides thin wrappers around the core plugin operations * that handle CLI-specific concerns like console output and process exit. * * For the core operations (without CLI side effects), see pluginOperations.ts */import figures from 'figures'import { errorMessage } from '../../utils/errors.js'import { gracefulShutdown } from '../../utils/gracefulShutdown.js'import { logError } from '../../utils/log.js'import { getManagedPluginNames } from '../../utils/plugins/managedPlugins.js'import { parsePluginIdentifier } from '../../utils/plugins/pluginIdentifier.js'import type { PluginScope } from '../../utils/plugins/schemas.js'import { writeToStdout } from '../../utils/process.js'import { buildPluginTelemetryFields, classifyPluginCommandError,} from '../../utils/telemetry/pluginTelemetry.js'import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, type AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, logEvent,} from '../analytics/index.js'import { disableAllPluginsOp, disablePluginOp, enablePluginOp, type InstallableScope, installPluginOp, uninstallPluginOp, updatePluginOp, VALID_INSTALLABLE_SCOPES, VALID_UPDATE_SCOPES,} from './pluginOperations.js'
export { VALID_INSTALLABLE_SCOPES, VALID_UPDATE_SCOPES }
type PluginCliCommand = | 'install' | 'uninstall' | 'enable' | 'disable' | 'disable-all' | 'update'
/** * Generic error handler for plugin CLI commands. Emits * tengu_plugin_command_failed before exit so dashboards can compute a * success rate against the corresponding success events. */function handlePluginCommandError( error: unknown, command: PluginCliCommand, plugin?: string,): never { logError(error) const operation = plugin ? `${command} plugin "${plugin}"` : command === 'disable-all' ? 'disable all plugins' : `${command} plugins` // biome-ignore lint/suspicious/noConsole:: intentional console output console.error( `${figures.cross} Failed to ${operation}: ${errorMessage(error)}`, ) const telemetryFields = plugin ? (() => { const { name, marketplace } = parsePluginIdentifier(plugin) return { _PROTO_plugin_name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, ...(marketplace && { _PROTO_marketplace_name: marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, }), ...buildPluginTelemetryFields( name, marketplace, getManagedPluginNames(), ), } })() : {} logEvent('tengu_plugin_command_failed', { command: command as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, error_category: classifyPluginCommandError( error, ) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, ...telemetryFields, }) // eslint-disable-next-line custom-rules/no-process-exit process.exit(1)}
/** * CLI command: Install a plugin non-interactively * @param plugin Plugin identifier (name or plugin@marketplace) * @param scope Installation scope: user, project, or local (defaults to 'user') */export async function installPlugin( plugin: string, scope: InstallableScope = 'user',): Promise<void> { try { // biome-ignore lint/suspicious/noConsole:: intentional console output console.log(`Installing plugin "${plugin}"...`)
const result = await installPluginOp(plugin, scope)
if (!result.success) { throw new Error(result.message) }
// biome-ignore lint/suspicious/noConsole:: intentional console output console.log(`${figures.tick} ${result.message}`)
// _PROTO_* routes to PII-tagged plugin_name/marketplace_name BQ columns. // Unredacted plugin_id was previously logged to general-access // additional_metadata for all users — dropped in favor of the privileged // column route. const { name, marketplace } = parsePluginIdentifier( result.pluginId || plugin, ) logEvent('tengu_plugin_installed_cli', { _PROTO_plugin_name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, ...(marketplace && { _PROTO_marketplace_name: marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, }), scope: (result.scope || scope) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, install_source: 'cli-explicit' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, ...buildPluginTelemetryFields(name, marketplace, getManagedPluginNames()), })
// eslint-disable-next-line custom-rules/no-process-exit process.exit(0) } catch (error) { handlePluginCommandError(error, 'install', plugin) }}
/** * CLI command: Uninstall a plugin non-interactively * @param plugin Plugin name or plugin@marketplace identifier * @param scope Uninstall from scope: user, project, or local (defaults to 'user') */export async function uninstallPlugin( plugin: string, scope: InstallableScope = 'user', keepData = false,): Promise<void> { try { const result = await uninstallPluginOp(plugin, scope, !keepData)
if (!result.success) { throw new Error(result.message) }
// biome-ignore lint/suspicious/noConsole:: intentional console output console.log(`${figures.tick} ${result.message}`)
const { name, marketplace } = parsePluginIdentifier( result.pluginId || plugin, ) logEvent('tengu_plugin_uninstalled_cli', { _PROTO_plugin_name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, ...(marketplace && { _PROTO_marketplace_name: marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, }), scope: (result.scope || scope) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, ...buildPluginTelemetryFields(name, marketplace, getManagedPluginNames()), })
// eslint-disable-next-line custom-rules/no-process-exit process.exit(0) } catch (error) { handlePluginCommandError(error, 'uninstall', plugin) }}
/** * CLI command: Enable a plugin non-interactively * @param plugin Plugin name or plugin@marketplace identifier * @param scope Optional scope. If not provided, finds the most specific scope for the current project. */export async function enablePlugin( plugin: string, scope?: InstallableScope,): Promise<void> { try { const result = await enablePluginOp(plugin, scope)
if (!result.success) { throw new Error(result.message) }
// biome-ignore lint/suspicious/noConsole:: intentional console output console.log(`${figures.tick} ${result.message}`)
const { name, marketplace } = parsePluginIdentifier( result.pluginId || plugin, ) logEvent('tengu_plugin_enabled_cli', { _PROTO_plugin_name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, ...(marketplace && { _PROTO_marketplace_name: marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, }), scope: result.scope as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, ...buildPluginTelemetryFields(name, marketplace, getManagedPluginNames()), })
// eslint-disable-next-line custom-rules/no-process-exit process.exit(0) } catch (error) { handlePluginCommandError(error, 'enable', plugin) }}
/** * CLI command: Disable a plugin non-interactively * @param plugin Plugin name or plugin@marketplace identifier * @param scope Optional scope. If not provided, finds the most specific scope for the current project. */export async function disablePlugin( plugin: string, scope?: InstallableScope,): Promise<void> { try { const result = await disablePluginOp(plugin, scope)
if (!result.success) { throw new Error(result.message) }
// biome-ignore lint/suspicious/noConsole:: intentional console output console.log(`${figures.tick} ${result.message}`)
const { name, marketplace } = parsePluginIdentifier( result.pluginId || plugin, ) logEvent('tengu_plugin_disabled_cli', { _PROTO_plugin_name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, ...(marketplace && { _PROTO_marketplace_name: marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, }), scope: result.scope as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, ...buildPluginTelemetryFields(name, marketplace, getManagedPluginNames()), })
// eslint-disable-next-line custom-rules/no-process-exit process.exit(0) } catch (error) { handlePluginCommandError(error, 'disable', plugin) }}
/** * CLI command: Disable all enabled plugins non-interactively */export async function disableAllPlugins(): Promise<void> { try { const result = await disableAllPluginsOp()
if (!result.success) { throw new Error(result.message) }
// biome-ignore lint/suspicious/noConsole:: intentional console output console.log(`${figures.tick} ${result.message}`)
logEvent('tengu_plugin_disabled_all_cli', {})
// eslint-disable-next-line custom-rules/no-process-exit process.exit(0) } catch (error) { handlePluginCommandError(error, 'disable-all') }}
/** * CLI command: Update a plugin non-interactively * @param plugin Plugin name or plugin@marketplace identifier * @param scope Scope to update */export async function updatePluginCli( plugin: string, scope: PluginScope,): Promise<void> { try { writeToStdout( `Checking for updates for plugin "${plugin}" at ${scope} scope…\n`, )
const result = await updatePluginOp(plugin, scope)
if (!result.success) { throw new Error(result.message) }
writeToStdout(`${figures.tick} ${result.message}\n`)
if (!result.alreadyUpToDate) { const { name, marketplace } = parsePluginIdentifier( result.pluginId || plugin, ) logEvent('tengu_plugin_updated_cli', { _PROTO_plugin_name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, ...(marketplace && { _PROTO_marketplace_name: marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, }), old_version: (result.oldVersion || 'unknown') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, new_version: (result.newVersion || 'unknown') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, ...buildPluginTelemetryFields( name, marketplace, getManagedPluginNames(), ), }) }
await gracefulShutdown(0) } catch (error) { handlePluginCommandError(error, 'update', plugin) }}