390 lines
15 KiB
JavaScript
390 lines
15 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const fs = require('fs');
|
|||
|
|
|
|||
|
|
function emit(stage, message, status = 'running', extra = {}) {
|
|||
|
|
process.stderr.write(JSON.stringify({
|
|||
|
|
type: 'process_event',
|
|||
|
|
stage,
|
|||
|
|
message,
|
|||
|
|
status,
|
|||
|
|
timestamp: new Date().toISOString(),
|
|||
|
|
...extra,
|
|||
|
|
}) + '\n');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function readInput() {
|
|||
|
|
const raw = String(process.argv[2] || process.env.SKILL_INPUT || process.env.AIFLOW_SKILL_INPUT || fs.readFileSync(0, 'utf8')).trim();
|
|||
|
|
return raw ? JSON.parse(raw) : {};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function str(value) {
|
|||
|
|
if (value === undefined || value === null || value === '') return '-';
|
|||
|
|
return String(value).replace(/\|/g, '\\|').replace(/\r?\n/g, '<br>');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function raw(value) {
|
|||
|
|
if (value === undefined || value === null || value === '') return '';
|
|||
|
|
return String(value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function hasText(value) {
|
|||
|
|
return value !== undefined && value !== null && String(value).trim() !== '' && String(value).trim() !== 'null';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function normalizeStatus(status) {
|
|||
|
|
const text = raw(status).trim().replace(/[-\s]/g, '_').toLowerCase();
|
|||
|
|
if (['pass', 'passed', 'approved', 'approve', 'success', 'ok', '审核通过', '通过'].includes(text)) return 'PASS';
|
|||
|
|
if (['reject', 'rejected', 'deny', 'denied', 'refuse', 'refused', '驳回', '拒绝', '不通过'].includes(text)) return 'REJECT';
|
|||
|
|
if (['manual_review', 'manual', 'review', '人工', '转人工', '人工复核', '人工复审'].includes(text)) return 'MANUAL_REVIEW';
|
|||
|
|
if (['failed', 'fail', 'failure', 'error', '异常', '失败'].includes(text)) return 'MANUAL_REVIEW';
|
|||
|
|
return '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function inferStatus(audit) {
|
|||
|
|
const normalized = normalizeStatus(audit.status || audit.decision || audit.auditDecision || audit.result);
|
|||
|
|
if (normalized) return normalized;
|
|||
|
|
const rules = Array.isArray(audit.rules) ? audit.rules : [];
|
|||
|
|
const fieldChecks = Array.isArray(audit.fieldChecks) ? audit.fieldChecks : [];
|
|||
|
|
if (rules.some(r => normalizeStatus(r.status) === 'REJECT')) return 'REJECT';
|
|||
|
|
if (rules.some(r => normalizeStatus(r.status) === 'MANUAL_REVIEW')) return 'MANUAL_REVIEW';
|
|||
|
|
if (rules.length && rules.every(r => normalizeStatus(r.status) === 'PASS')) return 'PASS';
|
|||
|
|
if (fieldChecks.some(r => normalizeRuleStatusForReport(r.result) === 'REJECT')) return 'REJECT';
|
|||
|
|
if (fieldChecks.some(r => {
|
|||
|
|
const result = normalizeRuleStatusForReport(r.result);
|
|||
|
|
return result === 'MANUAL_REVIEW' || result === 'REVIEW';
|
|||
|
|
})) return 'MANUAL_REVIEW';
|
|||
|
|
if (fieldChecks.length && fieldChecks.every(r => {
|
|||
|
|
const result = normalizeRuleStatusForReport(r.result);
|
|||
|
|
return result === 'PASS' || result === 'SKIP';
|
|||
|
|
})) return 'PASS';
|
|||
|
|
const reason = raw(audit.reason || audit.summary);
|
|||
|
|
if (/审核通过|建议通过|通过/.test(reason)) return 'PASS';
|
|||
|
|
if (/驳回|拒绝|不通过/.test(reason)) return 'REJECT';
|
|||
|
|
if (/转人工|人工复核|人工复审/.test(reason)) return 'MANUAL_REVIEW';
|
|||
|
|
return 'MANUAL_REVIEW';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function formatLocalGeneratedAt(value) {
|
|||
|
|
const explicit = raw(value);
|
|||
|
|
if (explicit) return explicit.replace(/T(\d{2}:\d{2}:\d{2})(?:\.\d{3})?Z$/, ' $1');
|
|||
|
|
const date = new Date();
|
|||
|
|
const parts = new Intl.DateTimeFormat('zh-CN', {
|
|||
|
|
timeZone: 'Asia/Shanghai',
|
|||
|
|
year: 'numeric',
|
|||
|
|
month: '2-digit',
|
|||
|
|
day: '2-digit',
|
|||
|
|
hour: '2-digit',
|
|||
|
|
minute: '2-digit',
|
|||
|
|
second: '2-digit',
|
|||
|
|
hour12: false,
|
|||
|
|
}).formatToParts(date).reduce((acc, part) => {
|
|||
|
|
acc[part.type] = part.value;
|
|||
|
|
return acc;
|
|||
|
|
}, {});
|
|||
|
|
return `${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute}:${parts.second}`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function businessText(value) {
|
|||
|
|
return raw(value).replace(/(公户车检查|购买方名称比对|购车时间超期|身份证比对|发票比对|车辆类型判断|PlusC车龄限制):/g, '$1:');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function statusLabel(status) {
|
|||
|
|
status = normalizeStatus(status) || status;
|
|||
|
|
if (status === 'PASS') return '通过';
|
|||
|
|
if (status === 'REJECT') return '驳回';
|
|||
|
|
if (status === 'MANUAL_REVIEW') return '转人工';
|
|||
|
|
return status || '未知';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function statusIcon(status) {
|
|||
|
|
status = normalizeStatus(status) || status;
|
|||
|
|
if (status === 'PASS') return 'PASS';
|
|||
|
|
if (status === 'REJECT') return 'REJECT';
|
|||
|
|
if (status === 'MANUAL_REVIEW') return 'MANUAL';
|
|||
|
|
return 'MANUAL';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function ruleStatus(status) {
|
|||
|
|
status = normalizeStatus(status) || status;
|
|||
|
|
if (status === 'PASS') return '通过';
|
|||
|
|
if (status === 'REJECT') return '驳回';
|
|||
|
|
if (status === 'MANUAL_REVIEW') return '转人工';
|
|||
|
|
return str(status);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function firstValue(obj, keys) {
|
|||
|
|
if (!obj || typeof obj !== 'object') return '';
|
|||
|
|
for (const key of keys) {
|
|||
|
|
const value = obj[key];
|
|||
|
|
if (hasText(value)) return value;
|
|||
|
|
}
|
|||
|
|
return '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const EVIDENCE_LABELS = {
|
|||
|
|
carHost: '车主类型',
|
|||
|
|
buyerName: '发票购买方',
|
|||
|
|
companyNames: '疑似企业名称',
|
|||
|
|
orderName: '订单客户姓名',
|
|||
|
|
invoiceBuyerName: '发票购买方',
|
|||
|
|
invoiceIssueDate: '发票开票日期',
|
|||
|
|
orderPurchaseDate: '订单购车日期',
|
|||
|
|
referenceDate: '审核基准日期',
|
|||
|
|
maxPurchaseAgeDays: '允许最长间隔',
|
|||
|
|
ageDays: '实际间隔天数',
|
|||
|
|
productName: '产品名称',
|
|||
|
|
packageType: '权益套餐类型',
|
|||
|
|
isPlusC: '是否PlusC/代步权益',
|
|||
|
|
vehicleAgeStartSource: '车龄起算字段',
|
|||
|
|
vehicleAgeStartDate: '车龄起算日期',
|
|||
|
|
plusCMaxVehicleAgeYears: 'PlusC允许最长车龄',
|
|||
|
|
vehicleAgeDays: '车辆实际车龄天数',
|
|||
|
|
vehicleAgeMonths: '车辆实际车龄月数',
|
|||
|
|
vehicleAgeYears: '车辆实际车龄年数',
|
|||
|
|
ocrName: '身份证OCR姓名',
|
|||
|
|
orderIdNumber: '订单身份证号',
|
|||
|
|
ocrIdNumber: '身份证OCR号码',
|
|||
|
|
orderVin: '订单VIN/车架号',
|
|||
|
|
invoiceVin: '发票VIN/车架号',
|
|||
|
|
orderEngineNo: '订单发动机号/电机号',
|
|||
|
|
invoiceEngineNo: '发票发动机号/电机号',
|
|||
|
|
orderModel: '订单车型/产品',
|
|||
|
|
invoiceBrandModel: '发票厂牌型号',
|
|||
|
|
sellerName: '销货单位',
|
|||
|
|
vehicleType: '发票车辆类型',
|
|||
|
|
brandModel: '发票厂牌型号',
|
|||
|
|
invoiceTotalAmount: '发票价税合计',
|
|||
|
|
orderVehiclePrice: '订单车辆价格',
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
function evidenceLabel(key) {
|
|||
|
|
return EVIDENCE_LABELS[key] || key;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function evidenceValue(key, value) {
|
|||
|
|
if (value === undefined || value === null || value === '') return '';
|
|||
|
|
if (Array.isArray(value)) return value.filter(v => v !== undefined && v !== null && String(v) !== '').join('、');
|
|||
|
|
if (key === 'carHost') {
|
|||
|
|
const text = String(value).trim();
|
|||
|
|
if (text === '0' || text === '个人' || text.toLowerCase() === 'false') return '个人';
|
|||
|
|
if (text === '1') return '企业/公户';
|
|||
|
|
return text;
|
|||
|
|
}
|
|||
|
|
if (key === 'maxPurchaseAgeDays') return `${value}天`;
|
|||
|
|
if (key === 'ageDays') return `${value}天`;
|
|||
|
|
if (key === 'isPlusC') return value ? '是' : '否';
|
|||
|
|
if (key === 'packageType') return String(value) === '3' ? 'PlusC/代步权益' : value;
|
|||
|
|
if (key === 'plusCMaxVehicleAgeYears') return `${value}年`;
|
|||
|
|
if (key === 'vehicleAgeDays') return `${value}天`;
|
|||
|
|
if (key === 'vehicleAgeMonths') return `${value}个月`;
|
|||
|
|
if (key === 'vehicleAgeYears') return `${value}年`;
|
|||
|
|
if (key === 'vehicleAgeStartSource') {
|
|||
|
|
const labels = {
|
|||
|
|
firstRegistrationDate: '首次注册日期',
|
|||
|
|
orderPurchaseDate: '订单购车日期',
|
|||
|
|
invoiceIssueDate: '发票开票日期',
|
|||
|
|
};
|
|||
|
|
return labels[value] || value;
|
|||
|
|
}
|
|||
|
|
return value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function renderEvidence(evidence) {
|
|||
|
|
if (!evidence || typeof evidence !== 'object' || !Object.keys(evidence).length) return '-';
|
|||
|
|
return Object.entries(evidence)
|
|||
|
|
.map(([key, value]) => [evidenceLabel(key), evidenceValue(key, value)])
|
|||
|
|
.filter(([, value]) => value !== undefined && value !== null && String(value) !== '')
|
|||
|
|
.map(([label, value]) => `${label}:${Array.isArray(value) ? value.join('、') : value}`)
|
|||
|
|
.join('<br>') || '-';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function renderOrderTable(audit, order) {
|
|||
|
|
const n = audit.normalized || {};
|
|||
|
|
const rows = [
|
|||
|
|
['订单号', firstValue(order, ['orderNo', 'orderNum']) || n.orderNo],
|
|||
|
|
['业务单号', firstValue(order, ['businessNo', 'orderBusinessNo'])],
|
|||
|
|
['客户姓名', n.orderName || firstValue(order, ['cardName', 'ownerName', 'customerName'])],
|
|||
|
|
['身份证号', n.orderIdNumber || firstValue(order, ['cardNumber', 'idNumber'])],
|
|||
|
|
['VIN/车架号', n.orderVin || firstValue(order, ['carFrame', 'vin'])],
|
|||
|
|
['车辆类型', n.vehicleType || firstValue(order, ['vehicleType', 'carType'])],
|
|||
|
|
['厂牌型号', n.brandModel || firstValue(order, ['modelName', 'carModel'])],
|
|||
|
|
['计划号', firstValue(order, ['productNo', 'planNo', 'seresPlanNo'])],
|
|||
|
|
['产品名称', firstValue(order, ['productName', 'goodsName'])],
|
|||
|
|
['门店名称', firstValue(order, ['factroyName', 'factoryName', 'dealerName'])],
|
|||
|
|
['车牌号', firstValue(order, ['carPlate', 'plateNo'])],
|
|||
|
|
['购车日期', firstValue(order, ['carPurchaseTime', 'purchaseTime', 'purchaseDate'])],
|
|||
|
|
['里程', firstValue(order, ['mileage', 'kilometers'])],
|
|||
|
|
['车辆价格', firstValue(order, ['vehiclePrice'])],
|
|||
|
|
['车主类型', firstValue(order, ['carHost'])],
|
|||
|
|
['保单号', firstValue(order, ['policyNo', 'policyno', 'commercialInsuranceNo', 'seresInsuranceNo'])],
|
|||
|
|
['保险公司', firstValue(order, ['insuranceCompany'])],
|
|||
|
|
['商业险日期', firstValue(order, ['commercialInsuranceDate', 'effectiveDate', 'seresEffectiveDate'])],
|
|||
|
|
['商业险URL', firstValue(order, ['commercialInsuranceUrl', 'pingAnUrl'])],
|
|||
|
|
['附件模式', firstValue(order, ['seresAttachmentMode', 'attachmentExpectation'])],
|
|||
|
|
['发票URL', firstValue(order, ['billUrl', 'carBill'])],
|
|||
|
|
['身份证URL', firstValue(order, ['cardUrl'])],
|
|||
|
|
['发票日期', n.invoiceIssueDate || firstValue(order, ['invoiceIssueDate', 'seresInvoiceDate'])],
|
|||
|
|
].filter(([, v]) => hasText(v));
|
|||
|
|
return [
|
|||
|
|
'| 项目 | 内容 |',
|
|||
|
|
'| --- | --- |',
|
|||
|
|
...rows.map(([k, v]) => `| ${str(k)} | ${str(v)} |`),
|
|||
|
|
].join('\n');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function normalizeRuleStatusForReport(status) {
|
|||
|
|
const normalized = normalizeStatus(status);
|
|||
|
|
if (normalized === 'PASS') return 'PASS';
|
|||
|
|
if (normalized === 'REJECT') return 'REJECT';
|
|||
|
|
if (normalized === 'MANUAL_REVIEW') return 'MANUAL_REVIEW';
|
|||
|
|
const text = raw(status).trim().toUpperCase();
|
|||
|
|
if (['SKIP', 'IGNORED', 'N_A', 'NA', 'NOT_APPLICABLE'].includes(text)) return 'SKIP';
|
|||
|
|
return text || '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function renderRulesTable(rules, fieldChecks) {
|
|||
|
|
if (Array.isArray(rules) && rules.length > 0) {
|
|||
|
|
return [
|
|||
|
|
'| 序号 | 规则 | 结论 | 说明 | 证据 |',
|
|||
|
|
'| --- | --- | --- | --- | --- |',
|
|||
|
|
...rules.map((r, idx) => `| ${idx + 1} | ${str(r.name || r.id)} | ${ruleStatus(r.status)} | ${str(businessText(r.reason))} | ${renderEvidence(r.evidence)} |`),
|
|||
|
|
].join('\n');
|
|||
|
|
}
|
|||
|
|
if (Array.isArray(fieldChecks) && fieldChecks.length > 0) {
|
|||
|
|
return [
|
|||
|
|
'| 序号 | 字段 | 期望 | 实际 | 结论 | 说明 |',
|
|||
|
|
'| --- | --- | --- | --- | --- | --- |',
|
|||
|
|
...fieldChecks.map((check, idx) => {
|
|||
|
|
const result = normalizeRuleStatusForReport(check.result);
|
|||
|
|
const statusText =
|
|||
|
|
result === 'PASS' ? '通过'
|
|||
|
|
: result === 'REJECT' ? '驳回'
|
|||
|
|
: result === 'MANUAL_REVIEW' ? '转人工'
|
|||
|
|
: result === 'SKIP' ? '不适用'
|
|||
|
|
: str(check.result);
|
|||
|
|
return `| ${idx + 1} | ${str(check.field)} | ${str(check.expected)} | ${str(check.actual)} | ${statusText} | ${str(check.message)} |`;
|
|||
|
|
}),
|
|||
|
|
].join('\n');
|
|||
|
|
}
|
|||
|
|
return [
|
|||
|
|
'暂无独立规则明细;本次报告按结构化订单数据生成综合结论。',
|
|||
|
|
].join('\n');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function findRule(rules, id) {
|
|||
|
|
return (rules || []).find(r => r.id === id) || {};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function renderCompareSection(audit) {
|
|||
|
|
const rules = audit.rules || [];
|
|||
|
|
const idRule = findRule(rules, 'R4_ID_CARD');
|
|||
|
|
const invoiceRule = findRule(rules, 'R5_INVOICE');
|
|||
|
|
const id = idRule.evidence || {};
|
|||
|
|
const inv = invoiceRule.evidence || {};
|
|||
|
|
const sections = [];
|
|||
|
|
if ([id.orderName, id.ocrName, id.orderIdNumber, id.ocrIdNumber].some(hasText)) {
|
|||
|
|
sections.push([
|
|||
|
|
'## 身份证比对详情',
|
|||
|
|
'',
|
|||
|
|
'| 字段 | 订单录入 | OCR识别 |',
|
|||
|
|
'| --- | --- | --- |',
|
|||
|
|
`| 姓名 | ${str(id.orderName)} | ${str(id.ocrName)} |`,
|
|||
|
|
`| 身份证号 | ${str(id.orderIdNumber)} | ${str(id.ocrIdNumber)} |`,
|
|||
|
|
].join('\n'));
|
|||
|
|
}
|
|||
|
|
if ([inv.orderVin, inv.invoiceVin, inv.orderEngineNo, inv.invoiceEngineNo, inv.orderModel, inv.invoiceBrandModel, inv.sellerName].some(hasText)) {
|
|||
|
|
sections.push([
|
|||
|
|
'## 发票比对详情',
|
|||
|
|
'',
|
|||
|
|
'| 字段 | 订单录入 | 发票OCR |',
|
|||
|
|
'| --- | --- | --- |',
|
|||
|
|
`| VIN/车架号 | ${str(inv.orderVin)} | ${str(inv.invoiceVin)} |`,
|
|||
|
|
`| 发动机号 | ${str(inv.orderEngineNo)} | ${str(inv.invoiceEngineNo)} |`,
|
|||
|
|
`| 车型/产品 | ${str(inv.orderModel)} | ${str(inv.invoiceBrandModel)} |`,
|
|||
|
|
`| 销货单位 | - | ${str(inv.sellerName)} |`,
|
|||
|
|
].join('\n'));
|
|||
|
|
}
|
|||
|
|
return sections.join('\n\n');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function renderMarkdown(input) {
|
|||
|
|
const audit = input.auditResult;
|
|||
|
|
if (!audit || typeof audit !== 'object') throw new Error('auditResult is required');
|
|||
|
|
const companyName = raw(input.companyName) || '车主权益管理系统';
|
|||
|
|
const generatedAt = formatLocalGeneratedAt(input.generatedAt);
|
|||
|
|
const logo = raw(input.logoUrl);
|
|||
|
|
const status = inferStatus(audit);
|
|||
|
|
const score = audit.score ?? '-';
|
|||
|
|
const order = input.order || {};
|
|||
|
|
const compareSection = renderCompareSection(audit);
|
|||
|
|
const summaryText = raw(audit.summary || audit.reason || '').trim();
|
|||
|
|
const rules = Array.isArray(audit.rules) ? audit.rules : [];
|
|||
|
|
const fieldChecks = Array.isArray(audit.fieldChecks) ? audit.fieldChecks : [];
|
|||
|
|
const failedCount = rules.length
|
|||
|
|
? rules.filter(r => normalizeRuleStatusForReport(r.status) !== 'PASS').length
|
|||
|
|
: fieldChecks.filter(r => {
|
|||
|
|
const result = normalizeRuleStatusForReport(r.result);
|
|||
|
|
return result === 'REJECT' || result === 'MANUAL_REVIEW';
|
|||
|
|
}).length;
|
|||
|
|
const totalCount = rules.length || fieldChecks.length;
|
|||
|
|
const title = logo
|
|||
|
|
? `<p align="center"><img src="${logo}" alt="${companyName}" height="48"></p>\n\n# ${companyName}投保审核报告`
|
|||
|
|
: `# ${companyName}投保审核报告`;
|
|||
|
|
const summary = summaryText || (failedCount > 0
|
|||
|
|
? `${failedCount}项规则未通过,存在需驳回或人工复核事项。`
|
|||
|
|
: `${totalCount}项规则均通过,未发现需驳回或人工复核事项。`);
|
|||
|
|
|
|||
|
|
return [
|
|||
|
|
title,
|
|||
|
|
'',
|
|||
|
|
`> 生成时间:${generatedAt}`,
|
|||
|
|
'',
|
|||
|
|
'## 审核结论',
|
|||
|
|
'',
|
|||
|
|
`| 结论 | 评分 | 结论码 | 主要原因 |`,
|
|||
|
|
`| --- | ---: | --- | --- |`,
|
|||
|
|
`| ${statusLabel(status)} | ${score} | ${statusIcon(status)} | ${str(businessText(audit.reason) || summary)} |`,
|
|||
|
|
'',
|
|||
|
|
'## 订单信息',
|
|||
|
|
'',
|
|||
|
|
renderOrderTable(audit, order),
|
|||
|
|
'',
|
|||
|
|
'## 规则校验',
|
|||
|
|
'',
|
|||
|
|
renderRulesTable(rules, fieldChecks),
|
|||
|
|
'',
|
|||
|
|
compareSection,
|
|||
|
|
compareSection ? '' : null,
|
|||
|
|
'## 综合摘要',
|
|||
|
|
'',
|
|||
|
|
`${str(businessText(summary))}`,
|
|||
|
|
'',
|
|||
|
|
'> 本报告由AI审核流程自动生成,结论基于订单录入数据及已提供的结构化/OCR结果。转人工项需以业务人员复核结果为准。',
|
|||
|
|
'',
|
|||
|
|
].filter(line => line !== null).join('\n');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
(async () => {
|
|||
|
|
try {
|
|||
|
|
const input = readInput();
|
|||
|
|
emit('render', '生成Markdown审核报告');
|
|||
|
|
const reportMarkdown = renderMarkdown(input);
|
|||
|
|
const audit = input.auditResult || {};
|
|||
|
|
emit('complete', '审核报告生成完成', 'completed');
|
|||
|
|
process.stdout.write(JSON.stringify({
|
|||
|
|
success: true,
|
|||
|
|
reportMarkdown,
|
|||
|
|
summary: businessText(audit.reason || audit.summary || ''),
|
|||
|
|
status: inferStatus(audit),
|
|||
|
|
score: audit.score,
|
|||
|
|
}));
|
|||
|
|
} catch (err) {
|
|||
|
|
process.stdout.write(JSON.stringify({
|
|||
|
|
success: false,
|
|||
|
|
error: err && err.message ? err.message : String(err),
|
|||
|
|
}));
|
|||
|
|
}
|
|||
|
|
})();
|