polyv-live-cli
Version:
CLI tool for managing PolyV live streaming services.
804 lines (790 loc) β’ 41.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseInteger = parseInteger;
exports.validateSize = validateSize;
exports.validateOutputFormat = validateOutputFormat;
exports.validateProductType = validateProductType;
exports.validateProductStatus = validateProductStatus;
exports.validateProductSortType = validateProductSortType;
exports.validatePushCardType = validatePushCardType;
exports.validateLinkType = validateLinkType;
exports.validateUserProductLinkType = validateUserProductLinkType;
exports.validatePrice = validatePrice;
exports.registerProductCommands = registerProductCommands;
const product_handler_1 = require("../handlers/product.handler");
const manager_1 = require("../config/manager");
const auth_adapter_1 = require("../config/auth-adapter");
const errors_1 = require("../utils/errors");
const api_command_1 = require("../utils/api-command");
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 parseInteger(value) {
const parsed = parseInt(value, 10);
if (isNaN(parsed)) {
throw new Error(`"${value}" is not a valid integer`);
}
return parsed;
}
function validateSize(value) {
const parsed = parseInteger(value);
if (parsed < 1 || parsed > 100) {
throw new Error('Size must be between 1 and 100');
}
return parsed;
}
function validateOutputFormat(value) {
if (!['table', 'json'].includes(value)) {
throw new Error('Output format must be either "table" or "json"');
}
return value;
}
function validateProductType(value) {
const validTypes = ['normal', 'finance', 'position'];
if (!validTypes.includes(value)) {
throw new Error('Product type must be one of: normal, finance, position');
}
return value;
}
function validateProductStatus(value) {
const parsed = parseInt(value, 10);
if (parsed !== 1 && parsed !== 2) {
throw new Error('Product status must be 1 (δΈζΆ) or 2 (δΈζΆ)');
}
return parsed;
}
function validateProductSortType(value) {
const parsed = parseInt(value, 10);
if (parsed !== 10 && parsed !== 20 && parsed !== 50) {
throw new Error('Product sort type must be 10 (up), 20 (down), or 50 (rank)');
}
return parsed;
}
function validatePushCardType(value) {
if (value !== 'smallCard' && value !== 'bigCard') {
throw new Error('Push card type must be smallCard or bigCard');
}
return value;
}
function validateLinkType(value) {
const parsed = parseInt(value, 10);
if (parsed !== 10 && parsed !== 11) {
throw new Error('Link type must be 10 (ιη¨ιΎζ₯) or 11 (ε€εΉ³ε°ιΎζ₯)');
}
return parsed;
}
function validateUserProductLinkType(value) {
const parsed = parseInt(value, 10);
if (parsed !== 10 && parsed !== 11 && parsed !== 12) {
throw new Error('Link type must be 10 (ιη¨ιΎζ₯), 11 (ε€εΉ³ε°ιΎζ₯), or 12 (εηζΉζ³)');
}
return parsed;
}
function validatePrice(value) {
const parsed = parseFloat(value);
if (isNaN(parsed)) {
throw new Error(`"${value}" is not a valid price`);
}
if (parsed < 0) {
throw new Error('Price cannot be negative');
}
return parsed;
}
function withProductHandler(program, run) {
return (async () => {
try {
const parentOptions = program.opts();
const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions);
await run(new product_handler_1.ProductHandler(authConfig, serviceConfig));
}
catch (error) {
(0, errors_1.logError)(error instanceof Error ? error : new Error(String(error)));
process.exit(1);
}
})();
}
function addPaging(command) {
return command
.option('-P, --page <number>', 'page number', parseInteger, 1)
.option('-s, --size <number>', 'items per page', validateSize, 20)
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table');
}
function addUserProductOptions(command, update = false) {
if (update) {
command.requiredOption('-p, --product-id <productId>', 'user product ID');
}
command
.requiredOption('-n, --name <name>', 'product name')
.requiredOption('--link-type <type>', 'link type: 10 | 11 | 12', validateUserProductLinkType)
.requiredOption('-l, --link <url>', 'product link')
.option('-t, --product-type <type>', 'product type: normal | finance | position', validateProductType)
.option('--cover <url>', 'product cover URL')
.option('--pc-link <url>', 'PC link')
.option('--mobile-link <url>', 'mobile web link')
.option('--wx-miniprogram-link <url>', 'WeChat miniprogram link')
.option('--wx-miniprogram-original-id <id>', 'WeChat miniprogram original ID')
.option('--mobile-app-link <url>', 'mobile app link')
.option('--android-link <url>', 'Android native link')
.option('--ios-link <url>', 'iOS native link')
.option('--other-link <url>', 'other platform link')
.option('--features <json>', 'features/tags as JSON array string')
.option('--tag-ids <ids>', 'comma-separated product tag IDs')
.option('--btn-show <text>', 'button text')
.option('--product-desc <desc>', 'product description')
.option('--product-detail <detail>', 'product detail')
.option('--ext <json>', 'extended product info JSON string')
.option('--price-type <type>', 'price type: AMOUNT | CUSTOM')
.option('--real-price <amount>', 'real price amount', validatePrice)
.option('--custom-price <text>', 'custom price text')
.option('--original-price-type <type>', 'original price type: AMOUNT | CUSTOM')
.option('--price <amount>', 'original price amount', validatePrice)
.option('--custom-original-price <text>', 'custom original price text')
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table');
return command;
}
function toUserProductOptions(options) {
return {
...(options.productId ? { productId: options.productId } : {}),
name: options.name,
linkType: options.linkType,
link: options.link,
productType: options.productType,
cover: options.cover,
pcLink: options.pcLink,
mobileLink: options.mobileLink,
wxMiniprogramLink: options.wxMiniprogramLink,
wxMiniprogramOriginalId: options.wxMiniprogramOriginalId,
mobileAppLink: options.mobileAppLink,
androidLink: options.androidLink,
iosLink: options.iosLink,
otherLink: options.otherLink,
features: options.features,
tagIds: options.tagIds,
btnShow: options.btnShow,
productDesc: options.productDesc,
productDetail: options.productDetail,
ext: options.ext,
priceType: options.priceType,
realPrice: options.realPrice,
customPrice: options.customPrice,
originalPriceType: options.originalPriceType,
price: options.price,
customOrignalPrice: options.customOriginalPrice,
force: options.force,
output: options.output,
};
}
function registerProductCommands(program) {
const productCmd = program.command('product');
productCmd.description('Manage live streaming channel products');
const listCmd = productCmd
.command('list')
.description('List products with pagination')
.option('-c, --channel-id <channelId>', 'channel ID (required unless --platform is specified)')
.option('--platform', 'list platform products (user-level product library) instead of channel products')
.option('-P, --page <number>', 'page number (optional, minimum 1, default 1)', parseInteger, 1)
.option('-s, --size <number>', 'items per page (optional, 1-100, default 20)', validateSize, 20)
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action(async (options) => {
try {
if (!options.platform && !options.channelId) {
console.error('Error: --channel-id is required unless --platform is specified');
process.exit(1);
}
const parentOptions = program.opts();
const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions);
const productHandler = new product_handler_1.ProductHandler(authConfig, serviceConfig);
const listOptions = {
page: options.page,
size: options.size,
channelId: options.channelId,
platform: options.platform,
output: options.output
};
await productHandler.listProducts(listOptions);
}
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(error => console.error(` - ${error}`));
}
}
(0, errors_1.logError)(error instanceof Error ? error : new Error(String(error)));
process.exit(1);
}
});
listCmd.addHelpText('after', `
Examples:
# List channel products (requires channel ID)
$ polyv-live-cli product list -c "3151318"
$ polyv-live-cli product list -c "3151318" -P 2 -s 10
$ polyv-live-cli product list -c "3151318" -o json
# List platform products (user-level product library)
$ polyv-live-cli product list --platform
$ polyv-live-cli product list --platform -P 2 -s 10
Alternative (full parameter names):
$ polyv-live-cli product list --channel-id "3151318" --page 2 --size 10 --output json
$ polyv-live-cli product list --platform --page 2 --size 10
Modes:
--channel-id, -c List products for a specific channel (V3 API, includes channelId in response)
--platform List platform products from user-level product library (V4 API, no channelId)
Pagination:
--page, -P Page number (minimum 1, default 1)
--size, -s Items per page (1-100, default 20)
Output Formats:
table - Formatted table output (default)
json - JSON format for programmatic use
Notes:
- Use --channel-id for channel-specific products
- Use --platform for platform-level product library
- Channel products include channelId in the response
- All product prices are shown in Chinese Yuan (Β₯)
`);
const addCmd = productCmd
.command('add')
.description('Add a new product to channel')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('-n, --name <name>', 'product name')
.requiredOption('--status <status>', 'product status: 1 (on-shelf) | 2 (off-shelf)', validateProductStatus)
.requiredOption('--link-type <type>', 'link type: 10 (universal) | 11 (multi-platform)', validateLinkType)
.option('-t, --product-type <type>', 'product type: normal | finance | position (default: normal)', validateProductType, 'normal')
.option('--cover <url>', 'product cover URL')
.option('-l, --link <url>', 'universal link (required when link-type=10)')
.option('--pc-link <url>', 'PC link')
.option('--mobile-link <url>', 'mobile web link')
.option('--wx-miniprogram-link <url>', 'WeChat miniprogram link')
.option('--wx-miniprogram-original-id <id>', 'WeChat miniprogram original ID')
.option('--mobile-app-link <url>', 'mobile app link')
.option('--android-link <url>', 'Android native link')
.option('--ios-link <url>', 'iOS native link')
.option('--params <json>', 'custom params as JSON string')
.option('--product-desc <desc>', 'product description')
.option('--features <json>', 'features/tags as JSON array string')
.option('--btn-show <text>', 'button text')
.option('--yield <value>', 'yield/rate for finance products')
.option('--price-type <type>', 'price type: AMOUNT | CUSTOM')
.option('--real-price <amount>', 'real price amount', validatePrice)
.option('--custom-price <text>', 'custom price text')
.option('--original-price-type <type>', 'original price type: AMOUNT | CUSTOM')
.option('--price <amount>', 'original price amount', validatePrice)
.option('--custom-original-price <text>', 'custom original price text')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action(async (options) => {
try {
const parentOptions = program.opts();
const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions);
const productHandler = new product_handler_1.ProductHandler(authConfig, serviceConfig);
const addOptions = {
channelId: options.channelId,
name: options.name,
status: options.status,
linkType: options.linkType,
productType: options.productType,
cover: options.cover,
link: options.link,
pcLink: options.pcLink,
mobileLink: options.mobileLink,
wxMiniprogramLink: options.wxMiniprogramLink,
wxMiniprogramOriginalId: options.wxMiniprogramOriginalId,
mobileAppLink: options.mobileAppLink,
androidLink: options.androidLink,
iosLink: options.iosLink,
params: options.params,
productDesc: options.productDesc,
features: options.features,
btnShow: options.btnShow,
yield: options.yield,
priceType: options.priceType,
realPrice: options.realPrice,
customPrice: options.customPrice,
originalPriceType: options.originalPriceType,
price: options.price,
customOrignalPrice: options.customOriginalPrice,
output: options.output
};
await productHandler.addProduct(addOptions);
}
catch (error) {
(0, errors_1.logError)(error instanceof Error ? error : new Error(String(error)));
process.exit(1);
}
});
addCmd.addHelpText('after', `
Examples:
# Add a normal product
$ polyv-live-cli product add -c "3151318" -n "ζ΅θ―εε" --status 1 --link-type 10 \\
-l "https://example.com/product" --cover "https://example.com/cover.jpg" \\
--real-price 99.9 --price 199.9
# Add a finance product
$ polyv-live-cli product add -c "3151318" -n "ηθ΄’δΊ§ε" --status 1 --link-type 10 \\
-l "https://example.com/finance" --yield "5.5%" -t finance
Product Types:
normal - Regular product (default)
finance - Financial product
position - Job position
Status Values:
1 - On shelf (δΈζΆ)
2 - Off shelf (δΈζΆ)
Link Types:
10 - Universal link (requires --link)
11 - Multi-platform link (use --pc-link, --mobile-link, etc.)
Price Types:
AMOUNT - Numeric price (use --real-price, --price)
CUSTOM - Custom text price (use --custom-price)
`);
const updateCmd = productCmd
.command('update')
.description('Update an existing product')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('-p, --product-id <productId>', 'product ID', parseInteger)
.option('-n, --name <name>', 'product name')
.option('--status <status>', 'product status: 1 | 2', validateProductStatus)
.option('--link-type <type>', 'link type: 10 | 11', validateLinkType)
.option('-t, --product-type <type>', 'product type: normal | finance | position', validateProductType)
.option('--cover <url>', 'product cover URL')
.option('-l, --link <url>', 'universal link')
.option('--pc-link <url>', 'PC link')
.option('--mobile-link <url>', 'mobile web link')
.option('--wx-miniprogram-link <url>', 'WeChat miniprogram link')
.option('--wx-miniprogram-original-id <id>', 'WeChat miniprogram original ID')
.option('--mobile-app-link <url>', 'mobile app link')
.option('--android-link <url>', 'Android native link')
.option('--ios-link <url>', 'iOS native link')
.option('--params <json>', 'custom params as JSON string')
.option('--product-desc <desc>', 'product description')
.option('--features <json>', 'features/tags as JSON array string')
.option('--btn-show <text>', 'button text')
.option('--yield <value>', 'yield/rate for finance products')
.option('--price-type <type>', 'price type: AMOUNT | CUSTOM')
.option('--real-price <amount>', 'real price amount', validatePrice)
.option('--custom-price <text>', 'custom price text')
.option('--original-price-type <type>', 'original price type: AMOUNT | CUSTOM')
.option('--price <amount>', 'original price amount', validatePrice)
.option('--custom-original-price <text>', 'custom original price text')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action(async (options) => {
try {
const parentOptions = program.opts();
const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions);
const productHandler = new product_handler_1.ProductHandler(authConfig, serviceConfig);
const updateOptions = {
channelId: options.channelId,
productId: options.productId,
name: options.name,
status: options.status,
linkType: options.linkType,
cover: options.cover,
link: options.link,
pcLink: options.pcLink,
mobileLink: options.mobileLink,
wxMiniprogramLink: options.wxMiniprogramLink,
wxMiniprogramOriginalId: options.wxMiniprogramOriginalId,
mobileAppLink: options.mobileAppLink,
androidLink: options.androidLink,
iosLink: options.iosLink,
params: options.params,
productDesc: options.productDesc,
features: options.features,
btnShow: options.btnShow,
yield: options.yield,
priceType: options.priceType,
realPrice: options.realPrice,
customPrice: options.customPrice,
originalPriceType: options.originalPriceType,
price: options.price,
customOrignalPrice: options.customOriginalPrice,
output: options.output
};
await productHandler.updateProduct(updateOptions);
}
catch (error) {
(0, errors_1.logError)(error instanceof Error ? error : new Error(String(error)));
process.exit(1);
}
});
updateCmd.addHelpText('after', `
Examples:
# Update product name
$ polyv-live-cli product update -c "3151318" -p 12345 -n "ζ°εεε" --status 1 --link-type 10
# Change product status to off-shelf
$ polyv-live-cli product update -c "3151318" -p 12345 -n "εεε" --status 2 --link-type 10
`);
const deleteCmd = productCmd
.command('delete')
.description('Delete a product from channel')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('-p, --product-id <productId>', 'product ID', parseInteger)
.option('-f, --force', 'force delete without confirmation')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action(async (options) => {
try {
const parentOptions = program.opts();
const { authConfig, serviceConfig } = await loadAuthAndServiceConfig(parentOptions);
const productHandler = new product_handler_1.ProductHandler(authConfig, serviceConfig);
const deleteOptions = {
channelId: options.channelId,
productId: options.productId,
force: options.force || false,
output: options.output
};
await productHandler.deleteProduct(deleteOptions);
}
catch (error) {
(0, errors_1.logError)(error instanceof Error ? error : new Error(String(error)));
process.exit(1);
}
});
deleteCmd.addHelpText('after', `
Examples:
# Delete with confirmation
$ polyv-live-cli product delete -c "3151318" -p 12345
# Force delete without confirmation
$ polyv-live-cli product delete -c "3151318" -p 12345 --force
Note:
Deletion is permanent and cannot be undone.
Use --force to skip the confirmation prompt.
`);
productCmd.command('enabled')
.description('Get channel product library enabled status')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.getChannelProductEnabled(options)));
productCmd.command('update-enabled')
.description('Update channel product library enabled status')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('--enabled <yn>', 'enabled flag (Y|N)', api_command_1.validateYn)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.updateChannelProductEnabled(options)));
productCmd.command('batch-add')
.description('Batch add products to a channel')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('--products-json <json>', 'products JSON array', api_command_1.parseJsonArray)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.batchAddChannelProducts({
...options,
products: options.productsJson
})));
productCmd.command('batch-delete')
.description('Batch delete channel products')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('--product-ids <ids>', 'product IDs, comma-separated', api_command_1.parseNumberList)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.batchDeleteChannelProducts(options)));
productCmd.command('batch-shelf')
.description('Batch update channel product shelf status')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('--product-ids <ids>', 'product IDs, comma-separated', api_command_1.parseNumberList)
.requiredOption('--shelf <status>', 'shelf status: 1 (on shelf) | 2 (off shelf)', validateProductStatus)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.batchShelfChannelProducts(options)));
productCmd.command('shelf')
.description('Update one channel product shelf status')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('-p, --product-id <productId>', 'product ID', parseInteger)
.requiredOption('--shelf <status>', 'shelf status: 1 (on shelf) | 2 (off shelf)', validateProductStatus)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.shelfChannelProduct(options)));
productCmd.command('sort')
.description('Sort a channel product')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('-p, --product-id <productId>', 'product ID', parseInteger)
.requiredOption('--type <type>', '10 up, 20 down, 50 explicit rank', validateProductSortType)
.option('--sort <rank>', 'target rank when type=50', parseInteger)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.sortChannelProduct(options)));
productCmd.command('push')
.description('Push a channel product to viewers')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('-p, --product-id <productId>', 'product ID', parseInteger)
.option('--push-card-type <type>', 'push card type: smallCard | bigCard', validatePushCardType)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.pushChannelProduct(options)));
productCmd.command('cancel-push')
.description('Cancel a pushed channel product')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('-p, --product-id <productId>', 'product ID', parseInteger)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.cancelPushChannelProduct(options)));
productCmd.command('reference')
.description('Reference a platform product into a channel product library')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('--origin-id <id>', 'platform product origin ID')
.requiredOption('--status <status>', 'shelf status: 1 (on shelf) | 2 (off shelf)', validateProductStatus)
.option('--with-tags', 'sync platform tags')
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.referenceProduct(options)));
const pushRuleCmd = productCmd.command('push-rule').description('Manage channel product push rule');
pushRuleCmd.command('get')
.description('Get channel product push rule')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.getProductPushRule(options)));
pushRuleCmd.command('update')
.description('Update channel product push rule')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.option('--product-explain-enabled <yn>', 'product explain enabled (Y|N)', api_command_1.validateYn)
.option('--product-explaining-auto-push-and-sticky <yn>', 'auto push and sticky explaining product (Y|N)', api_command_1.validateYn)
.option('--product-list-sort-type <type>', 'product list sort type')
.option('--product-tag-sort-type <type>', 'product tag sort type')
.option('--product-push-rule <rule>', 'product push rule: smallCard | bigCard | chooseCard')
.option('--product-hot-effect-enabled <yn>', 'product hot effect enabled (Y|N)', api_command_1.validateYn)
.option('--normal-product-hot-effect-tips <text>', 'normal product hot effect tips')
.option('--job-product-hot-effect-tips <text>', 'job product hot effect tips')
.option('--finance-product-hot-effect-tips <text>', 'finance product hot effect tips')
.option('--out-link-product-redirect-enabled <yn>', 'out-link redirect enabled (Y|N)', api_command_1.validateYn)
.option('--product-tag-sort-order-ids <ids>', 'product tag sort order IDs, comma-separated', api_command_1.parseNumberList)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.updateProductPushRule(options)));
const channelTagCmd = productCmd.command('channel-tag').description('Manage channel product tags');
addPaging(channelTagCmd.command('list').description('List channel product tags'))
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.action((options) => withProductHandler(program, handler => handler.listChannelProductTags({
channelId: options.channelId,
page: options.page,
size: options.size,
output: options.output,
})));
channelTagCmd.command('create')
.description('Create a channel product tag')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('-n, --name <name>', 'tag name')
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.createChannelProductTag({
channelId: options.channelId,
name: options.name,
force: options.force,
output: options.output,
})));
channelTagCmd.command('update')
.description('Update a channel product tag')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('--id <id>', 'tag ID', parseInteger)
.requiredOption('-n, --name <name>', 'tag name')
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.updateChannelProductTag({
channelId: options.channelId,
id: options.id,
name: options.name,
force: options.force,
output: options.output,
})));
channelTagCmd.command('delete')
.description('Delete a channel product tag')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('--id <id>', 'tag ID', parseInteger)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.deleteChannelProductTag({
channelId: options.channelId,
id: options.id,
force: options.force,
output: options.output,
})));
const statsCmd = productCmd.command('stats').description('Query channel product statistics');
addPaging(statsCmd.command('list').description('List channel product statistics'))
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.option('--product-id <productId>', 'product ID')
.option('--product-name <productName>', 'product name filter')
.option('--session-id <sessionId>', 'session ID')
.action((options) => withProductHandler(program, handler => handler.listProductStats({
channelId: options.channelId,
productId: options.productId,
productName: options.productName,
sessionId: options.sessionId,
page: options.page,
size: options.size,
output: options.output,
})));
statsCmd.command('summary')
.description('Get channel product statistics summary')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.option('--session-id <sessionId>', 'session ID')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.getProductStatsSummary(options)));
productCmd.command('rank')
.description('Set channel product rank')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('-p, --product-id <productId>', 'product ID', parseInteger)
.requiredOption('--rank <rank>', 'target rank', parseInteger)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.sortChannelProductRank(options)));
productCmd.command('topping')
.description('Top a channel product')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('-p, --product-id <productId>', 'product ID', parseInteger)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.toppingChannelProduct(options)));
productCmd.command('untopping')
.description('Cancel topping for a channel product')
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.requiredOption('-p, --product-id <productId>', 'product ID', parseInteger)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.untoppingChannelProduct(options)));
const libraryCmd = productCmd.command('library').description('Manage user-level product library');
addPaging(libraryCmd.command('list').description('List user-level product library'))
.option('-k, --keyword <keyword>', 'keyword search')
.option('-t, --product-type <type>', 'product type filter')
.action((options) => withProductHandler(program, handler => handler.listUserProducts({
page: options.page,
size: options.size,
keyword: options.keyword,
productType: options.productType,
output: options.output,
})));
addUserProductOptions(libraryCmd.command('create').description('Create a user-level product'))
.action((options) => withProductHandler(program, handler => handler.createUserProduct(toUserProductOptions(options))));
addUserProductOptions(libraryCmd.command('update').description('Update a user-level product'), true)
.action((options) => withProductHandler(program, handler => handler.updateUserProduct(toUserProductOptions(options))));
libraryCmd.command('delete')
.description('Delete a user-level product')
.requiredOption('-p, --product-id <productId>', 'user product ID')
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.deleteUserProduct({
productId: options.productId,
force: options.force,
output: options.output,
})));
const tagCmd = productCmd.command('tag').description('Manage user-level product tags');
addPaging(tagCmd.command('list').description('List user-level product tags'))
.requiredOption('-c, --channel-id <channelId>', 'channel ID')
.option('-k, --keyword <keyword>', 'keyword search')
.action((options) => withProductHandler(program, handler => handler.listProductTags({
channelId: options.channelId,
page: options.page,
size: options.size,
keyword: options.keyword,
output: options.output,
})));
tagCmd.command('create')
.description('Create a user-level product tag')
.requiredOption('-n, --name <name>', 'tag name')
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.createProductTag({
name: options.name,
force: options.force,
output: options.output,
})));
tagCmd.command('update')
.description('Update a user-level product tag')
.requiredOption('--id <id>', 'tag ID', parseInteger)
.requiredOption('-n, --name <name>', 'tag name')
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.updateProductTag({
id: options.id,
name: options.name,
force: options.force,
output: options.output,
})));
tagCmd.command('delete')
.description('Delete a user-level product tag')
.requiredOption('--id <id>', 'tag ID', parseInteger)
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.deleteProductTag({
id: options.id,
force: options.force,
output: options.output,
})));
const orderCmd = productCmd.command('order').description('Manage user-level product orders');
addPaging(orderCmd.command('list').description('List user-level product orders'))
.action((options) => withProductHandler(program, handler => handler.listProductOrders({
page: options.page,
size: options.size,
output: options.output,
})));
orderCmd.command('get')
.description('Get a user-level product order')
.requiredOption('--order-no <orderNo>', 'order number')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.getProductOrder({
orderNo: options.orderNo,
output: options.output,
})));
orderCmd.command('batch-status')
.description('Batch update user-level product order status')
.requiredOption('--order-nos <orderNos>', 'comma-separated order numbers')
.requiredOption('--status <status>', 'new order status')
.option('-f, --force', 'skip confirmation prompt')
.option('-o, --output <format>', 'output format (table|json)', validateOutputFormat, 'table')
.action((options) => withProductHandler(program, handler => handler.batchUpdateProductOrderStatus({
orderNos: options.orderNos,
status: options.status,
force: options.force,
output: options.output,
})));
}
//# sourceMappingURL=product.commands.js.map