progmune-runtime 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action-runtime.js +96 -0
- package/dist/actions.js +2 -0
- package/dist/consolidate.js +9 -0
- package/dist/emitter.js +166 -0
- package/dist/extract-ir-python.js +55 -0
- package/dist/extract-ir.js +143 -0
- package/dist/failure-corpus.js +113 -0
- package/dist/feedback.js +73 -0
- package/dist/generate.js +119 -0
- package/dist/generate_500.js +112 -0
- package/dist/llm.js +24 -0
- package/dist/main.js +142 -0
- package/dist/mcp-server.js +55 -0
- package/dist/mcp-server.mjs +76 -0
- package/dist/memory-layer.js +150 -0
- package/dist/p0_ssg_demo.js +64 -0
- package/dist/planner.js +224 -0
- package/dist/python-emitter.js +149 -0
- package/dist/rule-miner.js +10 -0
- package/dist/runtime.js +58 -0
- package/dist/search-planner.js +177 -0
- package/dist/semantic-validator.js +71 -0
- package/dist/semantic_guard_test.js +106 -0
- package/dist/ssg-validator.js +32 -0
- package/dist/test_failure_corpus.js +16 -0
- package/dist/utils.js +22 -0
- package/dist/validator.js +196 -0
- package/package.json +26 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkSemantic = checkSemantic;
|
|
4
|
+
let irCache = null;
|
|
5
|
+
function loadContracts() {
|
|
6
|
+
if (irCache)
|
|
7
|
+
return irCache;
|
|
8
|
+
const raw = JSON.parse(require("fs").readFileSync("ir.json", "utf-8"));
|
|
9
|
+
irCache = raw;
|
|
10
|
+
return raw;
|
|
11
|
+
}
|
|
12
|
+
function matchIntent(intent, keywords) {
|
|
13
|
+
return keywords.some(kw => intent.includes(kw));
|
|
14
|
+
}
|
|
15
|
+
function checkSemantic(intent, actions) {
|
|
16
|
+
const ir = loadContracts();
|
|
17
|
+
const errors = [];
|
|
18
|
+
const callActions = actions.filter((a) => a.kind === "call");
|
|
19
|
+
for (const action of callActions) {
|
|
20
|
+
const funcDef = ir.find(f => f.name === action.function);
|
|
21
|
+
if (!funcDef || !funcDef.contracts)
|
|
22
|
+
continue;
|
|
23
|
+
for (const contract of funcDef.contracts) {
|
|
24
|
+
if (contract.when_intent && !matchIntent(intent, contract.when_intent))
|
|
25
|
+
continue;
|
|
26
|
+
switch (contract.type) {
|
|
27
|
+
case "require_param": {
|
|
28
|
+
const arg = action.args?.find((a) => a.name === contract.param);
|
|
29
|
+
if (arg && typeof arg.value === "string") {
|
|
30
|
+
if (contract.not_empty && (arg.value.includes("{}") || arg.value.trim() === "")) {
|
|
31
|
+
errors.push(`${action.function}: ${contract.description}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
case "must_be_checked": {
|
|
37
|
+
if (action.assignTo) {
|
|
38
|
+
const usedInIf = actions.some(a => a.kind === "if" && a.condition === action.assignTo);
|
|
39
|
+
if (!usedInIf) {
|
|
40
|
+
errors.push(`${action.function}: ${contract.description} (变量 ${action.assignTo} 未用于if条件)`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
errors.push(`${action.function}: ${contract.description} (未使用assignTo保存返回值)`);
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
case "sequence_after": {
|
|
49
|
+
const mustBefore = contract.function;
|
|
50
|
+
const idxCurrent = callActions.indexOf(action);
|
|
51
|
+
const idxBefore = callActions.findIndex(a => a.function === mustBefore);
|
|
52
|
+
if (idxBefore === -1 || idxBefore > idxCurrent) {
|
|
53
|
+
errors.push(`${action.function}: ${contract.description}`);
|
|
54
|
+
}
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case "param_from": {
|
|
58
|
+
const arg = action.args?.find((a) => a.name === contract.param);
|
|
59
|
+
if (arg && typeof arg.value === "string") {
|
|
60
|
+
const sourceAction = callActions.find(a => a.function === contract.function && a.assignTo === arg.value);
|
|
61
|
+
if (!sourceAction) {
|
|
62
|
+
errors.push(`${action.function}: ${contract.description} (参数 ${arg.value} 未引用 ${contract.function} 的输出)`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { valid: errors.length === 0, errors };
|
|
71
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const planner_1 = require("./planner");
|
|
37
|
+
const validator_1 = require("./validator");
|
|
38
|
+
const python_emitter_1 = require("./python-emitter");
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
// 直接使用当前目录下的 ir.json,不再重新提取
|
|
43
|
+
const ir = JSON.parse(fs.readFileSync("ir.json", "utf-8"));
|
|
44
|
+
console.log(`IR: ${ir.length} 函数 (含合约)`);
|
|
45
|
+
const intents = {
|
|
46
|
+
case1: "实现 login 函数,验证密码后生成JWT并返回",
|
|
47
|
+
case2: "实现用户注册,先加密密码,然后发送欢迎邮件",
|
|
48
|
+
case3: "实现带缓存的查询:先查缓存,若无则查询并更新缓存",
|
|
49
|
+
case4: "实现批量发送邮件:遍历用户列表,对每个活跃用户发送通知",
|
|
50
|
+
case5: "实现角色检查:验证用户是否为管理员,是则执行操作",
|
|
51
|
+
case6: "实现会话创建:验证凭据后,生成JWT,创建会话并缓存",
|
|
52
|
+
case7: "实现数据导出:获取所有用户数据,转换格式并保存到文件",
|
|
53
|
+
case8: "实现账户锁定:检查登录失败次数,超过阈值则锁定账户",
|
|
54
|
+
case9: "实现令牌刷新:验证旧令牌有效性,生成新令牌并更新会话",
|
|
55
|
+
case10: "实现用户注销:销毁会话,清理缓存,记录审计日志"
|
|
56
|
+
};
|
|
57
|
+
async function runSemanticTest() {
|
|
58
|
+
const results = [];
|
|
59
|
+
for (const [caseName, intent] of Object.entries(intents)) {
|
|
60
|
+
console.log(`\n🧪 ${caseName}: ${intent.substring(0, 50)}...`);
|
|
61
|
+
const actions = await (0, planner_1.plan)(intent);
|
|
62
|
+
if (!actions || actions.length === 0) {
|
|
63
|
+
results.push({ case: caseName, intent, generatedCode: "", actionSequence: [], syntaxPass: false, semanticError: true, runtimePass: false });
|
|
64
|
+
console.log(" ❌ 规划失败(无输出)");
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const seqResult = (0, validator_1.validateActionSequence)(actions);
|
|
68
|
+
const syntaxPass = seqResult.valid;
|
|
69
|
+
if (!syntaxPass) {
|
|
70
|
+
console.log(" ❌ 语法/变量校验失败:", seqResult.errors.join(", "));
|
|
71
|
+
results.push({ case: caseName, intent, generatedCode: "", actionSequence: actions, syntaxPass: false, semanticError: true, runtimePass: false });
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const code = (0, python_emitter_1.emitPython)(actions);
|
|
75
|
+
console.log(" 📝 代码:\n" + code.split("\n").slice(0, 8).map(l => " " + l).join("\n"));
|
|
76
|
+
const tmpFile = path.join(path.resolve("./test-semantic-guard"), "__test.py");
|
|
77
|
+
fs.writeFileSync(tmpFile, code);
|
|
78
|
+
let runtimePass = false;
|
|
79
|
+
try {
|
|
80
|
+
(0, child_process_1.execSync)(`python3 ${tmpFile}`, { timeout: 5000, encoding: "utf-8", cwd: path.resolve("./test-semantic-guard") });
|
|
81
|
+
runtimePass = true;
|
|
82
|
+
}
|
|
83
|
+
catch (e) { }
|
|
84
|
+
if (fs.existsSync(tmpFile))
|
|
85
|
+
fs.unlinkSync(tmpFile);
|
|
86
|
+
// 语义错误标记:如果序列中重要的业务函数缺失,视为语义错误(已在规划时由 checkSemantic 拦截)
|
|
87
|
+
const hasSemanticError = !runtimePass || actions.length <= 1; // 简单判断:只有1个调用的视为不完整
|
|
88
|
+
results.push({ case: caseName, intent, generatedCode: code, actionSequence: actions, syntaxPass, semanticError: hasSemanticError, runtimePass });
|
|
89
|
+
console.log(hasSemanticError ? " ⚠️ 潜在语义错误" : " ✅ 完整通过");
|
|
90
|
+
}
|
|
91
|
+
console.log("\n═══════════════════════════════════");
|
|
92
|
+
console.log("📊 语义阻断测试报告");
|
|
93
|
+
console.log("═══════════════════════════════════");
|
|
94
|
+
const total = results.length;
|
|
95
|
+
const syntaxBlocked = results.filter(r => !r.syntaxPass).length;
|
|
96
|
+
const semanticDetected = results.filter(r => r.semanticError).length;
|
|
97
|
+
const fullyClean = results.filter(r => r.syntaxPass && !r.semanticError).length;
|
|
98
|
+
console.log(`总测试案例: ${total}`);
|
|
99
|
+
console.log(`语法/变量层拦截: ${syntaxBlocked}`);
|
|
100
|
+
console.log(`语义错误检测: ${semanticDetected}`);
|
|
101
|
+
console.log(`完全安全通过: ${fullyClean}`);
|
|
102
|
+
console.log(`阻断率: ${((syntaxBlocked + semanticDetected) / total * 100).toFixed(0)}%`);
|
|
103
|
+
fs.writeFileSync("semantic_block_results.json", JSON.stringify(results, null, 2));
|
|
104
|
+
console.log("详细结果已保存到 semantic_block_results.json");
|
|
105
|
+
}
|
|
106
|
+
runSemanticTest().catch(console.error);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StateMachineValidator = void 0;
|
|
4
|
+
class StateMachineValidator {
|
|
5
|
+
constructor(rules, initialState = 'INIT') {
|
|
6
|
+
this.currentStates = new Set([initialState]);
|
|
7
|
+
this.rules = new Map();
|
|
8
|
+
rules.forEach(r => this.rules.set(r.function, r.protocol));
|
|
9
|
+
}
|
|
10
|
+
apply(functionName) {
|
|
11
|
+
const rule = this.rules.get(functionName);
|
|
12
|
+
if (!rule) {
|
|
13
|
+
return { valid: true, statesAfter: [...this.currentStates] };
|
|
14
|
+
}
|
|
15
|
+
const hasValidPreState = rule.pre_states.some(s => this.currentStates.has(s));
|
|
16
|
+
if (!hasValidPreState) {
|
|
17
|
+
return {
|
|
18
|
+
valid: false,
|
|
19
|
+
error: `非法调用:${functionName} 要求前置状态 [${rule.pre_states}],当前状态为 [${[...this.currentStates]}]`
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (rule.invalidate) {
|
|
23
|
+
rule.invalidate.forEach(s => this.currentStates.delete(s));
|
|
24
|
+
}
|
|
25
|
+
rule.post_states.forEach(s => this.currentStates.add(s));
|
|
26
|
+
return { valid: true, statesAfter: [...this.currentStates] };
|
|
27
|
+
}
|
|
28
|
+
getCurrentStates() {
|
|
29
|
+
return [...this.currentStates];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.StateMachineValidator = StateMachineValidator;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const failure_corpus_1 = require("./failure-corpus");
|
|
4
|
+
// 模拟记录一条失败案例
|
|
5
|
+
(0, failure_corpus_1.recordFailure)({
|
|
6
|
+
intent: "实现一个登录函数",
|
|
7
|
+
projectFunctions: ["verify_password", "generate_jwt"],
|
|
8
|
+
violatedSVL: "SVL-4",
|
|
9
|
+
constraintType: "protocol",
|
|
10
|
+
actionSequence: [{ kind: "call", function: "generate_jwt" }],
|
|
11
|
+
errorDetail: "非法调用:generate_jwt 要求前置状态 [AUTHENTICATED],当前状态为 [UNAUTHENTICATED]",
|
|
12
|
+
ssgState: "UNAUTHENTICATED",
|
|
13
|
+
});
|
|
14
|
+
// 打印当前统计
|
|
15
|
+
console.log("当前失败案例总数:", (0, failure_corpus_1.getAllFailures)().length);
|
|
16
|
+
console.log("高频模式:", (0, failure_corpus_1.getTopFailurePatterns)());
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jaccardSimilarity = jaccardSimilarity;
|
|
4
|
+
exports.extractKeywords = extractKeywords;
|
|
5
|
+
// 计算两个字符串的简单 Jaccard 相似度(基于字符二元组)
|
|
6
|
+
function jaccardSimilarity(a, b) {
|
|
7
|
+
const bigrams = (s) => {
|
|
8
|
+
const bgs = new Set();
|
|
9
|
+
for (let i = 0; i < s.length - 1; i++)
|
|
10
|
+
bgs.add(s.substring(i, i + 2));
|
|
11
|
+
return bgs;
|
|
12
|
+
};
|
|
13
|
+
const setA = bigrams(a);
|
|
14
|
+
const setB = bigrams(b);
|
|
15
|
+
const intersection = new Set([...setA].filter(x => setB.has(x)));
|
|
16
|
+
const union = new Set([...setA, ...setB]);
|
|
17
|
+
return intersection.size / (union.size || 1);
|
|
18
|
+
}
|
|
19
|
+
// 从意图中提取关键词
|
|
20
|
+
function extractKeywords(intent) {
|
|
21
|
+
return intent.split(/[\s,。!?,]+/).filter(w => w.length > 1).map(w => w.toLowerCase());
|
|
22
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.validateAction = validateAction;
|
|
37
|
+
exports.validateActionSequence = validateActionSequence;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
function loadIR() {
|
|
41
|
+
const irPath = path.resolve(__dirname, "../ir.json");
|
|
42
|
+
if (!fs.existsSync(irPath))
|
|
43
|
+
return [];
|
|
44
|
+
return JSON.parse(fs.readFileSync(irPath, "utf-8"));
|
|
45
|
+
}
|
|
46
|
+
const BUILTIN_WHITELIST = new Set([
|
|
47
|
+
"console.log", "setTimeout", "setInterval", "clearTimeout",
|
|
48
|
+
"JSON.stringify", "JSON.parse", "fetch"
|
|
49
|
+
]);
|
|
50
|
+
function normalizeType(type) {
|
|
51
|
+
if (!type)
|
|
52
|
+
return "any";
|
|
53
|
+
const t = type.toLowerCase().trim();
|
|
54
|
+
if (t === "string")
|
|
55
|
+
return "str";
|
|
56
|
+
if (t === "number" || t === "integer")
|
|
57
|
+
return "int";
|
|
58
|
+
if (t === "boolean")
|
|
59
|
+
return "bool";
|
|
60
|
+
if (t === "dictionary" || t === "record" || t === "dict")
|
|
61
|
+
return "dict";
|
|
62
|
+
if (t === "list")
|
|
63
|
+
return "list";
|
|
64
|
+
if (t === "tuple")
|
|
65
|
+
return "tuple";
|
|
66
|
+
if (t === "set")
|
|
67
|
+
return "set";
|
|
68
|
+
if (t === "any" || t === "variable")
|
|
69
|
+
return "any";
|
|
70
|
+
return t;
|
|
71
|
+
}
|
|
72
|
+
function checkVariableFlow(actions) {
|
|
73
|
+
const errors = [];
|
|
74
|
+
const declared = new Map();
|
|
75
|
+
for (let i = 0; i < actions.length; i++) {
|
|
76
|
+
const action = actions[i];
|
|
77
|
+
if (action.kind === "call") {
|
|
78
|
+
for (const arg of (action.args || [])) {
|
|
79
|
+
if (typeof arg.value === "string" && /^[a-zA-Z_]\w*$/.test(arg.value)) {
|
|
80
|
+
if (declared.has(arg.value))
|
|
81
|
+
continue;
|
|
82
|
+
if (arg.value.length < 15 && arg.value[0] === arg.value[0].toLowerCase()) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (action.assignTo) {
|
|
88
|
+
if (action.args?.some(a => a.value === action.assignTo)) {
|
|
89
|
+
errors.push(`变量 '${action.assignTo}' 在动作${i}中引用自身`);
|
|
90
|
+
}
|
|
91
|
+
declared.set(action.assignTo, "any");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else if (action.kind === "assign") {
|
|
95
|
+
if (action.target) {
|
|
96
|
+
if (typeof action.value === "string" && /^[a-zA-Z_]\w*$/.test(action.value)) {
|
|
97
|
+
if (!declared.has(action.value) && action.value.length < 15)
|
|
98
|
+
continue;
|
|
99
|
+
if (!declared.has(action.value))
|
|
100
|
+
errors.push(`赋值时引用未定义变量 '${action.value}'`);
|
|
101
|
+
}
|
|
102
|
+
declared.set(action.target, "any");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (action.kind === "return") {
|
|
106
|
+
if (typeof action.value === "string" && /^[a-zA-Z_]\w*$/.test(action.value)) {
|
|
107
|
+
if (!declared.has(action.value) && !/^["']/.test(action.value) && action.value.length < 15)
|
|
108
|
+
continue;
|
|
109
|
+
if (!declared.has(action.value))
|
|
110
|
+
errors.push(`返回未定义变量 '${action.value}'`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else if (action.kind === "if") {
|
|
114
|
+
if (typeof action.condition === "string" && declared.has(action.condition))
|
|
115
|
+
continue;
|
|
116
|
+
if (typeof action.condition === "string" && /^(true|false)$/.test(action.condition))
|
|
117
|
+
continue;
|
|
118
|
+
if (typeof action.condition === "string" && action.condition.length < 10) {
|
|
119
|
+
// 可能是表达式,放行
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
errors.push(`条件中引用了未定义的变量 '${action.condition}'`);
|
|
123
|
+
}
|
|
124
|
+
errors.push(...checkVariableFlow(action.thenActions || []));
|
|
125
|
+
errors.push(...checkVariableFlow(action.elseActions || []));
|
|
126
|
+
}
|
|
127
|
+
else if (action.kind === "for") {
|
|
128
|
+
errors.push(...checkVariableFlow(action.bodyActions || []));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return errors;
|
|
132
|
+
}
|
|
133
|
+
function validateAction(action) {
|
|
134
|
+
const functions = loadIR();
|
|
135
|
+
const errors = [];
|
|
136
|
+
if (!action || !["call", "if", "for", "assign", "return"].includes(action.kind)) {
|
|
137
|
+
errors.push(`无效动作类型: '${action?.kind}'`);
|
|
138
|
+
return { valid: false, errors };
|
|
139
|
+
}
|
|
140
|
+
if (action.kind === "call") {
|
|
141
|
+
const fn = functions.find((f) => f.name === action.function);
|
|
142
|
+
if (!fn) {
|
|
143
|
+
if (action.function && BUILTIN_WHITELIST.has(action.function))
|
|
144
|
+
return { valid: true, errors: [] };
|
|
145
|
+
errors.push(`函数 '${action.function}' 不存在`);
|
|
146
|
+
return { valid: false, errors };
|
|
147
|
+
}
|
|
148
|
+
if (!action.args) {
|
|
149
|
+
errors.push(`函数 '${action.function}' 缺少参数列表`);
|
|
150
|
+
return { valid: false, errors };
|
|
151
|
+
}
|
|
152
|
+
if (action.args.length !== fn.params.length) {
|
|
153
|
+
errors.push(`参数数量不匹配: 期望 ${fn.params.length}, 实际 ${action.args.length}`);
|
|
154
|
+
}
|
|
155
|
+
action.args.forEach((arg, i) => {
|
|
156
|
+
if (!arg) {
|
|
157
|
+
errors.push(`函数 '${action.function}' 的第${i}个参数为空`);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const expected = normalizeType(fn.params[i]?.type);
|
|
161
|
+
const actual = normalizeType(arg.type);
|
|
162
|
+
if (actual !== "any" && expected !== "any" && actual !== expected) {
|
|
163
|
+
errors.push(`类型不匹配: 参数 '${fn.params[i].name}' 期望 ${expected}, 实际 ${actual}`);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else if (action.kind === "if") {
|
|
168
|
+
if (action.thenActions) {
|
|
169
|
+
for (const a of action.thenActions)
|
|
170
|
+
errors.push(...validateAction(a).errors);
|
|
171
|
+
}
|
|
172
|
+
if (action.elseActions) {
|
|
173
|
+
for (const a of action.elseActions)
|
|
174
|
+
errors.push(...validateAction(a).errors);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else if (action.kind === "for") {
|
|
178
|
+
if (action.bodyActions) {
|
|
179
|
+
for (const a of action.bodyActions)
|
|
180
|
+
errors.push(...validateAction(a).errors);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { valid: errors.length === 0, errors };
|
|
184
|
+
}
|
|
185
|
+
function validateActionSequence(actions) {
|
|
186
|
+
const errors = [];
|
|
187
|
+
for (const action of actions) {
|
|
188
|
+
const result = validateAction(action);
|
|
189
|
+
if (!result.valid)
|
|
190
|
+
errors.push(...result.errors);
|
|
191
|
+
}
|
|
192
|
+
if (errors.length === 0) {
|
|
193
|
+
errors.push(...checkVariableFlow(actions));
|
|
194
|
+
}
|
|
195
|
+
return { valid: errors.length === 0, errors };
|
|
196
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "progmune-runtime",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Progmune Runtime — Program Immunology: Constraint-Guided Program Synthesis Runtime",
|
|
5
|
+
"main": "dist/mcp-server.mjs",
|
|
6
|
+
"bin": {
|
|
7
|
+
"progmune-runtime": "./dist/mcp-server.mjs"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc -p tsconfig.json",
|
|
11
|
+
"start": "node dist/mcp-server.mjs"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["program-synthesis", "verification", "mcp", "compiler", "runtime", "immunology"],
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
18
|
+
"openai": "^6.36.0",
|
|
19
|
+
"ts-morph": "^28.0.0",
|
|
20
|
+
"ts-node": "^10.9.2",
|
|
21
|
+
"typescript": "^6.0.3"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^25.6.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
|
+
"verbatimModuleSyntax": false,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"types": ["node"],
|
|
13
|
+
"ignoreDeprecations": "6.0"
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*.ts"],
|
|
16
|
+
"exclude": ["node_modules", "dist", "test-*", "*.ts"]
|
|
17
|
+
}
|