#!/usr/bin/env node /** * Static verification for lobster execution / prompt unification changes. * Run: node saas-adminui/scripts/verify-lobster-changes.js */ const fs = require('fs'); const path = require('path'); const root = path.resolve(__dirname, '../..'); const checks = []; function read(rel) { return fs.readFileSync(path.join(root, rel), 'utf8'); } function ok(name, pass, detail) { checks.push({ name, pass, detail: detail || '' }); } function includes(rel, needle, label) { const content = read(rel); ok(label || `${rel} contains ${needle}`, content.includes(needle)); } function notIncludes(rel, needle, label) { const content = read(rel); ok(label || `${rel} excludes ${needle}`, !content.includes(needle)); } // --- Prompt workflowCode --- includes( 'java/fs-service/src/main/java/com/fs/company/service/workflow/impl/LobsterWorkflowExecutorImpl.java', 'resolveWorkflowCodeForNode', 'Executor resolves templateCode for prompts' ); includes( 'java/fs-service/src/main/java/com/fs/company/service/workflow/impl/LobsterEvolutionEngineImpl.java', 'workflowPromptResolver.resolveWorkflowCodeByInstanceId', 'Evolution engine uses workflowPromptResolver' ); includes( 'java/fs-service/src/main/resources/mapper/company/LobsterSystemPromptMapper.xml', 'ORDER BY', 'Prompt selectByPriority has priority ordering' ); // --- PROMPT fallback --- includes( 'java/fs-service/src/main/java/com/fs/company/service/workflow/execution/LobsterInboundPromptReplyService.java', 'generateReply', 'Inbound prompt reply service exists' ); includes( 'java/fs-service/src/main/java/com/fs/company/service/workflow/execution/LobsterInboundReplyOrchestrator.java', 'replyViaPromptFallback', 'Orchestrator uses prompt fallback' ); // --- Tag binding --- includes( 'java/fs-service/src/main/java/com/fs/company/service/impl/CompanyTagTemplateBindingServiceImpl.java', 'evaluateMatchCondition(b.getMatchCondition(), userTags)', 'matchTemplate filters by matchCondition' ); includes( 'java/fs-service/src/main/resources/mapper/company/CompanyLobsterTagUserRelMapper.xml', 'selectLobsterTagsByContactIds', 'Multi-channel lobster tag query' ); includes( 'java/fs-service/src/main/resources/mapper/company/LobsterWorkflowInstanceMapper.xml', 'selectByBindingId', 'Binding instance SQL filter' ); // --- Frontend --- includes( 'saas-companyui/src/views/company/companyClient/index.vue', 'lobsterTagNames', 'WX client list shows lobster tags' ); includes( 'java/fs-service/src/main/java/com/fs/company/service/workflow/impl/NodePromptServiceImpl.java', 'saveNodePromptPair', 'Canvas prompt dual-write helper' ); // --- AI chat node --- includes( 'java/fs-service/src/main/java/com/fs/company/service/workflow/impl/DynamicNodeExecutorImpl.java', 'resolveAiChatSystemPrompt', 'AI chat node uses PromptManager' ); // --- Migration --- includes( 'java/sql/V20260617_01__lobster_execution_unify.sql', 'binding_id', 'Execution unify migration exists' ); // --- Hook PROMPT with content --- includes( 'java/fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java', 'QW prompt route unified reply', 'QW hook sends unified PROMPT reply' ); const failed = checks.filter(c => !c.pass); console.log('Lobster change verification'); console.log('='.repeat(50)); checks.forEach(c => { console.log(`${c.pass ? 'PASS' : 'FAIL'} ${c.name}${c.detail ? ' ?? ' + c.detail : ''}`); }); console.log('='.repeat(50)); console.log(`Total: ${checks.length}, Passed: ${checks.length - failed.length}, Failed: ${failed.length}`); process.exit(failed.length ? 1 : 0);