UNPKG

polyv-live-cli

Version:

CLI tool for managing PolyV live streaming services.

601 lines (582 loc) 32.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateOutputFormat = validateOutputFormat; exports.validateDateFormat = validateDateFormat; exports.validateDateRange = validateDateRange; exports.registerStatisticsCommands = registerStatisticsCommands; const statistics_handler_1 = require("../handlers/statistics.handler"); const manager_1 = require("../config/manager"); const auth_adapter_1 = require("../config/auth-adapter"); const errors_1 = require("../utils/errors"); const statistics_service_sdk_1 = require("../services/statistics.service.sdk"); const api_command_1 = require("../utils/api-command"); const date_validation_1 = require("../utils/date-validation"); async function loadAuthAndServiceConfig(parentOptions) { const authResult = auth_adapter_1.authAdapter.tryGetAuthConfig(parentOptions); if (!authResult) { throw new Error(auth_adapter_1.authAdapter.getStatusMessage(parentOptions)); } let configResult; try { configResult = await manager_1.configManager.load({ cliOptions: parentOptions, }); } catch (error) { if (error instanceof Error && error.message.includes('Auth configuration is incomplete')) { configResult = { config: { baseUrl: 'https://api.polyv.net', timeout: 30000, debug: false } }; } else { throw error; } } const serviceConfig = { baseUrl: configResult.config.baseUrl, timeout: configResult.config.timeout, debug: configResult.config.debug }; const isVerbose = !!parentOptions.verbose; if (isVerbose) { console.log(`🔐 Authentication Source: ${authResult.source}`); if (authResult.accountName) { console.log(`👤 Account: ${authResult.accountName}`); } console.log(''); } const result = { authConfig: authResult.config, serviceConfig, isVerbose, }; if (authResult.source) { result.authSource = authResult.source; } if (authResult.accountName) { result.accountName = authResult.accountName; } return result; } function validateOutputFormat(value) { if (!['table', 'json'].includes(value)) { throw new Error('Output format must be either "table" or "json"'); } return value; } function validateDateFormat(value) { const regex = /^\d{4}-\d{2}-\d{2}$/; if (!regex.test(value)) { throw new Error('Date format must be yyyy-MM-dd'); } const date = new Date(value); if (isNaN(date.getTime())) { throw new Error(`Invalid date: ${value}`); } const [year, month, day] = value.split('-').map(Number); if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) { throw new Error(`Invalid date: ${value}`); } return value; } function validateDateRange(startDay, endDay) { const result = (0, date_validation_1.validateDateRange)(startDay, endDay); if (!result.valid) { throw new Error(result.error || 'Invalid date range'); } } function registerStatisticsCommands(program) { const statisticsCmd = program.command('statistics'); statisticsCmd.description('View live streaming statistics data'); const runV4StatisticsCommand = async (options, action) => { try { const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(program.opts()); const service = new statistics_service_sdk_1.StatisticsServiceSdk(authConfig, serviceConfig); (0, api_command_1.displayApiResult)(await action(service), options.output); } catch (error) { (0, errors_1.logError)(error instanceof Error ? error : new Error(String(error))); process.exit(1); } }; statisticsCmd.command('session-summary-list') .description('List V4 session statistics summaries') .option('-c, --channel-id <channelId>', 'channel ID') .option('--keyword <keyword>', 'keyword') .option('--start-time <timestamp>', 'start timestamp', parseInt) .option('--end-time <timestamp>', 'end timestamp', parseInt) .option('--page-number <page>', 'page number', parseInt) .option('--page-size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action(async (options) => { try { const parentOptions = program.opts(); const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions); await new statistics_handler_1.StatisticsHandler(authConfig, serviceConfig).listSessionSummary(options); } catch (error) { (0, errors_1.logError)(error instanceof Error ? error : new Error(String(error))); process.exit(1); } }); statisticsCmd.command('inviter-poster-list') .description('List inviter poster statistics') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .option('--page-number <page>', 'page number', parseInt) .option('--page-size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action(async (options) => { try { const parentOptions = program.opts(); const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions); await new statistics_handler_1.StatisticsHandler(authConfig, serviceConfig).listInviterPoster(options); } catch (error) { (0, errors_1.logError)(error instanceof Error ? error : new Error(String(error))); process.exit(1); } }); statisticsCmd.command('lottery-list') .description('List V4 channel lottery statistics records') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .option('--lottery-id <id>', 'lottery ID') .option('--session-id <id>', 'session ID') .option('--start-time <timestamp>', 'start timestamp', parseInt) .option('--end-time <timestamp>', 'end timestamp', parseInt) .option('--page-number <page>', 'page number', parseInt) .option('--page-size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.listChannelLotteryRecords((0, api_command_1.apiParams)(options)))); statisticsCmd.command('live-data') .description('Get V4 channel live data summary') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .option('--start-time <timestamp>', 'start timestamp', parseInt) .option('--end-time <timestamp>', 'end timestamp', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getLiveData((0, api_command_1.apiParams)(options)))); statisticsCmd.command('live-session-list') .description('List V4 live session statistics') .option('--start-time <timestamp>', 'start timestamp', parseInt) .option('--end-time <timestamp>', 'end timestamp', parseInt) .option('--page-number <page>', 'page number', parseInt) .option('--page-size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.listSessionStats((0, api_command_1.apiParams)(options)))); statisticsCmd.command('weixin-booking-list') .description('List V4 WeChat booking records') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .option('--start-time <timestamp>', 'start timestamp', parseInt) .option('--end-time <timestamp>', 'end timestamp', parseInt) .option('--page-number <page>', 'page number', parseInt) .option('--page-size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.listWeixinBookings((0, api_command_1.apiParams)(options)))); statisticsCmd.command('invite-list') .description('List V4 invite statistics records') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .option('--sender-viewer-id <id>', 'sender viewer ID') .option('--start-time <timestamp>', 'start timestamp', parseInt) .option('--end-time <timestamp>', 'end timestamp', parseInt) .option('--page-number <page>', 'page number', parseInt) .option('--page-size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.listInviteStats((0, api_command_1.apiParams)(options)))); statisticsCmd.command('channel-play-summary') .description('Get multi-channel play summary statistics') .requiredOption('--start-date <date>', 'start date (yyyy-MM-dd)', validateDateFormat) .requiredOption('--end-date <date>', 'end date (yyyy-MM-dd)', validateDateFormat) .option('--user-id <id>', 'account user ID; defaults to configured userId') .option('--channel-ids <ids>', 'channel IDs, comma-separated') .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getChannelPlaySummary((0, api_command_1.apiParams)(options)))); statisticsCmd.command('realtime-viewers') .description('Get realtime viewer counts for channels') .requiredOption('--channel-ids <ids>', 'channel IDs, comma-separated') .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getRealtimeViewers((0, api_command_1.apiParams)(options)))); statisticsCmd.command('channel-statistic') .description('Get channel statistic data') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .requiredOption('--start-date <date>', 'start date (yyyy-MM-dd)', validateDateFormat) .requiredOption('--end-date <date>', 'end date (yyyy-MM-dd)', validateDateFormat) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getChannelStatistic((0, api_command_1.apiParams)(options)))); statisticsCmd.command('channel-session-stats') .description('Get channel session statistics') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .option('--session-ids <ids>', 'session IDs, comma-separated') .option('--start-time <timestamp>', 'start timestamp', parseInt) .option('--end-time <timestamp>', 'end timestamp', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getSessionStats((0, api_command_1.apiParams)(options)))); statisticsCmd.command('mic-list') .description('List channel mic detail statistics') .option('--channel-ids <ids>', 'channel IDs, comma-separated') .option('--start-day <date>', 'start date (yyyy-MM-dd)', validateDateFormat) .option('--end-day <date>', 'end date (yyyy-MM-dd)', validateDateFormat) .option('--page <page>', 'page number', parseInt) .option('--size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getMicDetailList((0, api_command_1.apiParams)(options)))); statisticsCmd.command('link-mic-list') .description('List channel link-mic detail logs') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .requiredOption('--start-date <date>', 'start date (yyyy-MM-dd)', validateDateFormat) .requiredOption('--end-date <date>', 'end date (yyyy-MM-dd)', validateDateFormat) .option('--page <page>', 'page number', parseInt) .option('--page-size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getLinkMicDetailList((0, api_command_1.apiParams)(options)))); statisticsCmd.command('channel-summary') .description('Get channel view summary statistics') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .requiredOption('--start-day <date>', 'start date (yyyy-MM-dd)', validateDateFormat) .requiredOption('--end-day <date>', 'end date (yyyy-MM-dd)', validateDateFormat) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getSummary((0, api_command_1.apiParams)(options)))); statisticsCmd.command('product-click') .description('List product click statistics') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .option('--session-id <id>', 'session ID') .option('--start-time <timestamp>', 'start timestamp', parseInt) .option('--end-time <timestamp>', 'end timestamp', parseInt) .option('--page-number <page>', 'page number', parseInt) .option('--page-size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getProductClickStats((0, api_command_1.apiParams)(options)))); statisticsCmd.command('product-list-click') .description('List product-list click statistics') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .option('--session-id <id>', 'session ID') .option('--start-time <timestamp>', 'start timestamp', parseInt) .option('--end-time <timestamp>', 'end timestamp', parseInt) .option('--page-number <page>', 'page number', parseInt) .option('--page-size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getProductListStats((0, api_command_1.apiParams)(options)))); statisticsCmd.command('redpack-list') .description('List redpack statistics') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .option('--session-id <id>', 'session ID') .option('--start-time <timestamp>', 'start timestamp', parseInt) .option('--end-time <timestamp>', 'end timestamp', parseInt) .option('--page-number <page>', 'page number', parseInt) .option('--page-size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getRedpackStats((0, api_command_1.apiParams)(options)))); statisticsCmd.command('realtime-v1') .description('Get realtime viewers using the legacy V1 statistics API') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .option('--user-id <id>', 'account user ID; defaults to configured userId') .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getRealtimeViewersV1((0, api_command_1.apiParams)(options)))); statisticsCmd.command('viewlog-v1') .description('Get V1 viewlog records') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .requiredOption('--current-day <date>', 'query date (yyyy-MM-dd)', validateDateFormat) .option('--user-id <id>', 'account user ID; defaults to configured userId') .option('--param1 <value>', 'legacy API param1 value') .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getViewlogV1((0, api_command_1.apiParams)(options)))); statisticsCmd.command('viewlog-v2') .description('Get V2 paged viewlog records') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .requiredOption('--current-day <date>', 'query date (yyyy-MM-dd)', validateDateFormat) .option('--page <page>', 'page number', parseInt) .option('--page-size <size>', 'page size', parseInt) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action((options) => runV4StatisticsCommand(options, (service) => service.getViewlog2((0, api_command_1.apiParams)(options)))); const viewCmd = statisticsCmd .command('view') .description('View daily statistics for a channel') .requiredOption('-c, --channel-id <channelId>', 'channel ID') .requiredOption('--start-day <date>', 'start date (yyyy-MM-dd)', validateDateFormat) .requiredOption('--end-day <date>', 'end date (yyyy-MM-dd)', validateDateFormat) .option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table') .action(async (options) => { try { validateDateRange(options.startDay, options.endDay); const parentOptions = program.opts(); const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions); const statisticsHandler = new statistics_handler_1.StatisticsHandler(authConfig, serviceConfig); const viewOptions = { channelId: options.channelId, startDay: options.startDay, endDay: options.endDay, output: options.output }; await statisticsHandler.viewStatistics(viewOptions); } catch (error) { if (error instanceof Error && error.message.includes('Authentication')) { const diagnostics = auth_adapter_1.authAdapter.getDiagnostics(program.opts()); console.error('\n🔍 Authentication Diagnostics:'); diagnostics.availableSources.forEach(source => { const status = source.appId && source.appSecret ? '✅' : '❌'; console.error(` ${status} ${source.metadata.source}: ${source.type}`); }); if (diagnostics.errors.length > 0) { console.error('\n❌ Errors:'); diagnostics.errors.forEach(err => console.error(` - ${err}`)); } } (0, errors_1.logError)(error instanceof Error ? error : new Error(String(error))); process.exit(1); } }); viewCmd.addHelpText('after', ` Examples: # View daily statistics for a channel $ polyv-live-cli statistics view -c "3151318" --start-day "2024-01-01" --end-day "2024-01-31" # Output in JSON format $ polyv-live-cli statistics view -c "3151318" --start-day "2024-01-01" --end-day "2024-01-31" -o json # With full parameter names $ polyv-live-cli statistics view --channel-id "3151318" --start-day "2024-01-01" --end-day "2024-01-31" --output table Date Range: --start-day Start date in yyyy-MM-dd format (required) --end-day End date in yyyy-MM-dd format (required) Note: Date range cannot exceed 60 days Output Formats: table Formatted table output (default) json JSON format for programmatic use Notes: - Channel ID is required for all statistics queries - Date range cannot exceed 60 days - Statistics include PC and mobile view data `); const concurrencyCmd = statisticsCmd .command('concurrency') .description('查看历史并发数据') .requiredOption('-c, --channel-id <channelId>', '频道ID') .requiredOption('--start-date <date>', '开始日期 (yyyy-MM-dd)', validateDateFormat) .requiredOption('--end-date <date>', '结束日期 (yyyy-MM-dd)', validateDateFormat) .option('-o, --output <format>', '输出格式 (table|json)', validateOutputFormat, 'table') .action(async (options) => { try { validateDateRange(options.startDate, options.endDate); const parentOptions = program.opts(); const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions); const statisticsHandler = new statistics_handler_1.StatisticsHandler(authConfig, serviceConfig); const concurrencyOptions = { channelId: options.channelId, startDate: options.startDate, endDate: options.endDate, output: options.output }; await statisticsHandler.viewConcurrency(concurrencyOptions); } catch (error) { if (error instanceof Error && error.message.includes('Authentication')) { const diagnostics = auth_adapter_1.authAdapter.getDiagnostics(program.opts()); console.error('\n🔍 Authentication Diagnostics:'); diagnostics.availableSources.forEach(source => { const status = source.appId && source.appSecret ? '✅' : '❌'; console.error(` ${status} ${source.metadata.source}: ${source.type}`); }); if (diagnostics.errors.length > 0) { console.error('\n❌ Errors:'); diagnostics.errors.forEach(err => console.error(` - ${err}`)); } } (0, errors_1.logError)(error instanceof Error ? error : new Error(String(error))); process.exit(1); } }); concurrencyCmd.addHelpText('after', ` Examples: # View historical concurrency data $ polyv-live-cli statistics concurrency -c "3151318" --start-date "2024-01-01" --end-date "2024-01-31" # Output in JSON format $ polyv-live-cli statistics concurrency -c "3151318" --start-date "2024-01-01" --end-date "2024-01-31" -o json Date Range: --start-date Start date in yyyy-MM-dd format (required) --end-date End date in yyyy-MM-dd format (required) Note: Date range cannot exceed 60 days Output Formats: table Formatted table output (default) json JSON format for programmatic use `); const maxConcurrentCmd = statisticsCmd .command('max-concurrent') .description('查看历史最高并发人数') .requiredOption('-c, --channel-id <channelId>', '频道ID') .requiredOption('--start-time <timestamp>', '开始时间戳 (13位毫秒)', parseInt) .requiredOption('--end-time <timestamp>', '结束时间戳 (13位毫秒)', parseInt) .option('-o, --output <format>', '输出格式 (table|json)', validateOutputFormat, 'table') .action(async (options) => { try { const rangeValidation = (0, date_validation_1.validateTimestampRange)(options.startTime, options.endTime); if (!rangeValidation.valid) { throw new Error(rangeValidation.error || 'Invalid time range'); } const parentOptions = program.opts(); const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions); const statisticsHandler = new statistics_handler_1.StatisticsHandler(authConfig, serviceConfig); const maxConcurrentOptions = { channelId: options.channelId, startTime: options.startTime, endTime: options.endTime, output: options.output }; await statisticsHandler.viewMaxConcurrent(maxConcurrentOptions); } catch (error) { if (error instanceof Error && error.message.includes('Authentication')) { const diagnostics = auth_adapter_1.authAdapter.getDiagnostics(program.opts()); console.error('\n🔍 Authentication Diagnostics:'); diagnostics.availableSources.forEach(source => { const status = source.appId && source.appSecret ? '✅' : '❌'; console.error(` ${status} ${source.metadata.source}: ${source.type}`); }); if (diagnostics.errors.length > 0) { console.error('\n❌ Errors:'); diagnostics.errors.forEach(err => console.error(` - ${err}`)); } } (0, errors_1.logError)(error instanceof Error ? error : new Error(String(error))); process.exit(1); } }); maxConcurrentCmd.addHelpText('after', ` Examples: # View maximum historical concurrent viewers $ polyv-live-cli statistics max-concurrent -c "3151318" --start-time 1704067200000 --end-time 1735689600000 # Output in JSON format $ polyv-live-cli statistics max-concurrent -c "3151318" --start-time 1704067200000 --end-time 1735689600000 -o json Timestamp Range: --start-time Start timestamp in 13-digit milliseconds (required) --end-time End timestamp in 13-digit milliseconds (required) Note: Time range cannot exceed 3 months Output Formats: table Formatted output (default) json JSON format for programmatic use `); const audienceCmd = statisticsCmd.command('audience'); audienceCmd.description('View audience statistics'); const regionCmd = audienceCmd .command('region') .description('View audience region distribution') .requiredOption('-c, --channel-id <channelId>', '频道ID') .requiredOption('--start-time <timestamp>', '开始时间戳 (13位毫秒)', parseInt) .requiredOption('--end-time <timestamp>', '结束时间戳 (13位毫秒)', parseInt) .option('-t, --type <type>', '地域类型 (country/province/city)', 'province') .option('-o, --output <format>', '输出格式 (table|json)', validateOutputFormat, 'table') .action(async (options) => { try { const rangeValidation = (0, date_validation_1.validate90DayTimestampRange)(options.startTime, options.endTime); if (!rangeValidation.valid) { throw new Error(rangeValidation.error || 'Invalid time range'); } const parentOptions = program.opts(); const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions); const statisticsHandler = new statistics_handler_1.StatisticsHandler(authConfig, serviceConfig); const regionOptions = { channelId: options.channelId, startTime: options.startTime, endTime: options.endTime, type: options.type, output: options.output }; await statisticsHandler.viewRegionDistribution(regionOptions); } catch (error) { if (error instanceof Error && error.message.includes('Authentication')) { const diagnostics = auth_adapter_1.authAdapter.getDiagnostics(program.opts()); console.error('\n🔍 Authentication Diagnostics:'); diagnostics.availableSources.forEach(source => { const status = source.appId && source.appSecret ? '✅' : '❌'; console.error(` ${status} ${source.metadata.source}: ${source.type}`); }); if (diagnostics.errors.length > 0) { console.error('\n❌ Errors:'); diagnostics.errors.forEach(err => console.error(` - ${err}`)); } } (0, errors_1.logError)(error instanceof Error ? error : new Error(String(error))); process.exit(1); } }); regionCmd.addHelpText('after', ` Examples: # View region distribution $ polyv-live-cli statistics audience region -c "3151318" --start-time 1648742400000 --end-time 1651334399000 # With city type $ polyv-live-cli statistics audience region -c "3151318" --start-time 1648742400000 --end-time 1651334399000 --type city # Output in JSON format $ polyv-live-cli statistics audience region -c "3151318" --start-time 1648742400000 --end-time 1651334399000 -o json Timestamp Range: --start-time Start timestamp in 13-digit milliseconds (required) --end-time End timestamp in 13-digit milliseconds (required) Note: Time range cannot exceed 90 days Region Types: --type Region type (default: province) country: View by country province: View by province (default) city: View by city Output Formats: table Formatted table output (default) json JSON format for programmatic use `); const deviceCmd = audienceCmd .command('device') .description('View audience device distribution') .requiredOption('-c, --channel-id <channelId>', '频道ID') .requiredOption('--start-time <timestamp>', '开始时间戳 (13位毫秒)', parseInt) .requiredOption('--end-time <timestamp>', '结束时间戳 (13位毫秒)', parseInt) .option('-o, --output <format>', '输出格式 (table|json)', validateOutputFormat, 'table') .action(async (options) => { try { const rangeValidation = (0, date_validation_1.validate90DayTimestampRange)(options.startTime, options.endTime); if (!rangeValidation.valid) { throw new Error(rangeValidation.error || 'Invalid time range'); } const parentOptions = program.opts(); const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions); const statisticsHandler = new statistics_handler_1.StatisticsHandler(authConfig, serviceConfig); const deviceOptions = { channelId: options.channelId, startTime: options.startTime, endTime: options.endTime, output: options.output }; await statisticsHandler.viewDeviceDistribution(deviceOptions); } catch (error) { if (error instanceof Error && error.message.includes('Authentication')) { const diagnostics = auth_adapter_1.authAdapter.getDiagnostics(program.opts()); console.error('\n🔍 Authentication Diagnostics:'); diagnostics.availableSources.forEach(source => { const status = source.appId && source.appSecret ? '✅' : '❌'; console.error(` ${status} ${source.metadata.source}: ${source.type}`); }); if (diagnostics.errors.length > 0) { console.error('\n❌ Errors:'); diagnostics.errors.forEach(err => console.error(` - ${err}`)); } } (0, errors_1.logError)(error instanceof Error ? error : new Error(String(error))); process.exit(1); } }); deviceCmd.addHelpText('after', ` Examples: # View device distribution $ polyv-live-cli statistics audience device -c "3151318" --start-time 1651386101000 --end-time 1652336501462 # Output in JSON format $ polyv-live-cli statistics audience device -c "3151318" --start-time 1651386101000 --end-time 1652336501462 -o json Timestamp Range: --start-time Start timestamp in 13-digit milliseconds (required) --end-time End timestamp in 13-digit milliseconds (required) Note: Time range cannot exceed 90 days Output Formats: table Formatted table output (default) json JSON format for programmatic use `); } //# sourceMappingURL=statistics.commands.js.map