Files
justvitamin/scripts/generate-dataforseo-support-escalation.ts
T
Omair Saleh 056c47581f feat: editorial review dashboard + elite-grade pilot batch (5 SKUs)
Ships the second dashboard surface — a Pattern Library + Preview Theatre — that
presents the 4-section PDP pilot batch back to Umar, compliance, and the board
in an editorial format. Adds the full data layer that drives it: 5 source-backed
per-SKU drafts at QA 100/100, 15 competitor PDP semantic extracts, PubMed
evidence packs, EFSA claims library extension, JV brand voice guide, hand-curated
product FAQs, and the Matrixify-ready CSV exports for Lewis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 18:50:09 +08:00

150 lines
7.7 KiB
TypeScript

#!/usr/bin/env bun
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
import { join } from 'path'
const root = process.cwd()
const outRoot = join(root, 'content_population_exports')
mkdirSync(outRoot, { recursive: true })
function readJson(path: string, fallback: any = null) {
if (!existsSync(path)) return fallback
return JSON.parse(readFileSync(path, 'utf8').replace(/^\uFEFF/, ''))
}
function csv(rows: any[], headers: string[]) {
const esc = (value: any) => {
if (value === undefined || value === null) value = ''
value = Array.isArray(value) ? value.join(' | ') : String(value)
return /[",\n\r]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value
}
return [headers.join(','), ...rows.map(row => headers.map(header => esc(row[header])).join(','))].join('\n') + '\n'
}
const generatedAt = new Date().toISOString()
const reviewsPilot = readJson(join(outRoot, 'dataforseo_reviews_live_pilot.json'), {})
const reviewsDocsStatus = readJson(join(outRoot, 'dataforseo_reviews_docs_status.json'), {})
const liveConnection = readJson(join(outRoot, 'dataforseo_live_connection.json'), {})
const taskPreflight = readJson(join(outRoot, 'dataforseo_task_preflight.json'), {})
const payload = readJson(join(outRoot, 'dataforseo_amazon_reviews_task_payload.json'), [])
const latestFailure = reviewsPilot.latestArchivedReviewPost || liveConnection.reviewEndpointStatus || null
const endpoint = reviewsPilot.endpoint || 'POST https://api.dataforseo.com/v3/merchant/amazon/reviews/task_post'
const issueRows = [
{
Item: 'Account connection',
Evidence: `${liveConnection.accountConnection?.status_code || 'n/a'} ${liveConnection.accountConnection?.status || liveConnection.accountConnection?.status_message || ''}`.trim(),
ActionNeeded: 'Credentials/account work; issue appears endpoint-specific.'
},
{
Item: 'Amazon Products endpoint',
Evidence: `${liveConnection.counts?.exactAsinsConfirmed || 0}/${liveConnection.counts?.targets || 0} exact ASINs confirmed from live product-result evidence`,
ActionNeeded: 'Products endpoint is usable; reviews endpoint is the blocker.'
},
{
Item: 'Amazon Reviews endpoint',
Evidence: latestFailure ? `${latestFailure.status_code}: ${latestFailure.status_message}` : 'No archived provider failure found',
ActionNeeded: 'Ask whether Merchant Amazon Reviews task_post is disabled for this account/API version/region and what replacement/restoration path exists.'
},
{
Item: 'Pilot payload',
Evidence: `${Array.isArray(payload) ? payload.length : 0} ASIN rows at depth 10, United Kingdom / English (United Kingdom)`,
ActionNeeded: 'Payload is intentionally capped; support can reproduce with one row if needed.'
},
{
Item: 'Official DataForSEO docs status',
Evidence: reviewsDocsStatus.docsUrl
? `endpointTemporarilyUnavailable=${reviewsDocsStatus.endpointTemporarilyUnavailable === true}; ${reviewsDocsStatus.counts?.passing || 0}/${reviewsDocsStatus.counts?.checks || 0} checks passing; docs=${reviewsDocsStatus.docsUrl}`
: 'No official docs status artifact found',
ActionNeeded: 'Ask support whether the official temporary-unavailable note applies globally, to this account, or to the Amazon UK Reviews path specifically.'
},
{
Item: 'Local spend guard',
Evidence: `${taskPreflight.counts?.passing || 0}/${taskPreflight.counts?.checks || 0} checks passing; paid posting blocked until approval gates are intentionally opened`,
ActionNeeded: 'No broad scraping or repeated retries until provider confirms endpoint availability.'
}
]
const supportMessage = [
'Hello DataForSEO support,',
'',
'We can authenticate successfully and use the Merchant Amazon Products endpoint, but Merchant Amazon Reviews task creation is returning a provider-level unavailable response before any task IDs are created.',
reviewsDocsStatus.endpointTemporarilyUnavailable === true
? 'Your public API documentation also currently marks the Amazon Reviews Task POST endpoint as temporarily unavailable.'
: '',
'',
`Endpoint: ${endpoint}`,
latestFailure ? `Response: ${latestFailure.status_code} ${latestFailure.status_message}` : 'Response: no archived response available',
latestFailure?.cost !== undefined ? `Cost reported: ${latestFailure.cost}` : '',
latestFailure?.tasks_count !== undefined ? `Tasks count: ${latestFailure.tasks_count}` : '',
'',
'The intended pilot is small: 6 Amazon UK ASINs, depth 10, location_name=United Kingdom, language_name=English (United Kingdom). The Products endpoint already returned usable ASIN evidence for those rows.',
'',
'Could you confirm whether /v3/merchant/amazon/reviews/task_post is temporarily unavailable globally, unavailable for this account, or requires a different endpoint/parameter set? If it is disabled, please advise the expected restoration path or replacement endpoint.',
'',
'No credentials are included in this packet.'
].filter(Boolean).join('\n')
const manifest = {
generatedAt,
ready: Boolean(latestFailure && Number(latestFailure.status_code) === 50304),
provider: 'DataForSEO',
endpoint,
issue: latestFailure ? `${latestFailure.status_code}: ${latestFailure.status_message}` : 'No provider failure archived yet',
credentialPrinted: false,
counts: {
payloadRows: Array.isArray(payload) ? payload.length : 0,
liveConfirmedAsins: liveConnection.counts?.exactAsinsConfirmed || 0,
missingAsins: liveConnection.counts?.missingAsins || 0,
preflightPassing: taskPreflight.counts?.passing || 0,
preflightChecks: taskPreflight.counts?.checks || 0
,
docsStatusPassing: reviewsDocsStatus.counts?.passing || 0,
docsStatusChecks: reviewsDocsStatus.counts?.checks || 0
},
files: {
supportMd: 'content_population_exports/dataforseo_support_escalation.md',
supportJson: 'content_population_exports/dataforseo_support_escalation.json',
supportCsv: 'content_population_exports/dataforseo_support_escalation.csv',
reviewsPilot: 'content_population_exports/dataforseo_reviews_live_pilot.json',
liveConnection: 'content_population_exports/dataforseo_live_connection.json',
reviewsDocsStatus: 'content_population_exports/dataforseo_reviews_docs_status.json',
payload: 'content_population_exports/dataforseo_amazon_reviews_task_payload.json'
},
issueRows,
supportMessage,
payloadPreview: Array.isArray(payload) ? payload.slice(0, 6).map((row: any) => ({ asin: row.asin, depth: row.depth, location_name: row.location_name, language_name: row.language_name, tag: row.tag })) : []
}
writeFileSync(join(outRoot, 'dataforseo_support_escalation.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf8')
writeFileSync(join(outRoot, 'dataforseo_support_escalation.csv'), csv(issueRows, ['Item', 'Evidence', 'ActionNeeded']), 'utf8')
writeFileSync(join(outRoot, 'dataforseo_support_escalation.md'), [
'# DataForSEO support escalation packet',
'',
`Generated: ${generatedAt}`,
'',
`Ready to send: ${manifest.ready ? 'YES' : 'NO'}`,
'',
'## Summary',
`- Provider: ${manifest.provider}`,
`- Endpoint: ${endpoint}`,
`- Issue: ${manifest.issue}`,
`- Credentials included: no`,
`- Pilot payload rows: ${manifest.counts.payloadRows}`,
`- Live-confirmed ASINs: ${manifest.counts.liveConfirmedAsins}`,
'',
'## Evidence checklist',
...issueRows.map(row => `- **${row.Item}:** ${row.Evidence}${row.ActionNeeded}`),
'',
'## Message to send',
'```text',
supportMessage,
'```',
'',
'## Payload preview',
...manifest.payloadPreview.map((row: any) => `- ${row.tag}: ${row.asin}, depth ${row.depth}, ${row.location_name} / ${row.language_name}`),
''
].join('\n'), 'utf8')
console.log(`DataForSEO support escalation ready: ${manifest.ready}`)
console.log(`Issue: ${manifest.issue}`)
console.log(`Payload rows: ${manifest.counts.payloadRows}`)