@ik-firewall/core 1.0.2 → 2.3.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/index.cjs +381 -97
- package/dist/index.d.cts +18 -3
- package/dist/index.d.ts +18 -3
- package/dist/index.js +371 -97
- package/package.json +6 -4
- package/scripts/setup-runtime.js +60 -19
package/dist/index.js
CHANGED
|
@@ -16,15 +16,18 @@ if (typeof window === "undefined") {
|
|
|
16
16
|
var Registry = class _Registry {
|
|
17
17
|
static instance;
|
|
18
18
|
registryPath;
|
|
19
|
+
usagePath;
|
|
19
20
|
hasWarnedNoFs = false;
|
|
20
21
|
hasWarnedSaveError = false;
|
|
21
22
|
constructor() {
|
|
22
23
|
this.registryPath = "";
|
|
24
|
+
this.usagePath = "";
|
|
23
25
|
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
24
26
|
try {
|
|
25
27
|
fs = __require("fs");
|
|
26
28
|
path = __require("path");
|
|
27
29
|
this.registryPath = path.join(process.cwd(), ".ik-adapter", "registry.json");
|
|
30
|
+
this.usagePath = path.join(process.cwd(), ".ik-adapter", "usage.json");
|
|
28
31
|
this.ensureDirectory();
|
|
29
32
|
} catch (e) {
|
|
30
33
|
if (!this.hasWarnedNoFs) {
|
|
@@ -59,8 +62,13 @@ var Registry = class _Registry {
|
|
|
59
62
|
if (!fs || !this.registryPath) return {};
|
|
60
63
|
try {
|
|
61
64
|
if (fs.existsSync(this.registryPath)) {
|
|
62
|
-
const
|
|
63
|
-
|
|
65
|
+
const rawData = fs.readFileSync(this.registryPath, "utf8");
|
|
66
|
+
try {
|
|
67
|
+
const decoded = Buffer.from(rawData, "base64").toString("utf8");
|
|
68
|
+
return JSON.parse(decoded);
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return JSON.parse(rawData);
|
|
71
|
+
}
|
|
64
72
|
}
|
|
65
73
|
} catch (error) {
|
|
66
74
|
console.error("[IK_REGISTRY] Load error:", error);
|
|
@@ -80,7 +88,9 @@ var Registry = class _Registry {
|
|
|
80
88
|
}
|
|
81
89
|
try {
|
|
82
90
|
this.ensureDirectory();
|
|
83
|
-
|
|
91
|
+
const jsonStr = JSON.stringify(data, null, 2);
|
|
92
|
+
const obfuscated = Buffer.from(jsonStr).toString("base64");
|
|
93
|
+
fs.writeFileSync(this.registryPath, obfuscated, "utf8");
|
|
84
94
|
} catch (error) {
|
|
85
95
|
if (!this.hasWarnedSaveError) {
|
|
86
96
|
console.error("\n[IK_REGISTRY] \u{1F6A8} ERROR saving registry.json. Your environment likely has a read-only filesystem (e.g. Vercel Serverless).");
|
|
@@ -111,17 +121,76 @@ var Registry = class _Registry {
|
|
|
111
121
|
delete all[instanceId];
|
|
112
122
|
this.save(all);
|
|
113
123
|
}
|
|
124
|
+
getUsageFilePath() {
|
|
125
|
+
return this.usagePath;
|
|
126
|
+
}
|
|
127
|
+
exists(filePath) {
|
|
128
|
+
return fs && fs.existsSync(filePath);
|
|
129
|
+
}
|
|
130
|
+
loadUsage() {
|
|
131
|
+
if (!fs || !this.usagePath || !fs.existsSync(this.usagePath)) return [];
|
|
132
|
+
try {
|
|
133
|
+
const raw = fs.readFileSync(this.usagePath, "utf8");
|
|
134
|
+
return JSON.parse(raw);
|
|
135
|
+
} catch (e) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
saveUsage(data) {
|
|
140
|
+
if (!fs || !this.usagePath) return;
|
|
141
|
+
try {
|
|
142
|
+
fs.writeFileSync(this.usagePath, JSON.stringify(data, null, 2), "utf8");
|
|
143
|
+
} catch (e) {
|
|
144
|
+
console.error("[IK_REGISTRY] Failed to save usage:", e);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
clearUsage() {
|
|
148
|
+
if (!fs || !this.usagePath) return;
|
|
149
|
+
try {
|
|
150
|
+
if (fs.existsSync(this.usagePath)) {
|
|
151
|
+
fs.unlinkSync(this.usagePath);
|
|
152
|
+
}
|
|
153
|
+
} catch (e) {
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/lib/hmac.ts
|
|
159
|
+
import crypto from "crypto";
|
|
160
|
+
var IKHmac = class {
|
|
161
|
+
secret;
|
|
162
|
+
constructor(secret) {
|
|
163
|
+
this.secret = secret;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Verifies that a response from the IK-Firewall server is authentic.
|
|
167
|
+
*/
|
|
168
|
+
verify(data) {
|
|
169
|
+
if (!data || !data.signature) return false;
|
|
170
|
+
const { signature, ...rest } = data;
|
|
171
|
+
const payload = JSON.stringify(rest);
|
|
172
|
+
const expectedSignature = crypto.createHmac("sha256", this.secret).update(payload).digest("hex");
|
|
173
|
+
return signature === expectedSignature;
|
|
174
|
+
}
|
|
114
175
|
};
|
|
115
176
|
|
|
116
177
|
// src/ConfigManager.ts
|
|
117
178
|
var ConfigManager = class {
|
|
118
179
|
config;
|
|
119
180
|
registry;
|
|
120
|
-
|
|
181
|
+
hooks;
|
|
182
|
+
constructor(initialConfig, hooks) {
|
|
121
183
|
this.config = initialConfig;
|
|
184
|
+
this.hooks = hooks;
|
|
122
185
|
this.registry = Registry.getInstance();
|
|
123
186
|
this.loadFromRegistry();
|
|
124
187
|
}
|
|
188
|
+
getRegistry() {
|
|
189
|
+
return this.registry;
|
|
190
|
+
}
|
|
191
|
+
setHooks(hooks) {
|
|
192
|
+
this.hooks = hooks;
|
|
193
|
+
}
|
|
125
194
|
loadFromRegistry() {
|
|
126
195
|
const instanceConfigs = this.registry.load();
|
|
127
196
|
this.config.instanceConfigs = {
|
|
@@ -129,6 +198,76 @@ var ConfigManager = class {
|
|
|
129
198
|
...instanceConfigs
|
|
130
199
|
};
|
|
131
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Syncs remote configuration (pricing, license status) from the Vercel API.
|
|
203
|
+
* Runs once every 24 hours (Heartbeat).
|
|
204
|
+
*/
|
|
205
|
+
async syncRemoteConfig(instanceId) {
|
|
206
|
+
const now = /* @__PURE__ */ new Date();
|
|
207
|
+
const instanceConfig = this.config.instanceConfigs?.[instanceId];
|
|
208
|
+
if (instanceConfig?.lastSyncDate) {
|
|
209
|
+
const lastSync = new Date(instanceConfig.lastSyncDate);
|
|
210
|
+
const hoursSinceSync = (now.getTime() - lastSync.getTime()) / (1e3 * 60 * 60);
|
|
211
|
+
if (hoursSinceSync < 24) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
const endpoint = this.config.centralReportEndpoint || "https://ik-firewall.vercel.app/api/v1/sync";
|
|
217
|
+
const instanceConfig2 = this.config.instanceConfigs?.[instanceId];
|
|
218
|
+
const registrationEmail = instanceConfig2?.ownerEmail || this.config.ownerEmail;
|
|
219
|
+
if ((!instanceConfig2?.licenseKey || instanceConfig2.licenseKey.startsWith("TRIAL-")) && registrationEmail) {
|
|
220
|
+
const success = await this.registerInstance(instanceId, registrationEmail);
|
|
221
|
+
if (success) {
|
|
222
|
+
this.hooks?.onStatus?.("\u2728 IK_SYNC: Zero-Config registration successful. License issued.");
|
|
223
|
+
return;
|
|
224
|
+
} else {
|
|
225
|
+
this.hooks?.onStatus?.("\u26A0\uFE0F IK_SYNC: Auto-registration failed. Continuing with local trial.");
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
this.hooks?.onStatus?.(`\u{1F4E1} IK_SYNC: Heartbeat to ${endpoint}...`);
|
|
229
|
+
const response = await fetch(`${endpoint}?licenseKey=${instanceConfig2?.licenseKey || ""}&instanceId=${instanceId}&version=2.2.0`);
|
|
230
|
+
if (response.ok) {
|
|
231
|
+
const data = await response.json();
|
|
232
|
+
if (this.config.hmacSecret) {
|
|
233
|
+
const verifier = new IKHmac(this.config.hmacSecret);
|
|
234
|
+
if (!verifier.verify(data)) {
|
|
235
|
+
console.error("[IK_SYNC] \u{1F6A8} Signature verification failed! Remote config rejected.");
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
this.setInstanceConfig(instanceId, {
|
|
240
|
+
billingStatus: data.billingStatus,
|
|
241
|
+
isVerified: data.isVerified,
|
|
242
|
+
pricing: data.pricing,
|
|
243
|
+
lastSyncDate: now.toISOString()
|
|
244
|
+
});
|
|
245
|
+
this.hooks?.onStatus?.("\u2705 IK_SYNC: Remote configuration updated and verified.");
|
|
246
|
+
} else {
|
|
247
|
+
throw new Error(`Server responded with ${response.status}`);
|
|
248
|
+
}
|
|
249
|
+
if (!instanceConfig2?.firstUseDate) {
|
|
250
|
+
this.setInstanceConfig(instanceId, { firstUseDate: now.toISOString() });
|
|
251
|
+
}
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.error(`[IK_CONFIG] Remote sync failed (${error.message}). Falling open (Fail-Open mode).`);
|
|
254
|
+
this.setInstanceConfig(instanceId, {
|
|
255
|
+
isVerified: true,
|
|
256
|
+
lastSyncDate: now.toISOString()
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
const registry = this.registry;
|
|
261
|
+
if (registry.exists(registry.getUsageFilePath())) {
|
|
262
|
+
const aggregatedUsage = registry.loadUsage();
|
|
263
|
+
if (aggregatedUsage.length > 0) {
|
|
264
|
+
this.hooks?.onStatus?.(`\u{1F4CA} IK_SYNC: Found ${aggregatedUsage.length} pending usage reports. Preparation for Daily Flush...`);
|
|
265
|
+
this._pendingUsage = aggregatedUsage;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
} catch (e) {
|
|
269
|
+
}
|
|
270
|
+
}
|
|
132
271
|
getConfig() {
|
|
133
272
|
return { ...this.config };
|
|
134
273
|
}
|
|
@@ -168,6 +307,40 @@ var ConfigManager = class {
|
|
|
168
307
|
}
|
|
169
308
|
this.updateConfig({ plugins });
|
|
170
309
|
}
|
|
310
|
+
async registerInstance(instanceId, email) {
|
|
311
|
+
try {
|
|
312
|
+
const endpoint = (this.config.centralReportEndpoint || "https://ik-firewall.vercel.app/api/v1").replace(/\/sync$/, "") + "/register";
|
|
313
|
+
this.hooks?.onStatus?.(`\u{1F680} IK_REGISTRY: Registering new instance for ${email}...`);
|
|
314
|
+
const response = await fetch(endpoint, {
|
|
315
|
+
method: "POST",
|
|
316
|
+
headers: { "Content-Type": "application/json" },
|
|
317
|
+
body: JSON.stringify({ email, instanceId })
|
|
318
|
+
});
|
|
319
|
+
if (response.ok) {
|
|
320
|
+
const data = await response.json();
|
|
321
|
+
if (this.config.hmacSecret) {
|
|
322
|
+
const verifier = new IKHmac(this.config.hmacSecret);
|
|
323
|
+
if (!verifier.verify(data)) {
|
|
324
|
+
console.error("[IK_REGISTRY] \u{1F6A8} Registration signature mismatch. Data rejected!");
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
this.setInstanceConfig(instanceId, {
|
|
329
|
+
licenseKey: data.licenseKey,
|
|
330
|
+
billingStatus: data.status.toLowerCase(),
|
|
331
|
+
isVerified: true,
|
|
332
|
+
firstUseDate: (/* @__PURE__ */ new Date()).toISOString(),
|
|
333
|
+
pricing: data.pricing
|
|
334
|
+
});
|
|
335
|
+
this.hooks?.onStatus?.("\u2728 IK_REGISTRY: Instance registered successfully and license issued.");
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
return false;
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.error("[IK_REGISTRY] Registration failed:", error);
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
171
344
|
ensureInstanceConfig(instanceId) {
|
|
172
345
|
const all = this.registry.load();
|
|
173
346
|
if (!all[instanceId]) {
|
|
@@ -175,7 +348,9 @@ var ConfigManager = class {
|
|
|
175
348
|
balance: this.config.balance || 0.5,
|
|
176
349
|
aggressiveness: this.config.aggressiveness || 0.5,
|
|
177
350
|
isEnabled: true,
|
|
178
|
-
contextMode: "FIRST_MESSAGE"
|
|
351
|
+
contextMode: "FIRST_MESSAGE",
|
|
352
|
+
ownerEmail: this.config.ownerEmail
|
|
353
|
+
// Inherit from global if not specific
|
|
179
354
|
});
|
|
180
355
|
this.loadFromRegistry();
|
|
181
356
|
}
|
|
@@ -1248,18 +1423,12 @@ var Orchestrator = class {
|
|
|
1248
1423
|
};
|
|
1249
1424
|
|
|
1250
1425
|
// src/UsageTracker.ts
|
|
1251
|
-
var UsageTracker = class
|
|
1426
|
+
var UsageTracker = class {
|
|
1252
1427
|
buffer = [];
|
|
1253
1428
|
MAX_BUFFER_SIZE = 1;
|
|
1254
1429
|
// Immediate reporting for Edge/Stateless environments
|
|
1255
1430
|
hooks;
|
|
1256
1431
|
config;
|
|
1257
|
-
// Static pricing benchmarks (cost per 1k tokens in USD)
|
|
1258
|
-
static PRICING = {
|
|
1259
|
-
"gpt-4o": { input: 5e-3, output: 0.015 },
|
|
1260
|
-
"gpt-4o-mini": { input: 15e-5, output: 6e-4 },
|
|
1261
|
-
"local": { input: 0, output: 0 }
|
|
1262
|
-
};
|
|
1263
1432
|
constructor(config, hooks) {
|
|
1264
1433
|
this.config = config;
|
|
1265
1434
|
this.hooks = hooks;
|
|
@@ -1270,8 +1439,10 @@ var UsageTracker = class _UsageTracker {
|
|
|
1270
1439
|
async logInteraction(params) {
|
|
1271
1440
|
const { instanceId, model, inputTokens, outputTokens, optimizedTokens = 0, cqScore, routingPath, clientOrigin, trace } = params;
|
|
1272
1441
|
const tokensSaved = optimizedTokens > 0 ? inputTokens - optimizedTokens : 0;
|
|
1273
|
-
const
|
|
1274
|
-
const
|
|
1442
|
+
const instanceConfig = this.config.getConfig().instanceConfigs?.[instanceId];
|
|
1443
|
+
const remotePricing = instanceConfig?.pricing?.[model] || { input: 5e-3, output: 0.015 };
|
|
1444
|
+
const costSaved = tokensSaved / 1e3 * remotePricing.input;
|
|
1445
|
+
const commissionDue = costSaved * 0.2;
|
|
1275
1446
|
const data = {
|
|
1276
1447
|
instanceId,
|
|
1277
1448
|
model_used: model,
|
|
@@ -1281,6 +1452,7 @@ var UsageTracker = class _UsageTracker {
|
|
|
1281
1452
|
optimized_tokens: optimizedTokens,
|
|
1282
1453
|
tokens_saved: tokensSaved,
|
|
1283
1454
|
cost_saved: Number(costSaved.toFixed(6)),
|
|
1455
|
+
commission_due: Number(commissionDue.toFixed(6)),
|
|
1284
1456
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1285
1457
|
is_local: model === "local",
|
|
1286
1458
|
cq_score: cqScore,
|
|
@@ -1289,40 +1461,71 @@ var UsageTracker = class _UsageTracker {
|
|
|
1289
1461
|
};
|
|
1290
1462
|
this.buffer.push(data);
|
|
1291
1463
|
this.hooks?.onUsageReported?.(data);
|
|
1292
|
-
|
|
1293
|
-
|
|
1464
|
+
await this.persistToLocalBuffer(data);
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Appends usage data to the local .ik-adapter/usage.json file
|
|
1468
|
+
*/
|
|
1469
|
+
async persistToLocalBuffer(data) {
|
|
1470
|
+
try {
|
|
1471
|
+
const registry = this.config.getRegistry();
|
|
1472
|
+
const usageFile = registry.getUsageFilePath();
|
|
1473
|
+
let existingUsage = [];
|
|
1474
|
+
if (registry.exists(usageFile)) {
|
|
1475
|
+
existingUsage = registry.loadUsage();
|
|
1476
|
+
}
|
|
1477
|
+
existingUsage.push(data);
|
|
1478
|
+
registry.saveUsage(existingUsage);
|
|
1479
|
+
} catch (e) {
|
|
1480
|
+
console.error("[IK_TRACKER] Failed to persist usage to local buffer:", e);
|
|
1294
1481
|
}
|
|
1295
1482
|
}
|
|
1296
1483
|
/**
|
|
1297
1484
|
* Send buffered usage data to the central IK billing service
|
|
1298
1485
|
*/
|
|
1299
|
-
async flush() {
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
this.buffer = [];
|
|
1305
|
-
return;
|
|
1306
|
-
}
|
|
1486
|
+
async flush(aggregatedData) {
|
|
1487
|
+
const usageToFlush = aggregatedData || this.buffer;
|
|
1488
|
+
if (usageToFlush.length === 0) return;
|
|
1489
|
+
const endpoint = this.config.get("centralReportEndpoint") || "https://ik-firewall.vercel.app/api/v1/report";
|
|
1490
|
+
const hmacSecret = this.config.get("hmacSecret");
|
|
1307
1491
|
try {
|
|
1308
|
-
|
|
1309
|
-
|
|
1492
|
+
this.hooks?.onStatus?.(`\u{1F4E4} IK_TRACKER: Flushing ${usageToFlush.length} interactions to ${endpoint}...`);
|
|
1493
|
+
const reportsByInstance = {};
|
|
1494
|
+
for (const report of usageToFlush) {
|
|
1495
|
+
if (!reportsByInstance[report.instanceId]) reportsByInstance[report.instanceId] = [];
|
|
1496
|
+
reportsByInstance[report.instanceId].push(report);
|
|
1497
|
+
}
|
|
1498
|
+
for (const [instanceId, reports] of Object.entries(reportsByInstance)) {
|
|
1499
|
+
const instanceConfig = this.config.getConfig().instanceConfigs?.[instanceId];
|
|
1500
|
+
const payload = {
|
|
1501
|
+
licenseKey: instanceConfig?.licenseKey || "",
|
|
1502
|
+
instanceId,
|
|
1503
|
+
reports: reports.map((r) => ({
|
|
1504
|
+
modelName: r.model_used,
|
|
1505
|
+
tokensIn: r.input_tokens,
|
|
1506
|
+
tokensOut: r.output_tokens,
|
|
1507
|
+
tokensSaved: r.tokens_saved,
|
|
1508
|
+
date: r.timestamp
|
|
1509
|
+
}))
|
|
1510
|
+
};
|
|
1511
|
+
if (hmacSecret) {
|
|
1512
|
+
const jsonStr = JSON.stringify(payload);
|
|
1513
|
+
const crypto2 = await import("crypto");
|
|
1514
|
+
payload.signature = crypto2.createHmac("sha256", hmacSecret).update(jsonStr).digest("hex");
|
|
1515
|
+
}
|
|
1516
|
+
const response = await fetch(endpoint, {
|
|
1310
1517
|
method: "POST",
|
|
1311
1518
|
headers: { "Content-Type": "application/json" },
|
|
1312
|
-
body: JSON.stringify(
|
|
1313
|
-
instanceId: report.instanceId,
|
|
1314
|
-
model: report.model_used,
|
|
1315
|
-
inputTokens: report.input_tokens,
|
|
1316
|
-
outputTokens: report.output_tokens,
|
|
1317
|
-
optimizedTokens: report.optimized_tokens,
|
|
1318
|
-
cqScore: report.cq_score,
|
|
1319
|
-
routingPath: report.routingPath,
|
|
1320
|
-
clientOrigin: report.clientOrigin,
|
|
1321
|
-
trace: report.trace
|
|
1322
|
-
})
|
|
1519
|
+
body: JSON.stringify(payload)
|
|
1323
1520
|
});
|
|
1521
|
+
if (!response.ok) {
|
|
1522
|
+
console.error(`[IK_TRACKER] Failed to send aggregate for ${instanceId}: ${response.statusText}`);
|
|
1523
|
+
}
|
|
1324
1524
|
}
|
|
1325
1525
|
this.buffer = [];
|
|
1526
|
+
if (aggregatedData) {
|
|
1527
|
+
this.config.getRegistry().clearUsage();
|
|
1528
|
+
}
|
|
1326
1529
|
} catch (error) {
|
|
1327
1530
|
console.error("[IK_TRACKER] Failed to flush usage data:", error);
|
|
1328
1531
|
}
|
|
@@ -1332,6 +1535,49 @@ var UsageTracker = class _UsageTracker {
|
|
|
1332
1535
|
}
|
|
1333
1536
|
};
|
|
1334
1537
|
|
|
1538
|
+
// src/CognitiveEngine.ts
|
|
1539
|
+
var CognitiveEngine = class {
|
|
1540
|
+
/**
|
|
1541
|
+
* Sniff Cognitive DNA (CQ Score: 0-100)
|
|
1542
|
+
* Higher score = higher quality, human-like, structured content.
|
|
1543
|
+
*/
|
|
1544
|
+
static sniffDNA(input) {
|
|
1545
|
+
if (!input || input.trim().length === 0) return 0;
|
|
1546
|
+
const length = input.length;
|
|
1547
|
+
const words = input.split(/\s+/).filter((w) => w.length > 0);
|
|
1548
|
+
const wordCount = words.length;
|
|
1549
|
+
const sentences = input.split(/[.!?]+/).filter((s) => s.trim().length > 0);
|
|
1550
|
+
const avgSentenceLength = sentences.length > 0 ? wordCount / sentences.length : 0;
|
|
1551
|
+
const sentenceVariety = sentences.length > 1 ? 10 : 0;
|
|
1552
|
+
const uniqueWords = new Set(words.map((w) => w.toLowerCase())).size;
|
|
1553
|
+
const lexicalBreadth = wordCount > 0 ? uniqueWords / wordCount * 40 : 0;
|
|
1554
|
+
const markers = (input.match(/[,;:"'()\[\]]/g) || []).length;
|
|
1555
|
+
const structuralHealth = Math.min(markers / wordCount * 100, 20);
|
|
1556
|
+
const repetitivePenalty = this.calculateRepetitivePenalty(input);
|
|
1557
|
+
let score = lexicalBreadth + structuralHealth + sentenceVariety + (avgSentenceLength > 5 ? 20 : 0);
|
|
1558
|
+
score -= repetitivePenalty;
|
|
1559
|
+
return Math.max(0, Math.min(100, score));
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Calculate Crystallization Threshold
|
|
1563
|
+
* Logic:
|
|
1564
|
+
* Low CQ (Noise) -> Aggressive optimization (Lower threshold, e.g. 0.6)
|
|
1565
|
+
* High CQ (Quality) -> Conservative optimization (Higher threshold, e.g. 0.9)
|
|
1566
|
+
*/
|
|
1567
|
+
static calculateThreshold(cqScore) {
|
|
1568
|
+
if (cqScore < 30) return 0.65;
|
|
1569
|
+
if (cqScore < 60) return 0.78;
|
|
1570
|
+
if (cqScore < 85) return 0.88;
|
|
1571
|
+
return 0.95;
|
|
1572
|
+
}
|
|
1573
|
+
static calculateRepetitivePenalty(input) {
|
|
1574
|
+
const chunks = input.match(/.{1,10}/g) || [];
|
|
1575
|
+
const uniqueChunks = new Set(chunks).size;
|
|
1576
|
+
const ratio = chunks.length > 10 ? uniqueChunks / chunks.length : 1;
|
|
1577
|
+
return ratio < 0.5 ? (1 - ratio) * 50 : 0;
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
|
|
1335
1581
|
// src/core.ts
|
|
1336
1582
|
var IKFirewallCore = class _IKFirewallCore {
|
|
1337
1583
|
static instance;
|
|
@@ -1386,55 +1632,75 @@ var IKFirewallCore = class _IKFirewallCore {
|
|
|
1386
1632
|
FLUID_THRESHOLD: 4
|
|
1387
1633
|
}
|
|
1388
1634
|
};
|
|
1389
|
-
gatekeeper
|
|
1635
|
+
gatekeeper;
|
|
1390
1636
|
configManager;
|
|
1391
1637
|
sessionDNA = 0;
|
|
1392
|
-
hooks;
|
|
1393
1638
|
cloudProvider;
|
|
1394
1639
|
localProvider;
|
|
1395
1640
|
orchestrator;
|
|
1396
1641
|
usageTracker;
|
|
1642
|
+
_hooks;
|
|
1643
|
+
defaultConfig;
|
|
1397
1644
|
constructor(config, hooks, cloudProvider) {
|
|
1398
|
-
|
|
1645
|
+
this.defaultConfig = {
|
|
1399
1646
|
aggressiveness: 0.15,
|
|
1400
1647
|
forcedEfficiency: false,
|
|
1401
1648
|
precisionBias: _IKFirewallCore.CONSTANTS.PRECISION.BIAS_DEFAULT,
|
|
1402
1649
|
balance: _IKFirewallCore.CONSTANTS.TONE.STABLE_VAL,
|
|
1403
1650
|
gatekeeperEnabled: true,
|
|
1404
1651
|
locale: "en",
|
|
1405
|
-
providerMode: "hybrid",
|
|
1652
|
+
providerMode: process.env.IK_FIREWALL_MODE || "hybrid",
|
|
1406
1653
|
runtimeStrategy: "internal",
|
|
1407
|
-
localAuditEndpoint: "http://127.0.0.1:8085/v1/chat/completions",
|
|
1408
|
-
|
|
1654
|
+
localAuditEndpoint: process.env.IK_FIREWALL_LOCAL_ENDPOINT || "http://127.0.0.1:8085/v1/chat/completions",
|
|
1655
|
+
centralReportEndpoint: process.env.IK_FIREWALL_CENTRAL_ENDPOINT || "https://ik-firewall.vercel.app/api/v1/sync",
|
|
1656
|
+
ownerEmail: process.env.IK_FIREWALL_EMAIL,
|
|
1657
|
+
hmacSecret: process.env.IK_FIREWALL_SECRET,
|
|
1658
|
+
fallbackToCloudAudit: process.env.IK_FIREWALL_FALLBACK === "true" || false,
|
|
1409
1659
|
modelConfig: {
|
|
1410
1660
|
modelType: "1b"
|
|
1411
1661
|
},
|
|
1412
1662
|
plugins: []
|
|
1413
1663
|
};
|
|
1414
|
-
this.
|
|
1415
|
-
|
|
1664
|
+
this._hooks = hooks;
|
|
1665
|
+
this.configManager = new ConfigManager({ ...this.defaultConfig, ...config }, hooks);
|
|
1666
|
+
this.gatekeeper = new HeuristicGatekeeper();
|
|
1667
|
+
this.sessionDNA = _IKFirewallCore.CONSTANTS.DNA.MIN;
|
|
1416
1668
|
const activeConfig = this.configManager.getConfig();
|
|
1417
1669
|
this.localProvider = new LocalProvider(activeConfig.localAuditEndpoint);
|
|
1418
1670
|
if (cloudProvider) {
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
}
|
|
1434
|
-
|
|
1671
|
+
this.cloudProvider = cloudProvider instanceof BaseProvider ? cloudProvider : new OpenAIProvider(cloudProvider);
|
|
1672
|
+
} else if (process.env.OPENAI_API_KEY) {
|
|
1673
|
+
this.cloudProvider = new OpenAIProvider(async (prompt, role) => {
|
|
1674
|
+
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
1675
|
+
method: "POST",
|
|
1676
|
+
headers: {
|
|
1677
|
+
"Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
|
|
1678
|
+
"Content-Type": "application/json"
|
|
1679
|
+
},
|
|
1680
|
+
body: JSON.stringify({
|
|
1681
|
+
model: role === "audit_layer" ? "gpt-4o-mini" : "gpt-4o",
|
|
1682
|
+
messages: [{ role: "user", content: prompt }],
|
|
1683
|
+
temperature: 0.1
|
|
1684
|
+
})
|
|
1685
|
+
});
|
|
1686
|
+
if (!response.ok) throw new Error(`OpenAI API Error: ${response.statusText}`);
|
|
1687
|
+
const data = await response.json();
|
|
1688
|
+
return {
|
|
1689
|
+
content: data.choices[0].message.content,
|
|
1690
|
+
usage: data.usage
|
|
1691
|
+
};
|
|
1692
|
+
});
|
|
1435
1693
|
}
|
|
1436
1694
|
this.orchestrator = new Orchestrator(this.configManager);
|
|
1437
|
-
this.usageTracker = new UsageTracker(this.configManager, this.
|
|
1695
|
+
this.usageTracker = new UsageTracker(this.configManager, this._hooks);
|
|
1696
|
+
}
|
|
1697
|
+
get hooks() {
|
|
1698
|
+
return this._hooks;
|
|
1699
|
+
}
|
|
1700
|
+
set hooks(newHooks) {
|
|
1701
|
+
this._hooks = newHooks;
|
|
1702
|
+
this.configManager.setHooks(newHooks);
|
|
1703
|
+
this.usageTracker.hooks = newHooks;
|
|
1438
1704
|
}
|
|
1439
1705
|
static getInstance(config, hooks) {
|
|
1440
1706
|
if (!_IKFirewallCore.instance) {
|
|
@@ -1546,6 +1812,21 @@ var IKFirewallCore = class _IKFirewallCore {
|
|
|
1546
1812
|
async analyze(input, provider, personaName = "professional", locale, instanceId) {
|
|
1547
1813
|
if (instanceId) {
|
|
1548
1814
|
this.configManager.ensureInstanceConfig(instanceId);
|
|
1815
|
+
const syncPromise = this.configManager.syncRemoteConfig(instanceId).then(async () => {
|
|
1816
|
+
const pendingUsage = this.configManager._pendingUsage;
|
|
1817
|
+
if (pendingUsage && pendingUsage.length > 0) {
|
|
1818
|
+
await this.usageTracker.flush(pendingUsage);
|
|
1819
|
+
this.configManager._pendingUsage = null;
|
|
1820
|
+
}
|
|
1821
|
+
});
|
|
1822
|
+
await syncPromise;
|
|
1823
|
+
}
|
|
1824
|
+
const mergedConfig = this.configManager.getMergedConfig(instanceId);
|
|
1825
|
+
if (instanceId && mergedConfig?.isVerified === false) {
|
|
1826
|
+
if (mergedConfig?.billingStatus === "blocked") {
|
|
1827
|
+
this.hooks?.onStatus?.("\u26A0\uFE0F [IK_FIREWALL] Subscription inactive. Optimization bypassed (Fail-Safe mode).");
|
|
1828
|
+
return this.getEmptyMetrics();
|
|
1829
|
+
}
|
|
1549
1830
|
}
|
|
1550
1831
|
let auditProvider = this.localProvider;
|
|
1551
1832
|
let overrideProvider = null;
|
|
@@ -1580,18 +1861,20 @@ var IKFirewallCore = class _IKFirewallCore {
|
|
|
1580
1861
|
const ei = (input.match(/[,;.:-]/g) || []).length / (input.split(" ").length || 1);
|
|
1581
1862
|
let requiresAuditOverride = false;
|
|
1582
1863
|
let boundaryMetrics = { boundaryScore: 0, requiresHumanIntervention: false };
|
|
1583
|
-
if (this.getConfig()
|
|
1584
|
-
boundaryMetrics = this.gatekeeper.evaluateBoundaryCommunication(input, this.getConfig()
|
|
1585
|
-
if (boundaryMetrics
|
|
1864
|
+
if (this.getConfig()?.safeMode) {
|
|
1865
|
+
boundaryMetrics = this.gatekeeper.evaluateBoundaryCommunication(input, this.getConfig()?.customBoundaryKeywords);
|
|
1866
|
+
if (boundaryMetrics?.requiresHumanIntervention) {
|
|
1586
1867
|
requiresAuditOverride = true;
|
|
1587
1868
|
}
|
|
1588
1869
|
}
|
|
1589
1870
|
const skipAudit = !requiresAuditOverride && this.getConfig().gatekeeperEnabled && localInsight.complexityScore < _IKFirewallCore.CONSTANTS.HEURISTICS.COMPLEXITY_SKIP_THRESHOLD;
|
|
1590
1871
|
if (skipAudit) {
|
|
1591
1872
|
this.hooks?.onStatus?.("\u26A1 IK_GATEKEEPER: Simple request detected. Skipping Deep Audit.");
|
|
1592
|
-
const metrics2 = this.createHeuristicMetrics(input, dm, ei, uniqueWords, localInsight
|
|
1593
|
-
metrics2.semanticInsights
|
|
1594
|
-
|
|
1873
|
+
const metrics2 = this.createHeuristicMetrics(input, dm, ei, uniqueWords, localInsight?.intentMode);
|
|
1874
|
+
if (metrics2.semanticInsights) {
|
|
1875
|
+
metrics2.semanticInsights.boundaryScore = boundaryMetrics.boundaryScore;
|
|
1876
|
+
metrics2.semanticInsights.requiresHumanIntervention = boundaryMetrics.requiresHumanIntervention;
|
|
1877
|
+
}
|
|
1595
1878
|
this.hooks?.onAuditComplete?.(metrics2);
|
|
1596
1879
|
return metrics2;
|
|
1597
1880
|
}
|
|
@@ -1606,10 +1889,17 @@ var IKFirewallCore = class _IKFirewallCore {
|
|
|
1606
1889
|
throw e;
|
|
1607
1890
|
}
|
|
1608
1891
|
}
|
|
1609
|
-
|
|
1892
|
+
const cqScore = CognitiveEngine.sniffDNA(input);
|
|
1893
|
+
metrics.dm = Math.max(metrics.dm, cqScore / 10);
|
|
1894
|
+
this.applyHardening(input, metrics, activeLocale);
|
|
1610
1895
|
this.hooks?.onAuditComplete?.(metrics);
|
|
1611
1896
|
return metrics;
|
|
1612
1897
|
}
|
|
1898
|
+
applyHardening(input, metrics, locale = "en") {
|
|
1899
|
+
const archetype = metrics.semanticInsights?.archetype || "DYNAMIC";
|
|
1900
|
+
const domain = metrics.semanticInsights?.detectedDomain || "GENERAL";
|
|
1901
|
+
this.setSessionDNA(this.sessionDNA + 5);
|
|
1902
|
+
}
|
|
1613
1903
|
getEmptyMetrics() {
|
|
1614
1904
|
return {
|
|
1615
1905
|
dm: 0,
|
|
@@ -1661,39 +1951,17 @@ var IKFirewallCore = class _IKFirewallCore {
|
|
|
1661
1951
|
inputLength: input.length
|
|
1662
1952
|
};
|
|
1663
1953
|
}
|
|
1664
|
-
sniffDNA(input, metrics, locale = "en") {
|
|
1665
|
-
const archetype = metrics.semanticInsights?.archetype || "DYNAMIC";
|
|
1666
|
-
const domain = metrics.semanticInsights?.detectedDomain || "GENERAL";
|
|
1667
|
-
const meta = Dictionaries.meta[locale] || Dictionaries.meta.en;
|
|
1668
|
-
const patterns = (meta.system_prompt_patterns || "").split(",").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
1669
|
-
const isSystemPrompt = patterns.some((kw) => input.toLowerCase().includes(kw));
|
|
1670
|
-
let dnaShift = 0;
|
|
1671
|
-
if (archetype === "CRYSTAL") {
|
|
1672
|
-
dnaShift += _IKFirewallCore.CONSTANTS.DNA.HARDENING_MODERATE;
|
|
1673
|
-
} else if (archetype === "FLUID") {
|
|
1674
|
-
dnaShift -= _IKFirewallCore.CONSTANTS.DNA.SOFTENING_STEP;
|
|
1675
|
-
} else if (archetype === "NEURAL") {
|
|
1676
|
-
dnaShift -= _IKFirewallCore.CONSTANTS.DNA.SOFTENING_STEP / 2;
|
|
1677
|
-
}
|
|
1678
|
-
if (["LEGAL", "MEDICAL", "FINANCE", "CONSTRUCTION", "ENGINEERING"].includes(domain)) {
|
|
1679
|
-
dnaShift += _IKFirewallCore.CONSTANTS.DNA.HARDENING_DOMAIN;
|
|
1680
|
-
}
|
|
1681
|
-
if (isSystemPrompt) {
|
|
1682
|
-
dnaShift += _IKFirewallCore.CONSTANTS.DNA.HARDENING_MODERATE;
|
|
1683
|
-
}
|
|
1684
|
-
this.setSessionDNA(this.sessionDNA + dnaShift);
|
|
1685
|
-
}
|
|
1686
1954
|
async analyzeAIAssisted(input, provider, personaName = "professional", instanceId, configOverride) {
|
|
1687
1955
|
if (instanceId) {
|
|
1688
1956
|
this.configManager.ensureInstanceConfig(instanceId);
|
|
1689
1957
|
}
|
|
1690
1958
|
const activeConfig = this.configManager.getMergedConfig(instanceId, configOverride);
|
|
1691
|
-
const locale = activeConfig
|
|
1959
|
+
const locale = activeConfig?.locale || "en";
|
|
1692
1960
|
const cqScore = this.orchestrator.calculateCQ(input);
|
|
1693
1961
|
let isAuditTask = false;
|
|
1694
|
-
if (activeConfig
|
|
1695
|
-
const boundaryResult = this.gatekeeper.evaluateBoundaryCommunication(input, activeConfig
|
|
1696
|
-
if (boundaryResult
|
|
1962
|
+
if (activeConfig?.safeMode) {
|
|
1963
|
+
const boundaryResult = this.gatekeeper.evaluateBoundaryCommunication(input, activeConfig?.customBoundaryKeywords);
|
|
1964
|
+
if (boundaryResult?.requiresHumanIntervention) {
|
|
1697
1965
|
isAuditTask = true;
|
|
1698
1966
|
}
|
|
1699
1967
|
}
|
|
@@ -1915,7 +2183,11 @@ Any action outside this scope MUST be rejected and flagged as a boundary violati
|
|
|
1915
2183
|
inputLength
|
|
1916
2184
|
};
|
|
1917
2185
|
}
|
|
1918
|
-
crystallize(input, metrics, locale = "en") {
|
|
2186
|
+
crystallize(input, metrics, locale = "en", instanceId) {
|
|
2187
|
+
const mergedConfig = this.configManager.getMergedConfig(instanceId);
|
|
2188
|
+
if (instanceId && mergedConfig?.isVerified === false) {
|
|
2189
|
+
return input;
|
|
2190
|
+
}
|
|
1919
2191
|
const archetype = metrics?.semanticInsights?.archetype || "DYNAMIC";
|
|
1920
2192
|
const tolerance = metrics?.semanticInsights?.ambiguityTolerance || 0.5;
|
|
1921
2193
|
const blueprint = metrics?.semanticInsights?.conceptualBlueprint;
|
|
@@ -1929,11 +2201,13 @@ Any action outside this scope MUST be rejected and flagged as a boundary violati
|
|
|
1929
2201
|
this.hooks?.onStatus?.(`\u{1F6E1}\uFE0F IK_CRYSTALLIZE: Precision domain detected (${domain}). Relaxing noise reduction...`);
|
|
1930
2202
|
archetypeModifier = -0.3;
|
|
1931
2203
|
}
|
|
2204
|
+
const cqScore = CognitiveEngine.sniffDNA(input);
|
|
2205
|
+
const cognitiveThreshold = CognitiveEngine.calculateThreshold(cqScore);
|
|
1932
2206
|
const threshold = Math.min(
|
|
1933
2207
|
_IKFirewallCore.CONSTANTS.AI.THRESHOLD_MAX,
|
|
1934
2208
|
Math.max(
|
|
1935
2209
|
_IKFirewallCore.CONSTANTS.AI.THRESHOLD_MIN,
|
|
1936
|
-
|
|
2210
|
+
cognitiveThreshold + archetypeModifier - tolerance * 0.1
|
|
1937
2211
|
)
|
|
1938
2212
|
);
|
|
1939
2213
|
const lengthRatio = blueprint ? blueprint.length / input.length : 1;
|