import fs from 'node:fs'; import assert from 'node:assert/strict'; import {runWorkshop,createWorkshopCaller} from '../skills/ksf-workshop-runtime/scripts/ksf-workshop.mjs'; import {loadPublicBoard} from '../src/services/workshop-data.js'; import {buildModel} from '../src/core/kfs/build.js'; const config={};for(const line of fs.readFileSync('F:/文档/康师傅数字人受限连接.env','utf8').split(/\r?\n/)){const m=line.match(/^(KSF_SERVICE_TOKEN|KSF_FUNCTIONS_URL|KSF_PARSE_APP_ID)=(.*)$/);if(m)config[m[1]]=m[2].trim();} const call=createWorkshopCaller({token:config.KSF_SERVICE_TOKEN,url:config.KSF_FUNCTIONS_URL,appId:config.KSF_PARSE_APP_ID}); fs.mkdirSync('work',{recursive:true}); const journal={at:new Date().toISOString(),execution:'management-machine using the same workshop Skill module; not a Hermes message',dataMode:'sample',actualPlatformCollectionPerformed:false,operations:[],published:false}; const key='ksf-workshop-link-20261101'; const save=()=>fs.writeFileSync('work/workshop-live.json',JSON.stringify(journal,null,2)); const run=async(command,task,suffix)=>{const result=await runWorkshop(command,task,{requestId:key+':'+suffix,call});journal.operations.push({command,requestId:key+':'+suffix,result});save();return result;}; try{ const ctx=await run('workshop-start',{workshopKey:key,participant:{key:key+':technical-participant',name:'工作坊联调(非学员)',identityBasis:'participant_registration'},agent:{name:'王刚001(联调模拟)'},conversation:{key:key+':management-simulation',channel:'technical-test'},groupId:'TEST-1101',dataMode:'sample'},'start');journal.sessionId=ctx.sessionId;save(); const material=await run('workshop-import',{source:{title:'技术样例定义,非供应商或工厂资料',sourceKind:'dataset',dataMode:'sample',rawText:'仅用于验证关联字段,不代表真实产品、原料性能、工艺参数或报价。演示素材:面粉;演示工艺:混合。'},records:[ {itemKey:'material',className:'KfMaterial',externalKey:key+':material',data:{dataMode:'sample',name:'【样例】面粉关联占位',materialType:'ingredient',specification:'未验证,不能用于实际配方',function:'验证配料引用'}}, {itemKey:'process',className:'KfProcess',externalKey:key+':process',data:{dataMode:'sample',name:'【样例】混合工艺关联占位',processType:'mixing',processKind:'route',description:'仅验证工艺关联,无设备与参数依据'}} ]},'materials'); const materialId=material.items.find(x=>x.itemKey==='material').objectId,processId=material.items.find(x=>x.itemKey==='process').objectId; const task=(stage,title,type,projectId)=>({sessionId:ctx.sessionId,...(projectId?{projectId}:{}),taskKey:key+':'+stage+(projectId?':project':''),version:1,stageCode:stage,title,artifactType:type,category:'拌面',rawText:'技术联调样例:'+title+'。没有消费者采集数据;市场、工艺、成本均待验证。',sourceIds:[],pendingQuestions:['消费者证据待采集','企业配方与工艺待提供'],report:{executive:['样例用于检查员工/Agent/项目/阶段与数据关联,不能作为市场结论。'],chapters:[{id:stage.toLowerCase(),title,stage,sections:[{kind:'para',title:'本阶段产出',text:'这是构造的技术样例,无真实消费者证据。'},{kind:'table',title:'待验证清单',data:{cols:['内容','状态'],rows:[['消费者需求','未采集'],['工艺与成本','未核实']]}}]}]}}); await run('workshop-save',task('S1','品类与竞品待查清单','category_report'),'s1'); await run('workshop-save',task('S2','用户需求待验证清单','pain_list'),'s2'); const opportunity=await run('workshop-save',{...task('S3','机会卡字段联调','opportunity_card'),opportunity:{key:key+':opportunity',title:'拌面方向待验证',demandSummary:'仅验证机会到项目关联',targetAudience:'待研究人群',usageScenario:'待研究场景',fusionDefinition:'赛道×用户×场景,均无真实研究证据'}},'s3');journal.opportunityId=opportunity.opportunityId; const proj=await run('project-create',{sessionId:ctx.sessionId,opportunityId:opportunity.opportunityId,projectKey:key+':project',title:'拌面新品链路技术联调'},'project');journal.projectId=proj.projectId; await run('workshop-save',task('S3','所选机会与项目范围','opportunity_card',proj.projectId),'project-s3'); const progress=(toStage,state,note)=>({sessionId:ctx.sessionId,projectId:proj.projectId,toStage,state,note}); await run('project-progress',progress('S4','in_progress','机会范围已保存,进入验证练习'),'s4-start'); await run('project-progress',progress('S4','blocked','缺少真实消费者与企业工艺资料,显示卡住状态'),'blocked'); const blocked=await run('project-status',{sessionId:ctx.sessionId,projectId:proj.projectId},'blocked-read');assert.equal(blocked.project.progressState,'blocked'); await run('project-progress',progress('S4','in_progress','继续技术链路验证,不声称真实业务条件已满足'),'resume'); await run('workshop-save',task('S4','工艺成本验证缺口','assessment_report',proj.projectId),'s4'); await run('project-progress',progress('S5','in_progress','验证缺口已明确,进入课堂方案练习'),'s5-start'); const concept=await run('concept-save',{sessionId:ctx.sessionId,projectId:proj.projectId,version:1,concept:{name:'拌面方案字段联调',targetAudience:'待研究',usageScenario:'待研究',specification:{packaging:'待验证',netWeightG:null},valueProposition:'待验证买点',flavor:'待验证',sellingPoints:['待验证'],productionSteps:[{processId,step:'样例混合路线,不含真实工艺参数'}],pendingQuestions:['所有配方比例、工艺、成本与需求均待验证']},formulaItems:[{materialId,component:'面体',basis:'用量未知;仅验证原料关联',note:'未填写 amount,不编造比例'}]},'concept');journal.conceptId=concept.conceptId; const final=await run('workshop-save',task('S5','新品方案与待验证事项','concept',proj.projectId),'s5');journal.artifactId=final.artifactId; await run('project-progress',progress('S5','completed','仅技术课堂链路完成;非真实产品完成或企业批准'),'complete'); const readback=await run('project-status',{sessionId:ctx.sessionId,projectId:proj.projectId},'readback');assert.equal(readback.project.progressState,'completed');assert.equal(readback.concept.objectId,concept.conceptId);assert.equal(readback.formulaItems[0].materialRef.objectId,materialId);assert.equal(readback.concept.productionSteps[0].processId,processId);assert.equal(readback.artifacts.length,3);assert.equal(readback.events.length,10); const raw=await loadPublicBoard({url:config.KSF_FUNCTIONS_URL,appId:config.KSF_PARSE_APP_ID});const db=buildModel(raw);assert.equal(db.artifactById[final.artifactId].upload.agentName,'王刚001(联调模拟)');assert.equal(db.projectById[proj.projectId].stage.code,'S5');assert.equal(db.projectById[proj.projectId].artifactIds.length,3);assert.equal(db.artifactById[final.artifactId].ownerName,'工作坊联调(非学员)'); journal.ok=true;journal.readback={projectId:proj.projectId,conceptId:concept.conceptId,formulaItems:readback.formulaItems.length,projectArtifacts:readback.artifacts.length,eventCount:readback.events.length,publicBoardVisible:true,agentName:db.artifactById[final.artifactId].upload.agentName,employeeName:db.artifactById[final.artifactId].ownerName,stage:db.projectById[proj.projectId].stage.code,progressState:readback.project.progressState};save();console.log(JSON.stringify({ok:true,sessionId:ctx.sessionId,opportunityId:journal.opportunityId,...journal.readback,reportUrl:'https://kangshifu.fmode.cn/report/'+final.artifactId,dataMode:'sample',actualPlatformCollectionPerformed:false,formalReportPublished:false},null,2)); }catch(e){journal.ok=false;journal.error={code:e.code||'VERIFY_FAILED',message:'验证停止;保留既有编号与稳定业务键,不自动删除或换键。'};save();console.error(JSON.stringify(journal.error));process.exitCode=1;}