report-bridge.mjs 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637
  1. import fs from 'node:fs';
  2. import path from 'node:path';
  3. import crypto from 'node:crypto';
  4. import {pathToFileURL} from 'node:url';
  5. import {createFmodeClient} from './fmode-client.mjs';
  6. export async function previewReport({reportDir,seriesKey,version,reportType,dataMode,artifactSubtype,stageCode},client=createFmodeClient()){
  7. if(!stageCode||!seriesKey||!Number.isInteger(Number(version))||Number(version)<1)throw Error('必须明确提供 stageCode、seriesKey 和正整数 version');
  8. const html=fs.readFileSync(path.join(reportDir,'report.html'),'utf8'),hash=crypto.createHash('sha256').update(Buffer.from(html)).digest('hex');
  9. const reportIdentity={reportSeriesKey:seriesKey,version:Number(version),reportContentHash:hash};
  10. const preview=await client.invoke('ksfCreateReportPreview',{requestId:'preview:'+seriesKey+'@v'+version+':'+hash,reportIdentity,html,reportType,dataMode,...(artifactSubtype?{artifactSubtype}:{})});
  11. return{...preview,stageCode,reportType,dataMode,...(artifactSubtype?{artifactSubtype}:{}),employeeActionRequired:true};
  12. }
  13. export async function submitConfirmedReport(opts){
  14. if(!opts.stageCode||!opts.previewId||!opts.skillDir)throw Error('必须提供 stageCode、previewId 和冻结 r7 的 skillDir');
  15. const client=createFmodeClient({reportMetadata:{stageCode:opts.stageCode}});
  16. const html=fs.readFileSync(path.join(opts.reportDir,'report.html'));
  17. const identity={reportSeriesKey:opts.seriesKey,version:Number(opts.version),reportContentHash:crypto.createHash('sha256').update(html).digest('hex')};
  18. const confirmation=await client.confirmation(opts.previewId,identity);
  19. const module=await import(pathToFileURL(path.resolve(opts.skillDir,'scripts/report-client.mjs')).href);
  20. return module.runReportPipeline({...opts,confirmation,callFn:client.callFn,transfer:client.transfer});
  21. }
  22. if(process.argv[1]&&import.meta.url===pathToFileURL(path.resolve(process.argv[1])).href){
  23. try{
  24. const argv=process.argv.slice(2),arg=n=>{const i=argv.indexOf('--'+n);return i<0?null:argv[i+1];};
  25. const opts={reportDir:arg('report-dir'),seriesKey:arg('series-key'),version:Number(arg('version')),reportType:arg('report-type'),dataMode:arg('data-mode'),artifactSubtype:arg('artifact-subtype'),stageCode:arg('stage-code'),previewId:arg('preview-id'),skillDir:arg('skill-dir')};
  26. const receiptFile=arg('receipt');if(receiptFile){if(fs.existsSync(receiptFile))opts.priorReceipt=JSON.parse(fs.readFileSync(receiptFile,'utf8'));}
  27. const mode=arg('mode');if(!['preview','submit'].includes(mode))throw Error('--mode 必须为 preview 或 submit');
  28. const result=mode==='preview'?await previewReport(opts):await submitConfirmedReport(opts);
  29. if(receiptFile&&result.artifacts?.receipt)fs.writeFileSync(receiptFile,JSON.stringify(result.artifacts.receipt,null,2));
  30. // 不打印签名确认凭据或任何环境变量。
  31. console.log(JSON.stringify(mode==='preview'?result:{steps:result.steps,artifactId:result.artifacts?.artifactId,cardId:result.artifacts?.cardId,fatal:result.fatal},null,2));
  32. if(result.fatal)process.exitCode=result.fatal.code||1;
  33. }catch(e){console.error(e.message);process.exitCode=1;}
  34. }