progmune-runtime 2.0.1 → 2.0.4
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/.progmune_memory/episodic.json +186 -0
- package/dist/immune-reporter.js +126 -0
- package/dist/mcp-server.mjs +19 -9
- package/dist/planner.js +45 -2
- package/dist/report.js +32 -0
- package/package.json +1 -1
- package/protocols.json +16 -0
- package/dist/mcp-server.js +0 -55
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"intent": "生成一个JWT令牌",
|
|
4
|
+
"actions": [
|
|
5
|
+
{
|
|
6
|
+
"kind": "assign",
|
|
7
|
+
"target": "payload",
|
|
8
|
+
"value": "user:123"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"kind": "call",
|
|
12
|
+
"function": "generate_jwt",
|
|
13
|
+
"args": [
|
|
14
|
+
{
|
|
15
|
+
"name": "payload",
|
|
16
|
+
"type": "UserPayload",
|
|
17
|
+
"value": "payload"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"assignTo": "token"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"kind": "return",
|
|
24
|
+
"value": "token"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"success": true,
|
|
28
|
+
"id": "ep_1778775611414",
|
|
29
|
+
"timestamp": "2026-05-14T16:20:11.414Z"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"intent": "生成一个JWT令牌",
|
|
33
|
+
"actions": [],
|
|
34
|
+
"success": false,
|
|
35
|
+
"id": "ep_1778775591074",
|
|
36
|
+
"timestamp": "2026-05-14T16:19:51.074Z"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"intent": "生成一个JWT令牌",
|
|
40
|
+
"actions": [
|
|
41
|
+
{
|
|
42
|
+
"kind": "assign",
|
|
43
|
+
"target": "payload",
|
|
44
|
+
"value": "user:123"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"kind": "call",
|
|
48
|
+
"function": "generate_jwt",
|
|
49
|
+
"args": [
|
|
50
|
+
{
|
|
51
|
+
"name": "payload",
|
|
52
|
+
"type": "UserPayload",
|
|
53
|
+
"value": "payload"
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"assignTo": "token"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"kind": "return",
|
|
60
|
+
"value": "token"
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"success": true,
|
|
64
|
+
"id": "ep_1778775315729",
|
|
65
|
+
"timestamp": "2026-05-14T16:15:15.729Z"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"intent": "生成一个JWT令牌",
|
|
69
|
+
"actions": [
|
|
70
|
+
{
|
|
71
|
+
"kind": "assign",
|
|
72
|
+
"target": "payload",
|
|
73
|
+
"value": "user:123"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"kind": "call",
|
|
77
|
+
"function": "generate_jwt",
|
|
78
|
+
"args": [
|
|
79
|
+
{
|
|
80
|
+
"name": "payload",
|
|
81
|
+
"type": "UserPayload",
|
|
82
|
+
"value": "payload"
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
"assignTo": "token"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"kind": "return",
|
|
89
|
+
"value": "token"
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
"success": true,
|
|
93
|
+
"id": "ep_1778775173516",
|
|
94
|
+
"timestamp": "2026-05-14T16:12:53.516Z"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"intent": "创建一个带令牌的会话",
|
|
98
|
+
"actions": [
|
|
99
|
+
{
|
|
100
|
+
"kind": "assign",
|
|
101
|
+
"target": "user_payload",
|
|
102
|
+
"value": "user:123"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"kind": "call",
|
|
106
|
+
"function": "query_data",
|
|
107
|
+
"args": [
|
|
108
|
+
{
|
|
109
|
+
"name": "key",
|
|
110
|
+
"type": "str",
|
|
111
|
+
"value": "user_payload"
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"assignTo": "user_data"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"kind": "assign",
|
|
118
|
+
"target": "plain_password",
|
|
119
|
+
"value": "password123"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"kind": "call",
|
|
123
|
+
"function": "verify_password",
|
|
124
|
+
"args": [
|
|
125
|
+
{
|
|
126
|
+
"name": "plain",
|
|
127
|
+
"type": "str",
|
|
128
|
+
"value": "plain_password"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"name": "hash",
|
|
132
|
+
"type": "PasswordHash",
|
|
133
|
+
"value": "user_data"
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
"assignTo": "password_valid"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"kind": "call",
|
|
140
|
+
"function": "generate_jwt",
|
|
141
|
+
"args": [
|
|
142
|
+
{
|
|
143
|
+
"name": "payload",
|
|
144
|
+
"type": "UserPayload",
|
|
145
|
+
"value": "user_payload"
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"assignTo": "token"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"kind": "call",
|
|
152
|
+
"function": "create_session",
|
|
153
|
+
"args": [
|
|
154
|
+
{
|
|
155
|
+
"name": "user",
|
|
156
|
+
"type": "UserPayload",
|
|
157
|
+
"value": "user_payload"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"name": "token",
|
|
161
|
+
"type": "Token",
|
|
162
|
+
"value": "token"
|
|
163
|
+
}
|
|
164
|
+
],
|
|
165
|
+
"assignTo": "session"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"kind": "return",
|
|
169
|
+
"value": "session"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"kind": "return",
|
|
173
|
+
"value": "error"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"kind": "if",
|
|
177
|
+
"condition": "password_valid",
|
|
178
|
+
"thenActions": [],
|
|
179
|
+
"elseActions": []
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
"success": true,
|
|
183
|
+
"id": "ep_1778775082912",
|
|
184
|
+
"timestamp": "2026-05-14T16:11:22.919Z"
|
|
185
|
+
}
|
|
186
|
+
]
|
|
@@ -0,0 +1,126 @@
|
|
|
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.extractFingerprints = extractFingerprints;
|
|
37
|
+
exports.reportFingerprints = reportFingerprints;
|
|
38
|
+
exports.previewFingerprints = previewFingerprints;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const https = __importStar(require("https"));
|
|
42
|
+
const http = __importStar(require("http"));
|
|
43
|
+
const crypto = __importStar(require("crypto"));
|
|
44
|
+
const REPORT_ENDPOINT = process.env.PROGMUNE_HUB || "http://localhost:3000/report";
|
|
45
|
+
const FINGERPRINT_FILE = path.resolve(__dirname, "../.progmune_memory/fingerprints.json");
|
|
46
|
+
function getInstanceId() {
|
|
47
|
+
const host = require("os").hostname();
|
|
48
|
+
const cwd = process.cwd();
|
|
49
|
+
return crypto.createHash("sha256").update(host + cwd).digest("hex").substring(0, 16);
|
|
50
|
+
}
|
|
51
|
+
function extractFingerprints() {
|
|
52
|
+
const corpusDir = path.resolve(__dirname, "../failure_corpus");
|
|
53
|
+
if (!fs.existsSync(corpusDir))
|
|
54
|
+
return [];
|
|
55
|
+
const fingerprints = [];
|
|
56
|
+
const instanceId = getInstanceId();
|
|
57
|
+
for (const dateDir of fs.readdirSync(corpusDir)) {
|
|
58
|
+
const datePath = path.join(corpusDir, dateDir);
|
|
59
|
+
if (!fs.statSync(datePath).isDirectory())
|
|
60
|
+
continue;
|
|
61
|
+
for (const file of fs.readdirSync(datePath)) {
|
|
62
|
+
if (!file.endsWith(".json"))
|
|
63
|
+
continue;
|
|
64
|
+
const record = JSON.parse(fs.readFileSync(path.join(datePath, file), "utf-8"));
|
|
65
|
+
const funcSeq = (record.actionSequence || [])
|
|
66
|
+
.filter((a) => a.kind === "call")
|
|
67
|
+
.map((a) => a.function);
|
|
68
|
+
fingerprints.push({
|
|
69
|
+
instance_id: instanceId,
|
|
70
|
+
timestamp: record.timestamp,
|
|
71
|
+
violatedSVL: record.violatedSVL,
|
|
72
|
+
constraintType: record.constraintType,
|
|
73
|
+
functionSequence: funcSeq,
|
|
74
|
+
preState: record.ssgState ? [record.ssgState] : undefined,
|
|
75
|
+
postState: undefined,
|
|
76
|
+
count: 1,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return fingerprints;
|
|
81
|
+
}
|
|
82
|
+
async function reportFingerprints() {
|
|
83
|
+
const fingerprints = extractFingerprints();
|
|
84
|
+
if (fingerprints.length === 0) {
|
|
85
|
+
return { success: true, message: "无新指纹需要上报" };
|
|
86
|
+
}
|
|
87
|
+
let reported = [];
|
|
88
|
+
if (fs.existsSync(FINGERPRINT_FILE)) {
|
|
89
|
+
reported = JSON.parse(fs.readFileSync(FINGERPRINT_FILE, "utf-8"));
|
|
90
|
+
}
|
|
91
|
+
const newFingerprints = fingerprints.filter(f => !reported.includes(f.functionSequence.join(",")));
|
|
92
|
+
if (newFingerprints.length === 0) {
|
|
93
|
+
return { success: true, message: "所有指纹已上报,无新增" };
|
|
94
|
+
}
|
|
95
|
+
const payload = JSON.stringify({ fingerprints: newFingerprints });
|
|
96
|
+
return new Promise((resolve) => {
|
|
97
|
+
const url = new URL(REPORT_ENDPOINT);
|
|
98
|
+
const transport = url.protocol === "https:" ? https : http;
|
|
99
|
+
const req = transport.request(REPORT_ENDPOINT, {
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers: { "Content-Type": "application/json" },
|
|
102
|
+
}, (res) => {
|
|
103
|
+
let data = "";
|
|
104
|
+
res.on("data", (chunk) => data += chunk);
|
|
105
|
+
res.on("end", () => {
|
|
106
|
+
if (res.statusCode === 200) {
|
|
107
|
+
const updated = [...reported, ...newFingerprints.map(f => f.functionSequence.join(","))];
|
|
108
|
+
const dir = path.dirname(FINGERPRINT_FILE);
|
|
109
|
+
if (!fs.existsSync(dir))
|
|
110
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
111
|
+
fs.writeFileSync(FINGERPRINT_FILE, JSON.stringify(updated, null, 2));
|
|
112
|
+
resolve({ success: true, message: `成功上报 ${newFingerprints.length} 条新指纹` });
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
resolve({ success: false, message: `上报失败: ${res.statusCode} ${data}` });
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
req.on("error", (e) => resolve({ success: false, message: `网络错误: ${e.message}` }));
|
|
120
|
+
req.write(payload);
|
|
121
|
+
req.end();
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function previewFingerprints() {
|
|
125
|
+
return extractFingerprints();
|
|
126
|
+
}
|
package/dist/mcp-server.mjs
CHANGED
|
@@ -10,15 +10,13 @@ import * as path from 'path';
|
|
|
10
10
|
const require = createRequire(import.meta.url);
|
|
11
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
|
|
13
|
-
//
|
|
13
|
+
// 处理命令行子命令
|
|
14
14
|
const args = process.argv.slice(2);
|
|
15
15
|
const OPT_IN_FILE = path.resolve(__dirname, '../.progmune_memory/opt_in.json');
|
|
16
|
-
|
|
17
16
|
if (args[0] === 'opt-in') {
|
|
18
17
|
const command = args[1] || 'status';
|
|
19
18
|
const dir = path.dirname(OPT_IN_FILE);
|
|
20
19
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
21
|
-
|
|
22
20
|
if (command === 'enable') {
|
|
23
21
|
fs.writeFileSync(OPT_IN_FILE, JSON.stringify({ enabled: true, timestamp: new Date().toISOString() }, null, 2));
|
|
24
22
|
console.log('✅ 已开启免疫网络上报。感谢您为全球免疫网络做出贡献!');
|
|
@@ -28,7 +26,6 @@ if (args[0] === 'opt-in') {
|
|
|
28
26
|
if (fs.existsSync(OPT_IN_FILE)) fs.unlinkSync(OPT_IN_FILE);
|
|
29
27
|
console.log('⛔ 已关闭免疫网络上报。Progmune 将以完全离线模式运行。');
|
|
30
28
|
} else {
|
|
31
|
-
// 显示当前状态
|
|
32
29
|
if (fs.existsSync(OPT_IN_FILE)) {
|
|
33
30
|
console.log('免疫网络上报状态: 已开启');
|
|
34
31
|
console.log('脱敏错误指纹将自动上报。');
|
|
@@ -43,12 +40,25 @@ if (args[0] === 'opt-in') {
|
|
|
43
40
|
process.exit(0);
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
// ========== MCP 服务器部分 ==========
|
|
47
43
|
const { plan } = require('./planner.js');
|
|
48
44
|
const { extractIRPython } = require('./extract-ir-python.js');
|
|
49
45
|
const { emitPython } = require('./python-emitter.js');
|
|
50
46
|
const { recordRun } = require('./feedback.js');
|
|
51
47
|
|
|
48
|
+
// 从独立的协议文件中加载协议,并注入到 IR 中
|
|
49
|
+
function mergeProtocols(irArray) {
|
|
50
|
+
const protocolsFile = path.resolve(__dirname, '../protocols.json');
|
|
51
|
+
if (!fs.existsSync(protocolsFile)) return irArray;
|
|
52
|
+
|
|
53
|
+
const protocols = JSON.parse(fs.readFileSync(protocolsFile, 'utf-8'));
|
|
54
|
+
return irArray.map(fn => {
|
|
55
|
+
if (protocols[fn.name]) {
|
|
56
|
+
return { ...fn, protocol: protocols[fn.name] };
|
|
57
|
+
}
|
|
58
|
+
return fn;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
52
62
|
async function main() {
|
|
53
63
|
const server = new Server({
|
|
54
64
|
name: "progmune",
|
|
@@ -74,19 +84,19 @@ async function main() {
|
|
|
74
84
|
if (request.params.name === "progmune_generate") {
|
|
75
85
|
const { intent, projectPath } = request.params.arguments;
|
|
76
86
|
|
|
77
|
-
// 前置检查 LLM_API_KEY
|
|
78
87
|
if (!process.env.LLM_API_KEY) {
|
|
79
88
|
return { content: [{ type: "text", text: "❌ 未设置 LLM_API_KEY。请在终端执行:\nexport LLM_API_KEY='你的密钥'\n然后重启客户端。" }] };
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
// 前置检查免疫网络配置
|
|
83
91
|
if (!fs.existsSync(OPT_IN_FILE)) {
|
|
84
92
|
return { content: [{ type: "text", text: "⚠️ 请先完成免疫网络配置。\n\n运行以下命令开启(推荐):\n npx progmune-runtime opt-in enable\n\n或运行以下命令以离线模式使用:\n npx progmune-runtime opt-in disable\n\n配置完成后重启客户端即可使用。" }] };
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
// 执行规划
|
|
88
95
|
const fns = extractIRPython(projectPath);
|
|
89
|
-
|
|
96
|
+
// 关键步骤:将协议信息合并到 IR 中
|
|
97
|
+
const irWithProtocols = mergeProtocols(fns);
|
|
98
|
+
fs.writeFileSync("ir.json", JSON.stringify(irWithProtocols, null, 2));
|
|
99
|
+
|
|
90
100
|
const actions = await plan(intent);
|
|
91
101
|
if (!actions || actions.length === 0) {
|
|
92
102
|
return { content: [{ type: "text", text: "无法生成满足约束的代码。" }] };
|
package/dist/planner.js
CHANGED
|
@@ -42,6 +42,7 @@ const feedback_1 = require("./feedback");
|
|
|
42
42
|
const utils_1 = require("./utils");
|
|
43
43
|
const failure_corpus_1 = require("./failure-corpus");
|
|
44
44
|
const memory_layer_1 = require("./memory-layer");
|
|
45
|
+
const ssg_validator_1 = require("./ssg-validator");
|
|
45
46
|
const fs = __importStar(require("fs"));
|
|
46
47
|
function enrichActions(actions, ir) {
|
|
47
48
|
return actions.map(a => {
|
|
@@ -87,17 +88,36 @@ function determineConstraintType(svl) {
|
|
|
87
88
|
case "SVL-4": return "protocol";
|
|
88
89
|
}
|
|
89
90
|
}
|
|
91
|
+
/** 加载 IR 中所有带 protocol 的函数为协议规则 */
|
|
92
|
+
function loadProtocols(ir) {
|
|
93
|
+
return ir
|
|
94
|
+
.filter((f) => f.protocol)
|
|
95
|
+
.map((f) => ({ function: f.name, protocol: f.protocol }));
|
|
96
|
+
}
|
|
97
|
+
/** 验证动作序列的协议合法性 */
|
|
98
|
+
function validateProtocol(actions, protocols, initialState) {
|
|
99
|
+
const ssv = new ssg_validator_1.StateMachineValidator(protocols, initialState);
|
|
100
|
+
for (let i = 0; i < actions.length; i++) {
|
|
101
|
+
const a = actions[i];
|
|
102
|
+
if (a.kind === "call" && a.function) {
|
|
103
|
+
const result = ssv.apply(a.function);
|
|
104
|
+
if (!result.valid) {
|
|
105
|
+
return { valid: false, error: result.error, index: i };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return { valid: true };
|
|
110
|
+
}
|
|
90
111
|
async function plan(userIntent) {
|
|
91
112
|
(0, llm_1.resetCallCount)();
|
|
92
113
|
const ir = JSON.parse(fs.readFileSync("ir.json", "utf-8"));
|
|
93
|
-
//
|
|
114
|
+
// 语义模板快速通道
|
|
94
115
|
const cachedTemplate = (0, memory_layer_1.findSemanticTemplate)(userIntent);
|
|
95
116
|
if (cachedTemplate && cachedTemplate.successRate >= 0.8 && cachedTemplate.useCount >= 2) {
|
|
96
117
|
console.log("⚡ 命中语义模板,直接复用已验证序列");
|
|
97
118
|
(0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: cachedTemplate.actionSequence, success: true });
|
|
98
119
|
return cachedTemplate.actionSequence;
|
|
99
120
|
}
|
|
100
|
-
// ====================================
|
|
101
121
|
const keywords = (0, utils_1.extractKeywords)(userIntent);
|
|
102
122
|
const scored = ir.map((f) => {
|
|
103
123
|
let score = 0;
|
|
@@ -159,6 +179,8 @@ ${userIntent}
|
|
|
159
179
|
只输出代码。`;
|
|
160
180
|
let finalActions = [];
|
|
161
181
|
let currentPrompt = basePrompt;
|
|
182
|
+
// 加载协议规则,设定初始状态(例如未认证场景)
|
|
183
|
+
const protocols = loadProtocols(ir);
|
|
162
184
|
for (let r = 0; r < 3; r++) {
|
|
163
185
|
let text;
|
|
164
186
|
try {
|
|
@@ -179,6 +201,7 @@ ${userIntent}
|
|
|
179
201
|
}
|
|
180
202
|
const enriched = enrichActions(rawActions, ir);
|
|
181
203
|
const filtered = enriched.filter(a => !forbiddenFuncs.includes(a.function || ''));
|
|
204
|
+
// 1) 基础序列校验
|
|
182
205
|
const seqResult = (0, validator_1.validateActionSequence)(filtered);
|
|
183
206
|
if (!seqResult.valid) {
|
|
184
207
|
const errorsFlat = seqResult.errors.flat();
|
|
@@ -196,6 +219,26 @@ ${userIntent}
|
|
|
196
219
|
currentPrompt = basePrompt + `\n错误:${errorsFlat.join(";")}。请修正。`;
|
|
197
220
|
continue;
|
|
198
221
|
}
|
|
222
|
+
// 2) 协议状态机校验 (SSG)
|
|
223
|
+
if (protocols.length > 0) {
|
|
224
|
+
const protoResult = validateProtocol(filtered, protocols, "UNAUTHENTICATED");
|
|
225
|
+
if (!protoResult.valid) {
|
|
226
|
+
console.log("🛡️ SSG 协议违规:", protoResult.error);
|
|
227
|
+
(0, failure_corpus_1.recordFailure)({
|
|
228
|
+
intent: userIntent,
|
|
229
|
+
projectFunctions: ir.map((f) => f.name),
|
|
230
|
+
violatedSVL: "SVL-4",
|
|
231
|
+
constraintType: "protocol",
|
|
232
|
+
actionSequence: filtered,
|
|
233
|
+
errorDetail: protoResult.error,
|
|
234
|
+
});
|
|
235
|
+
(0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: filtered, success: false, svlViolated: "SVL-4" });
|
|
236
|
+
currentPrompt = basePrompt + `\n协议错误:${protoResult.error}。请按照正确的业务顺序重新生成,确保先通过认证再签发令牌。`;
|
|
237
|
+
continue;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// 3) 语义合约校验
|
|
199
242
|
const semResult = (0, semantic_validator_1.checkSemantic)(userIntent, filtered);
|
|
200
243
|
if (!semResult.valid) {
|
|
201
244
|
console.log("⚠️ 语义校验失败:", semResult.errors.join(", "));
|
package/dist/report.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const immune_reporter_1 = require("./immune-reporter");
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
const command = args[0] || "preview";
|
|
6
|
+
async function main() {
|
|
7
|
+
switch (command) {
|
|
8
|
+
case "preview":
|
|
9
|
+
console.log("═══ 待上报的脱敏错误指纹 ═══");
|
|
10
|
+
const fps = (0, immune_reporter_1.previewFingerprints)();
|
|
11
|
+
if (fps.length === 0) {
|
|
12
|
+
console.log("(暂无新的失败案例)");
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
fps.forEach((f, i) => {
|
|
16
|
+
console.log(`${i + 1}. [${f.violatedSVL}:${f.constraintType}] ${f.functionSequence.join(" → ") || "(无函数调用)"}`);
|
|
17
|
+
});
|
|
18
|
+
console.log(`\n共 ${fps.length} 条脱敏指纹待上报`);
|
|
19
|
+
}
|
|
20
|
+
break;
|
|
21
|
+
case "report":
|
|
22
|
+
console.log("正在安全上报脱敏指纹...");
|
|
23
|
+
const result = await (0, immune_reporter_1.reportFingerprints)();
|
|
24
|
+
console.log(result.message);
|
|
25
|
+
break;
|
|
26
|
+
default:
|
|
27
|
+
console.log("用法: npx ts-node src/report.ts [preview|report]");
|
|
28
|
+
console.log(" preview 查看待上报的脱敏错误指纹");
|
|
29
|
+
console.log(" report 执行安全上报");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
main().catch(console.error);
|
package/package.json
CHANGED
package/protocols.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"verify_password": {
|
|
3
|
+
"pre_states": ["UNAUTHENTICATED"],
|
|
4
|
+
"post_states": ["AUTHENTICATED"]
|
|
5
|
+
},
|
|
6
|
+
"generate_jwt": {
|
|
7
|
+
"pre_states": ["AUTHENTICATED"],
|
|
8
|
+
"post_states": ["TOKEN_ISSUED"],
|
|
9
|
+
"invalidate": ["AUTHENTICATED"]
|
|
10
|
+
},
|
|
11
|
+
"create_session": {
|
|
12
|
+
"pre_states": ["TOKEN_ISSUED"],
|
|
13
|
+
"post_states": ["SESSION_ACTIVE"],
|
|
14
|
+
"invalidate": ["TOKEN_ISSUED"]
|
|
15
|
+
}
|
|
16
|
+
}
|
package/dist/mcp-server.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
-
import { createRequire } from 'module';
|
|
5
|
-
|
|
6
|
-
const require = createRequire(import.meta.url);
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
|
|
9
|
-
const { plan } = require('./planner.js');
|
|
10
|
-
const { extractIRPython } = require('./extract-ir-python.js');
|
|
11
|
-
const { emitPython } = require('./python-emitter.js');
|
|
12
|
-
const { recordRun } = require('./feedback.js');
|
|
13
|
-
|
|
14
|
-
async function main() {
|
|
15
|
-
const server = new Server({
|
|
16
|
-
name: "progmune",
|
|
17
|
-
version: "2.0.0"
|
|
18
|
-
}, { capabilities: { tools: {} } });
|
|
19
|
-
|
|
20
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
21
|
-
tools: [{
|
|
22
|
-
name: "generate_verified_code",
|
|
23
|
-
description: "生成类型安全的Python代码,仅使用项目中真实存在的函数。",
|
|
24
|
-
inputSchema: {
|
|
25
|
-
type: "object",
|
|
26
|
-
properties: {
|
|
27
|
-
intent: { type: "string", description: "编程意图" },
|
|
28
|
-
projectPath: { type: "string", description: "项目根目录绝对路径" }
|
|
29
|
-
},
|
|
30
|
-
required: ["intent", "projectPath"]
|
|
31
|
-
}
|
|
32
|
-
}]
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
36
|
-
if (request.params.name === "generate_verified_code") {
|
|
37
|
-
const { intent, projectPath } = request.params.arguments;
|
|
38
|
-
const fns = extractIRPython(projectPath);
|
|
39
|
-
fs.writeFileSync("ir.json", JSON.stringify(fns, null, 2));
|
|
40
|
-
const actions = await plan(intent);
|
|
41
|
-
if (!actions || actions.length === 0) {
|
|
42
|
-
return { content: [{ type: "text", text: "无法生成满足约束的代码。" }] };
|
|
43
|
-
}
|
|
44
|
-
const code = emitPython(actions);
|
|
45
|
-
recordRun(intent, actions, true);
|
|
46
|
-
return { content: [{ type: "text", text: code }] };
|
|
47
|
-
}
|
|
48
|
-
throw new Error("未知工具");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const transport = new StdioServerTransport();
|
|
52
|
-
await server.connect(transport);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
main().catch(console.error);
|