import { Command, type Interfaces } from '@oclif/core'; import type { LoadConfigOptions } from './config.js'; import type { ResolvedB2CConfig } from '../config/index.js'; import { type Logger } from '../logging/index.js'; import { type ExtraParamsConfig } from '../clients/middleware.js'; import { SafetyGuard } from '../safety/safety-guard.js'; import type { SafetyEvaluation } from '../safety/types.js'; import { Telemetry, type TelemetryAttributes } from '../telemetry/index.js'; export type Flags = Interfaces.InferredFlags<(typeof BaseCommand)['baseFlags'] & T['flags']>; export type Args = Interfaces.InferredArgs; /** * Stable error-code values passed to oclif's `this.error(msg, {code})` so that * {@link classifyError} can categorize the resulting telemetry without parsing * error messages. * * - `VALIDATION` — user/config input error (missing flag, bad value). Expected, * not a reliability defect. * - `GUARDRAIL` — blocked by the user's own safety configuration. Working as * intended, not a failure of the tool. */ export declare const ERROR_CODE: { readonly VALIDATION: "VALIDATION"; readonly GUARDRAIL: "GUARDRAIL"; }; /** Telemetry category recorded on COMMAND_ERROR (see {@link classifyError}). */ export type ErrorCategory = 'guardrail' | 'runtime' | 'validation'; /** * Classify a thrown command error for telemetry so analytics can exclude * expected validation/guardrail errors from reliability (defect-rate) metrics. * * Resolution order: * 1. An explicit oclif `code` of {@link ERROR_CODE.VALIDATION} / `GUARDRAIL` * (set at the throw site via `this.error(msg, {code})`). * 2. Safety error class names thrown from HTTP/auth middleware that do not flow * through `this.error` (`SafetyBlockedError`, `SafetyConfirmationRequired`). * 3. Everything else is a genuine `runtime` error (the reliability signal). */ export declare function classifyError(err: unknown): ErrorCategory; /** * Base command class for B2C CLI tools. * * Environment variables for logging: * - SFCC_JSON_LOGS: Output log messages as JSON lines (for log aggregation) * - SFCC_LOG_TO_STDOUT: Send logs to stdout instead of stderr * - SFCC_LOG_COLORIZE: Force colors on/off (default: auto-detect TTY) * - SFCC_REDACT_SECRETS: Set to 'false' to disable secret redaction * - NO_COLOR: Industry standard to disable colors * * Environment variables for telemetry: * - SF_DISABLE_TELEMETRY: Set to 'true' to disable telemetry (sf CLI standard) * - SFCC_DISABLE_TELEMETRY: Set to 'true' to disable telemetry * - SFCC_APP_INSIGHTS_KEY: Override connection string from package.json */ export declare abstract class BaseCommand extends Command { static baseFlags: { 'log-level': Interfaces.OptionFlag<"trace" | "debug" | "info" | "warn" | "error" | "silent" | undefined, Interfaces.CustomOptions>; debug: Interfaces.BooleanFlag; json: Interfaces.BooleanFlag; jsonl: Interfaces.BooleanFlag; lang: Interfaces.OptionFlag; config: Interfaces.OptionFlag; instance: Interfaces.OptionFlag; 'project-directory': Interfaces.OptionFlag; 'extra-query': Interfaces.OptionFlag; 'extra-body': Interfaces.OptionFlag; 'extra-headers': Interfaces.OptionFlag; }; protected flags: Flags; protected args: Args; protected resolvedConfig: ResolvedB2CConfig; protected logger: Logger; /** Safety guard for evaluating operations against safety rules and levels. */ protected safetyGuard: SafetyGuard; /** Telemetry instance for tracking command events */ protected telemetry?: Telemetry; /** Start time for command duration tracking */ private commandStartTime?; init(): Promise; /** * Auto-initialize telemetry from package.json oclif.telemetry config. * Called during init() to enable automatic telemetry for all commands. */ private initTelemetryFromConfig; /** * Manual telemetry initialization for non-pjson usage (e.g., MCP server with additional attributes). * Use this when you need to pass custom initial attributes or use a different connection string. * * @param options - Telemetry options * @param options.appInsightsKey - Optional Application Insights connection string (overrides pjson config) * @param options.initialAttributes - Custom attributes to add to telemetry events * @returns The telemetry instance, or undefined if telemetry is disabled */ protected initTelemetry(options: { appInsightsKey?: string; initialAttributes?: TelemetryAttributes; }): Promise; /** * Determine colorize setting based on env vars and TTY. * Priority: NO_COLOR > SFCC_LOG_COLORIZE > TTY detection */ private shouldColorize; protected configureLogging(): void; /** * Override oclif's log() to use pino. */ log(message?: string, ...args: unknown[]): void; /** * Override oclif's warn() to use pino. */ warn(input: string | Error): string | Error; /** * Gets base configuration options from common flags. * * Subclasses should spread these options when overriding loadConfiguration() * to ensure common options like projectDirectory are always included. * * @example * ```typescript * protected override loadConfiguration(): ResolvedB2CConfig { * const options: LoadConfigOptions = { * ...this.getBaseConfigOptions(), * // Add subclass-specific options here * }; * return loadConfig(extractMyFlags(this.flags), options); * } * ``` */ protected getBaseConfigOptions(): LoadConfigOptions; protected loadConfiguration(): Promise; /** * Enrich telemetry with realm/tenant context from the resolved configuration. * Called after loadConfiguration() in init() so that COMMAND_SUCCESS and * COMMAND_EXCEPTION events include organizational context. */ protected addTelemetryContext(): void; /** * Build a suffix appended to "X is required" config errors. * * When NO config source contributed any values (no dw.json found, no env * vars, no flags — the dominant cause of these errors in telemetry), the user * is almost certainly unconfigured rather than missing one specific field, so * we point them at the configuration guide. When a source DID load, we stay * quiet: the existing message already lists the precise flag/env var to set. * * @returns A "\n\nSee: " suffix, or '' when config is already partially present. */ protected configDocsHint(): string; /** * Collects config sources from plugins via the `b2c:config-sources` hook. * * This method is called during command initialization, after flags are parsed * but before configuration is resolved. It allows CLI plugins to provide * custom ConfigSource implementations. * * Plugin sources are registered with the global config source registry * and automatically included in all subsequent `resolveConfig()` calls. * * Priority mapping: * - 'before' → -1 (higher priority than defaults) * - 'after' → 10 (lower priority than defaults) * - number → used directly */ protected collectPluginConfigSources(): Promise; /** * Collects HTTP middleware from plugins via the `b2c:http-middleware` hook. * * This method is called during command initialization, after flags are parsed * but before any API clients are created. It allows CLI plugins to provide * custom middleware that will be applied to all HTTP clients. * * Plugin middleware is registered with the global middleware registry. */ protected collectPluginHttpMiddleware(): Promise; /** * Collects auth middleware from plugins via the `b2c:auth-middleware` hook. * * This method is called during command initialization, after flags are parsed * but before any authentication is performed. It allows CLI plugins to provide * custom middleware that will be applied to OAuth token requests. * * Plugin middleware is registered with the global auth middleware registry. */ protected collectPluginAuthMiddleware(): Promise; /** * Handle errors thrown during command execution. * * Logs the error using the structured logger (including cause if available). * In JSON mode, outputs a JSON error object to stdout instead of oclif's default format. * Sends exception to telemetry if initialized. */ protected catch(err: Error & { exitCode?: number; }): Promise; /** * Called after run() completes (whether successfully or via catch()). * Tracks COMMAND_SUCCESS and stops telemetry. */ protected finally(err: Error | undefined): Promise; baseCommandTest(): void; /** * Check if destructive operations are allowed based on safety level. * Provides early, user-friendly error messages before HTTP requests are attempted. * * This is a command-level check that complements the HTTP middleware safety guard. * While the middleware provides unbypassable protection, this method offers better * error messages and early detection. * * Destructive operations include: * - Deleting resources (sandboxes, users, API clients, etc.) * - Resetting or wiping data * - Force operations that overwrite data * - Revoking access or permissions * * NOTE: This is optional - the HTTP middleware will catch any operations that bypass * this check. Use this method for better UX when you know an operation is destructive. * * @param operationDescription - Description of the operation (e.g., "delete sandbox", "reset user password") * @throws Error if safety level blocks the operation * * @example * ```typescript * async run() { * this.assertDestructiveOperationAllowed('delete sandbox'); * // ... proceed with deletion * } * ``` */ protected assertDestructiveOperationAllowed(operationDescription?: string): void; /** * Parse extra params from --extra-query, --extra-body, and --extra-headers flags. * Returns undefined if no extra params are specified. * * @returns ExtraParamsConfig or undefined */ protected getExtraParams(): ExtraParamsConfig | undefined; /** * Register safety middleware that evaluates all HTTP requests against the SafetyGuard. * * The middleware reads `this.safetyGuard` lazily (via arrow function closure), so it * picks up the full config after `initializeSafetyGuard()` runs. This allows the * middleware to be registered early in init() before config resolution completes. */ private registerSafetyMiddleware; /** * Update the safety guard with config-provided safety settings. * Called after loadConfiguration() to merge env vars, global safety config, * and per-instance dw.json config. */ private initializeSafetyGuard; /** * Evaluate command-level safety rules for the current command. * * This runs at the end of init() so every command is evaluated against * command rules (e.g., `{ command: "code:deploy", action: "block" }`). * If no command rule matches, this is a no-op — level-based blocking * is handled by the HTTP middleware and assertDestructiveOperationAllowed(). */ private evaluateCommandSafety; /** * Require interactive confirmation for a safety-guarded operation. * * If stdin is a TTY, prompts the user. Otherwise, blocks with an error message. * The error message clearly indicates the block is from the user's own safety configuration. * * @param evaluation - The safety evaluation that triggered confirmation * @throws Error if confirmation is denied or not possible */ protected confirmOrBlock(evaluation: SafetyEvaluation): Promise; /** * Register extra params (query, body, headers) as global middleware. * This applies to ALL HTTP clients created during command execution. */ private registerExtraParamsMiddleware; }