@ik-firewall/core 1.0.1 → 2.2.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 CHANGED
@@ -31,6 +31,7 @@ __export(index_exports, {
31
31
  IKFirewallCore: () => IKFirewallCore,
32
32
  LocalProvider: () => LocalProvider,
33
33
  OpenAIProvider: () => OpenAIProvider,
34
+ PerplexityProvider: () => PerplexityProvider,
34
35
  StressTester: () => StressTester,
35
36
  SurgicalTester: () => SurgicalTester
36
37
  });
@@ -160,6 +161,48 @@ var ConfigManager = class {
160
161
  ...instanceConfigs
161
162
  };
162
163
  }
164
+ /**
165
+ * Syncs remote configuration (pricing, license status) from the Vercel API.
166
+ * Runs once every 24 hours (Heartbeat).
167
+ */
168
+ async syncRemoteConfig(instanceId) {
169
+ const now = /* @__PURE__ */ new Date();
170
+ const instanceConfig = this.config.instanceConfigs?.[instanceId];
171
+ if (instanceConfig?.lastSyncDate) {
172
+ const lastSync = new Date(instanceConfig.lastSyncDate);
173
+ const hoursSinceSync = (now.getTime() - lastSync.getTime()) / (1e3 * 60 * 60);
174
+ if (hoursSinceSync < 24) {
175
+ return;
176
+ }
177
+ }
178
+ try {
179
+ const endpoint = this.config.centralReportEndpoint || "https://ik-firewall-backend.vercel.app/api/v1/sync";
180
+ const data = {
181
+ billingStatus: "trial",
182
+ isVerified: true,
183
+ pricing: {
184
+ "gpt-4o": { input: 5e-3, output: 0.015 },
185
+ "gpt-4o-mini": { input: 15e-5, output: 6e-4 }
186
+ },
187
+ latestVersion: "2.2.0"
188
+ };
189
+ this.setInstanceConfig(instanceId, {
190
+ billingStatus: data.billingStatus,
191
+ isVerified: data.isVerified,
192
+ pricing: data.pricing,
193
+ lastSyncDate: now.toISOString()
194
+ });
195
+ if (!instanceConfig?.firstUseDate) {
196
+ this.setInstanceConfig(instanceId, { firstUseDate: now.toISOString() });
197
+ }
198
+ } catch (error) {
199
+ console.error("[IK_CONFIG] Remote sync failed. Falling open (Fail-Open mode).");
200
+ this.setInstanceConfig(instanceId, {
201
+ isVerified: true,
202
+ lastSyncDate: now.toISOString()
203
+ });
204
+ }
205
+ }
163
206
  getConfig() {
164
207
  return { ...this.config };
165
208
  }
@@ -1149,6 +1192,18 @@ var GeminiProvider = class extends BaseProvider {
1149
1192
  }
1150
1193
  };
1151
1194
 
1195
+ // src/providers/PerplexityProvider.ts
1196
+ var PerplexityProvider = class extends BaseProvider {
1197
+ functionalProvider;
1198
+ constructor(provider) {
1199
+ super();
1200
+ this.functionalProvider = provider;
1201
+ }
1202
+ async call(prompt, role) {
1203
+ return this.functionalProvider(prompt, role);
1204
+ }
1205
+ };
1206
+
1152
1207
  // src/Orchestrator.ts
1153
1208
  var Orchestrator = class {
1154
1209
  config;
@@ -1167,6 +1222,7 @@ var Orchestrator = class {
1167
1222
  if (mode === "anthropic") return { provider: "anthropic", model: "claude-3-5-sonnet-20241022", tier: 4 };
1168
1223
  if (mode === "deepseek") return { provider: "deepseek", model: "deepseek-reasoner", tier: 4 };
1169
1224
  if (mode === "gemini") return { provider: "gemini", model: "gemini-1.5-pro", tier: 4 };
1225
+ if (mode === "perplexity") return { provider: "perplexity", model: "sonar-large-online", tier: 4 };
1170
1226
  return { provider: "openai", model: "gpt-4o", tier: 4 };
1171
1227
  }
1172
1228
  if (mode === "local") {
@@ -1193,6 +1249,13 @@ var Orchestrator = class {
1193
1249
  tier: isHighComplexity ? 3 : 2
1194
1250
  };
1195
1251
  }
1252
+ if (mode === "perplexity") {
1253
+ return {
1254
+ provider: "perplexity",
1255
+ model: isHighComplexity ? "sonar-large-online" : "sonar-small-online",
1256
+ tier: isHighComplexity ? 3 : 2
1257
+ };
1258
+ }
1196
1259
  if (mode === "cloud" || mode === "openai") {
1197
1260
  return {
1198
1261
  provider: "openai",
@@ -1259,7 +1322,7 @@ var Orchestrator = class {
1259
1322
  };
1260
1323
 
1261
1324
  // src/UsageTracker.ts
1262
- var UsageTracker = class _UsageTracker {
1325
+ var UsageTracker = class {
1263
1326
  buffer = [];
1264
1327
  MAX_BUFFER_SIZE = 1;
1265
1328
  // Immediate reporting for Edge/Stateless environments
@@ -1281,8 +1344,10 @@ var UsageTracker = class _UsageTracker {
1281
1344
  async logInteraction(params) {
1282
1345
  const { instanceId, model, inputTokens, outputTokens, optimizedTokens = 0, cqScore, routingPath, clientOrigin, trace } = params;
1283
1346
  const tokensSaved = optimizedTokens > 0 ? inputTokens - optimizedTokens : 0;
1284
- const modelPricing = _UsageTracker.PRICING[model] || _UsageTracker.PRICING["gpt-4o-mini"];
1285
- const costSaved = tokensSaved / 1e3 * modelPricing.input;
1347
+ const instanceConfig = this.config.getConfig().instanceConfigs?.[instanceId];
1348
+ const remotePricing = instanceConfig?.pricing?.[model] || { input: 5e-3, output: 0.015 };
1349
+ const costSaved = tokensSaved / 1e3 * remotePricing.input;
1350
+ const commissionDue = costSaved * 0.2;
1286
1351
  const data = {
1287
1352
  instanceId,
1288
1353
  model_used: model,
@@ -1292,6 +1357,7 @@ var UsageTracker = class _UsageTracker {
1292
1357
  optimized_tokens: optimizedTokens,
1293
1358
  tokens_saved: tokensSaved,
1294
1359
  cost_saved: Number(costSaved.toFixed(6)),
1360
+ commission_due: Number(commissionDue.toFixed(6)),
1295
1361
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1296
1362
  is_local: model === "local",
1297
1363
  cq_score: cqScore,
@@ -1311,7 +1377,6 @@ var UsageTracker = class _UsageTracker {
1311
1377
  if (this.buffer.length === 0) return;
1312
1378
  const endpoint = this.config.get("centralReportEndpoint");
1313
1379
  if (!endpoint) {
1314
- console.warn("[IK_TRACKER] No centralReportEndpoint defined. Clearing buffer to prevent memory leak.");
1315
1380
  this.buffer = [];
1316
1381
  return;
1317
1382
  }
@@ -1326,10 +1391,13 @@ var UsageTracker = class _UsageTracker {
1326
1391
  inputTokens: report.input_tokens,
1327
1392
  outputTokens: report.output_tokens,
1328
1393
  optimizedTokens: report.optimized_tokens,
1394
+ tokensSaved: report.tokens_saved,
1395
+ costSaved: report.cost_saved,
1396
+ commissionDue: report.commission_due,
1329
1397
  cqScore: report.cq_score,
1330
1398
  routingPath: report.routingPath,
1331
1399
  clientOrigin: report.clientOrigin,
1332
- trace: report.trace
1400
+ timestamp: report.timestamp
1333
1401
  })
1334
1402
  });
1335
1403
  }
@@ -1437,6 +1505,8 @@ var IKFirewallCore = class _IKFirewallCore {
1437
1505
  this.cloudProvider = new DeepSeekProvider(cloudProvider);
1438
1506
  } else if (mode === "gemini") {
1439
1507
  this.cloudProvider = new GeminiProvider(cloudProvider);
1508
+ } else if (mode === "perplexity") {
1509
+ this.cloudProvider = new PerplexityProvider(cloudProvider);
1440
1510
  } else {
1441
1511
  this.cloudProvider = new OpenAIProvider(cloudProvider);
1442
1512
  }
@@ -1507,6 +1577,10 @@ var IKFirewallCore = class _IKFirewallCore {
1507
1577
  available: !!process.env.GEMINI_API_KEY,
1508
1578
  reason: !!process.env.GEMINI_API_KEY ? void 0 : "Missing GEMINI_API_KEY inside environment."
1509
1579
  };
1580
+ providers["perplexity"] = {
1581
+ available: !!process.env.PERPLEXITY_API_KEY,
1582
+ reason: !!process.env.PERPLEXITY_API_KEY ? void 0 : "Missing PERPLEXITY_API_KEY inside environment."
1583
+ };
1510
1584
  return providers;
1511
1585
  }
1512
1586
  /**
@@ -1551,6 +1625,14 @@ var IKFirewallCore = class _IKFirewallCore {
1551
1625
  async analyze(input, provider, personaName = "professional", locale, instanceId) {
1552
1626
  if (instanceId) {
1553
1627
  this.configManager.ensureInstanceConfig(instanceId);
1628
+ await this.configManager.syncRemoteConfig(instanceId);
1629
+ }
1630
+ const mergedConfig = this.configManager.getMergedConfig(instanceId);
1631
+ if (instanceId && mergedConfig?.isVerified === false) {
1632
+ if (mergedConfig?.billingStatus === "blocked") {
1633
+ this.hooks?.onStatus?.("\u26A0\uFE0F [IK_FIREWALL] Subscription inactive. Optimization bypassed (Fail-Safe mode).");
1634
+ return this.getEmptyMetrics();
1635
+ }
1554
1636
  }
1555
1637
  let auditProvider = this.localProvider;
1556
1638
  let overrideProvider = null;
@@ -1565,6 +1647,8 @@ var IKFirewallCore = class _IKFirewallCore {
1565
1647
  overrideProvider = new DeepSeekProvider(provider);
1566
1648
  } else if (mode === "gemini") {
1567
1649
  overrideProvider = new GeminiProvider(provider);
1650
+ } else if (mode === "perplexity") {
1651
+ overrideProvider = new PerplexityProvider(provider);
1568
1652
  } else {
1569
1653
  overrideProvider = new OpenAIProvider(provider);
1570
1654
  }
@@ -1583,18 +1667,20 @@ var IKFirewallCore = class _IKFirewallCore {
1583
1667
  const ei = (input.match(/[,;.:-]/g) || []).length / (input.split(" ").length || 1);
1584
1668
  let requiresAuditOverride = false;
1585
1669
  let boundaryMetrics = { boundaryScore: 0, requiresHumanIntervention: false };
1586
- if (this.getConfig().safeMode) {
1587
- boundaryMetrics = this.gatekeeper.evaluateBoundaryCommunication(input, this.getConfig().customBoundaryKeywords);
1588
- if (boundaryMetrics.requiresHumanIntervention) {
1670
+ if (this.getConfig()?.safeMode) {
1671
+ boundaryMetrics = this.gatekeeper.evaluateBoundaryCommunication(input, this.getConfig()?.customBoundaryKeywords);
1672
+ if (boundaryMetrics?.requiresHumanIntervention) {
1589
1673
  requiresAuditOverride = true;
1590
1674
  }
1591
1675
  }
1592
1676
  const skipAudit = !requiresAuditOverride && this.getConfig().gatekeeperEnabled && localInsight.complexityScore < _IKFirewallCore.CONSTANTS.HEURISTICS.COMPLEXITY_SKIP_THRESHOLD;
1593
1677
  if (skipAudit) {
1594
1678
  this.hooks?.onStatus?.("\u26A1 IK_GATEKEEPER: Simple request detected. Skipping Deep Audit.");
1595
- const metrics2 = this.createHeuristicMetrics(input, dm, ei, uniqueWords, localInsight.intentMode);
1596
- metrics2.semanticInsights.boundaryScore = boundaryMetrics.boundaryScore;
1597
- metrics2.semanticInsights.requiresHumanIntervention = boundaryMetrics.requiresHumanIntervention;
1679
+ const metrics2 = this.createHeuristicMetrics(input, dm, ei, uniqueWords, localInsight?.intentMode);
1680
+ if (metrics2.semanticInsights) {
1681
+ metrics2.semanticInsights.boundaryScore = boundaryMetrics.boundaryScore;
1682
+ metrics2.semanticInsights.requiresHumanIntervention = boundaryMetrics.requiresHumanIntervention;
1683
+ }
1598
1684
  this.hooks?.onAuditComplete?.(metrics2);
1599
1685
  return metrics2;
1600
1686
  }
@@ -1691,12 +1777,12 @@ var IKFirewallCore = class _IKFirewallCore {
1691
1777
  this.configManager.ensureInstanceConfig(instanceId);
1692
1778
  }
1693
1779
  const activeConfig = this.configManager.getMergedConfig(instanceId, configOverride);
1694
- const locale = activeConfig.locale || "en";
1780
+ const locale = activeConfig?.locale || "en";
1695
1781
  const cqScore = this.orchestrator.calculateCQ(input);
1696
1782
  let isAuditTask = false;
1697
- if (activeConfig.safeMode) {
1698
- const boundaryResult = this.gatekeeper.evaluateBoundaryCommunication(input, activeConfig.customBoundaryKeywords);
1699
- if (boundaryResult.requiresHumanIntervention) {
1783
+ if (activeConfig?.safeMode) {
1784
+ const boundaryResult = this.gatekeeper.evaluateBoundaryCommunication(input, activeConfig?.customBoundaryKeywords);
1785
+ if (boundaryResult?.requiresHumanIntervention) {
1700
1786
  isAuditTask = true;
1701
1787
  }
1702
1788
  }
@@ -1714,6 +1800,8 @@ var IKFirewallCore = class _IKFirewallCore {
1714
1800
  overrideProvider = new DeepSeekProvider(provider);
1715
1801
  } else if (mode === "gemini") {
1716
1802
  overrideProvider = new GeminiProvider(provider);
1803
+ } else if (mode === "perplexity") {
1804
+ overrideProvider = new PerplexityProvider(provider);
1717
1805
  } else {
1718
1806
  overrideProvider = new OpenAIProvider(provider);
1719
1807
  }
@@ -1916,7 +2004,11 @@ Any action outside this scope MUST be rejected and flagged as a boundary violati
1916
2004
  inputLength
1917
2005
  };
1918
2006
  }
1919
- crystallize(input, metrics, locale = "en") {
2007
+ crystallize(input, metrics, locale = "en", instanceId) {
2008
+ const mergedConfig = this.configManager.getMergedConfig(instanceId);
2009
+ if (instanceId && mergedConfig?.isVerified === false) {
2010
+ return input;
2011
+ }
1920
2012
  const archetype = metrics?.semanticInsights?.archetype || "DYNAMIC";
1921
2013
  const tolerance = metrics?.semanticInsights?.ambiguityTolerance || 0.5;
1922
2014
  const blueprint = metrics?.semanticInsights?.conceptualBlueprint;
@@ -2440,6 +2532,7 @@ var IKBenchmarks = {
2440
2532
  IKFirewallCore,
2441
2533
  LocalProvider,
2442
2534
  OpenAIProvider,
2535
+ PerplexityProvider,
2443
2536
  StressTester,
2444
2537
  SurgicalTester
2445
2538
  });
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  type ModelRole = 'high_performance' | 'cost_optimized' | 'audit_layer';
2
- type ProviderMode = 'openai' | 'anthropic' | 'deepseek' | 'gemini' | 'local' | 'hybrid';
2
+ type ProviderMode = 'openai' | 'anthropic' | 'deepseek' | 'gemini' | 'perplexity' | 'local' | 'hybrid';
3
3
  type RuntimeStrategy = 'internal' | 'sidecar' | 'wasm';
4
4
  type ContextMode = 'FIRST_MESSAGE' | 'EVERY_MESSAGE' | 'SYSTEM_ONLY';
5
5
  type PromptLength = 'SHORT' | 'MEDIUM' | 'LONG';
@@ -43,6 +43,15 @@ interface IKConfig {
43
43
  contextMode?: ContextMode;
44
44
  instanceConfigs?: Record<string, Partial<IKConfig>>;
45
45
  isEnabled?: boolean;
46
+ licenseKey?: string;
47
+ billingStatus?: 'active' | 'blocked' | 'trial';
48
+ isVerified?: boolean;
49
+ firstUseDate?: string;
50
+ lastSyncDate?: string;
51
+ pricing?: Record<string, {
52
+ input: number;
53
+ output: number;
54
+ }>;
46
55
  }
47
56
  interface IKPluginInfo {
48
57
  id: string;
@@ -147,6 +156,7 @@ interface UsageData {
147
156
  optimized_tokens?: number;
148
157
  tokens_saved: number;
149
158
  cost_saved: number;
159
+ commission_due: number;
150
160
  timestamp: string;
151
161
  is_local: boolean;
152
162
  cq_score: number;
@@ -224,6 +234,16 @@ declare class GeminiProvider extends BaseProvider {
224
234
  call(prompt: string, role: ModelRole): Promise<AIResponse>;
225
235
  }
226
236
 
237
+ /**
238
+ * PerplexityProvider ensures compatibility with Perplexity API (Sonar online models)
239
+ * while connecting to the core firewall logic.
240
+ */
241
+ declare class PerplexityProvider extends BaseProvider {
242
+ private functionalProvider;
243
+ constructor(provider: AIProvider);
244
+ call(prompt: string, role: ModelRole): Promise<AIResponse>;
245
+ }
246
+
227
247
  /**
228
248
  * IK FIREWALL: THE CORE ALGORITHM (STANDALONE VERSION)
229
249
  * Version: 2.1.0 (Modular & Provider-Agnostic)
@@ -342,7 +362,7 @@ declare class IKFirewallCore {
342
362
  private calculateDNARank;
343
363
  private constructAuditPrompt;
344
364
  private getFallbackMetrics;
345
- crystallize(input: string, metrics?: IKMetrics, locale?: string): string;
365
+ crystallize(input: string, metrics?: IKMetrics, locale?: string, instanceId?: string): string;
346
366
  private miniCrystallize;
347
367
  getAgentDirective(personaName: string, userInsights?: IKMetrics['semanticInsights'], locale?: string): string;
348
368
  private getApproachGuidance;
@@ -680,4 +700,4 @@ declare const IKBenchmarks: {
680
700
  BIG_TEST: TestPrompt[];
681
701
  };
682
702
 
683
- export { type AIProvider, type AIResponse, AnthropicProvider, BaseProvider, type ContextMode, DeepSeekProvider, Dictionaries, DictionaryRegex, GeminiProvider, HeuristicGatekeeper, IKBenchmarks, type IKConfig, IKFirewallCore, type IKHooks, type IKMetrics, type IKPluginInfo, LocalProvider, type ModelRole, OpenAIProvider, type PromptLength, type PromptType, type ProviderMode, type RuntimeStrategy, type StressReport, type StressTestCase, StressTester, SurgicalTester, type TestCase, type TestPrompt, type TestResult, type UsageData };
703
+ export { type AIProvider, type AIResponse, AnthropicProvider, BaseProvider, type ContextMode, DeepSeekProvider, Dictionaries, DictionaryRegex, GeminiProvider, HeuristicGatekeeper, IKBenchmarks, type IKConfig, IKFirewallCore, type IKHooks, type IKMetrics, type IKPluginInfo, LocalProvider, type ModelRole, OpenAIProvider, PerplexityProvider, type PromptLength, type PromptType, type ProviderMode, type RuntimeStrategy, type StressReport, type StressTestCase, StressTester, SurgicalTester, type TestCase, type TestPrompt, type TestResult, type UsageData };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  type ModelRole = 'high_performance' | 'cost_optimized' | 'audit_layer';
2
- type ProviderMode = 'openai' | 'anthropic' | 'deepseek' | 'gemini' | 'local' | 'hybrid';
2
+ type ProviderMode = 'openai' | 'anthropic' | 'deepseek' | 'gemini' | 'perplexity' | 'local' | 'hybrid';
3
3
  type RuntimeStrategy = 'internal' | 'sidecar' | 'wasm';
4
4
  type ContextMode = 'FIRST_MESSAGE' | 'EVERY_MESSAGE' | 'SYSTEM_ONLY';
5
5
  type PromptLength = 'SHORT' | 'MEDIUM' | 'LONG';
@@ -43,6 +43,15 @@ interface IKConfig {
43
43
  contextMode?: ContextMode;
44
44
  instanceConfigs?: Record<string, Partial<IKConfig>>;
45
45
  isEnabled?: boolean;
46
+ licenseKey?: string;
47
+ billingStatus?: 'active' | 'blocked' | 'trial';
48
+ isVerified?: boolean;
49
+ firstUseDate?: string;
50
+ lastSyncDate?: string;
51
+ pricing?: Record<string, {
52
+ input: number;
53
+ output: number;
54
+ }>;
46
55
  }
47
56
  interface IKPluginInfo {
48
57
  id: string;
@@ -147,6 +156,7 @@ interface UsageData {
147
156
  optimized_tokens?: number;
148
157
  tokens_saved: number;
149
158
  cost_saved: number;
159
+ commission_due: number;
150
160
  timestamp: string;
151
161
  is_local: boolean;
152
162
  cq_score: number;
@@ -224,6 +234,16 @@ declare class GeminiProvider extends BaseProvider {
224
234
  call(prompt: string, role: ModelRole): Promise<AIResponse>;
225
235
  }
226
236
 
237
+ /**
238
+ * PerplexityProvider ensures compatibility with Perplexity API (Sonar online models)
239
+ * while connecting to the core firewall logic.
240
+ */
241
+ declare class PerplexityProvider extends BaseProvider {
242
+ private functionalProvider;
243
+ constructor(provider: AIProvider);
244
+ call(prompt: string, role: ModelRole): Promise<AIResponse>;
245
+ }
246
+
227
247
  /**
228
248
  * IK FIREWALL: THE CORE ALGORITHM (STANDALONE VERSION)
229
249
  * Version: 2.1.0 (Modular & Provider-Agnostic)
@@ -342,7 +362,7 @@ declare class IKFirewallCore {
342
362
  private calculateDNARank;
343
363
  private constructAuditPrompt;
344
364
  private getFallbackMetrics;
345
- crystallize(input: string, metrics?: IKMetrics, locale?: string): string;
365
+ crystallize(input: string, metrics?: IKMetrics, locale?: string, instanceId?: string): string;
346
366
  private miniCrystallize;
347
367
  getAgentDirective(personaName: string, userInsights?: IKMetrics['semanticInsights'], locale?: string): string;
348
368
  private getApproachGuidance;
@@ -680,4 +700,4 @@ declare const IKBenchmarks: {
680
700
  BIG_TEST: TestPrompt[];
681
701
  };
682
702
 
683
- export { type AIProvider, type AIResponse, AnthropicProvider, BaseProvider, type ContextMode, DeepSeekProvider, Dictionaries, DictionaryRegex, GeminiProvider, HeuristicGatekeeper, IKBenchmarks, type IKConfig, IKFirewallCore, type IKHooks, type IKMetrics, type IKPluginInfo, LocalProvider, type ModelRole, OpenAIProvider, type PromptLength, type PromptType, type ProviderMode, type RuntimeStrategy, type StressReport, type StressTestCase, StressTester, SurgicalTester, type TestCase, type TestPrompt, type TestResult, type UsageData };
703
+ export { type AIProvider, type AIResponse, AnthropicProvider, BaseProvider, type ContextMode, DeepSeekProvider, Dictionaries, DictionaryRegex, GeminiProvider, HeuristicGatekeeper, IKBenchmarks, type IKConfig, IKFirewallCore, type IKHooks, type IKMetrics, type IKPluginInfo, LocalProvider, type ModelRole, OpenAIProvider, PerplexityProvider, type PromptLength, type PromptType, type ProviderMode, type RuntimeStrategy, type StressReport, type StressTestCase, StressTester, SurgicalTester, type TestCase, type TestPrompt, type TestResult, type UsageData };
package/dist/index.js CHANGED
@@ -129,6 +129,48 @@ var ConfigManager = class {
129
129
  ...instanceConfigs
130
130
  };
131
131
  }
132
+ /**
133
+ * Syncs remote configuration (pricing, license status) from the Vercel API.
134
+ * Runs once every 24 hours (Heartbeat).
135
+ */
136
+ async syncRemoteConfig(instanceId) {
137
+ const now = /* @__PURE__ */ new Date();
138
+ const instanceConfig = this.config.instanceConfigs?.[instanceId];
139
+ if (instanceConfig?.lastSyncDate) {
140
+ const lastSync = new Date(instanceConfig.lastSyncDate);
141
+ const hoursSinceSync = (now.getTime() - lastSync.getTime()) / (1e3 * 60 * 60);
142
+ if (hoursSinceSync < 24) {
143
+ return;
144
+ }
145
+ }
146
+ try {
147
+ const endpoint = this.config.centralReportEndpoint || "https://ik-firewall-backend.vercel.app/api/v1/sync";
148
+ const data = {
149
+ billingStatus: "trial",
150
+ isVerified: true,
151
+ pricing: {
152
+ "gpt-4o": { input: 5e-3, output: 0.015 },
153
+ "gpt-4o-mini": { input: 15e-5, output: 6e-4 }
154
+ },
155
+ latestVersion: "2.2.0"
156
+ };
157
+ this.setInstanceConfig(instanceId, {
158
+ billingStatus: data.billingStatus,
159
+ isVerified: data.isVerified,
160
+ pricing: data.pricing,
161
+ lastSyncDate: now.toISOString()
162
+ });
163
+ if (!instanceConfig?.firstUseDate) {
164
+ this.setInstanceConfig(instanceId, { firstUseDate: now.toISOString() });
165
+ }
166
+ } catch (error) {
167
+ console.error("[IK_CONFIG] Remote sync failed. Falling open (Fail-Open mode).");
168
+ this.setInstanceConfig(instanceId, {
169
+ isVerified: true,
170
+ lastSyncDate: now.toISOString()
171
+ });
172
+ }
173
+ }
132
174
  getConfig() {
133
175
  return { ...this.config };
134
176
  }
@@ -1118,6 +1160,18 @@ var GeminiProvider = class extends BaseProvider {
1118
1160
  }
1119
1161
  };
1120
1162
 
1163
+ // src/providers/PerplexityProvider.ts
1164
+ var PerplexityProvider = class extends BaseProvider {
1165
+ functionalProvider;
1166
+ constructor(provider) {
1167
+ super();
1168
+ this.functionalProvider = provider;
1169
+ }
1170
+ async call(prompt, role) {
1171
+ return this.functionalProvider(prompt, role);
1172
+ }
1173
+ };
1174
+
1121
1175
  // src/Orchestrator.ts
1122
1176
  var Orchestrator = class {
1123
1177
  config;
@@ -1136,6 +1190,7 @@ var Orchestrator = class {
1136
1190
  if (mode === "anthropic") return { provider: "anthropic", model: "claude-3-5-sonnet-20241022", tier: 4 };
1137
1191
  if (mode === "deepseek") return { provider: "deepseek", model: "deepseek-reasoner", tier: 4 };
1138
1192
  if (mode === "gemini") return { provider: "gemini", model: "gemini-1.5-pro", tier: 4 };
1193
+ if (mode === "perplexity") return { provider: "perplexity", model: "sonar-large-online", tier: 4 };
1139
1194
  return { provider: "openai", model: "gpt-4o", tier: 4 };
1140
1195
  }
1141
1196
  if (mode === "local") {
@@ -1162,6 +1217,13 @@ var Orchestrator = class {
1162
1217
  tier: isHighComplexity ? 3 : 2
1163
1218
  };
1164
1219
  }
1220
+ if (mode === "perplexity") {
1221
+ return {
1222
+ provider: "perplexity",
1223
+ model: isHighComplexity ? "sonar-large-online" : "sonar-small-online",
1224
+ tier: isHighComplexity ? 3 : 2
1225
+ };
1226
+ }
1165
1227
  if (mode === "cloud" || mode === "openai") {
1166
1228
  return {
1167
1229
  provider: "openai",
@@ -1228,7 +1290,7 @@ var Orchestrator = class {
1228
1290
  };
1229
1291
 
1230
1292
  // src/UsageTracker.ts
1231
- var UsageTracker = class _UsageTracker {
1293
+ var UsageTracker = class {
1232
1294
  buffer = [];
1233
1295
  MAX_BUFFER_SIZE = 1;
1234
1296
  // Immediate reporting for Edge/Stateless environments
@@ -1250,8 +1312,10 @@ var UsageTracker = class _UsageTracker {
1250
1312
  async logInteraction(params) {
1251
1313
  const { instanceId, model, inputTokens, outputTokens, optimizedTokens = 0, cqScore, routingPath, clientOrigin, trace } = params;
1252
1314
  const tokensSaved = optimizedTokens > 0 ? inputTokens - optimizedTokens : 0;
1253
- const modelPricing = _UsageTracker.PRICING[model] || _UsageTracker.PRICING["gpt-4o-mini"];
1254
- const costSaved = tokensSaved / 1e3 * modelPricing.input;
1315
+ const instanceConfig = this.config.getConfig().instanceConfigs?.[instanceId];
1316
+ const remotePricing = instanceConfig?.pricing?.[model] || { input: 5e-3, output: 0.015 };
1317
+ const costSaved = tokensSaved / 1e3 * remotePricing.input;
1318
+ const commissionDue = costSaved * 0.2;
1255
1319
  const data = {
1256
1320
  instanceId,
1257
1321
  model_used: model,
@@ -1261,6 +1325,7 @@ var UsageTracker = class _UsageTracker {
1261
1325
  optimized_tokens: optimizedTokens,
1262
1326
  tokens_saved: tokensSaved,
1263
1327
  cost_saved: Number(costSaved.toFixed(6)),
1328
+ commission_due: Number(commissionDue.toFixed(6)),
1264
1329
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1265
1330
  is_local: model === "local",
1266
1331
  cq_score: cqScore,
@@ -1280,7 +1345,6 @@ var UsageTracker = class _UsageTracker {
1280
1345
  if (this.buffer.length === 0) return;
1281
1346
  const endpoint = this.config.get("centralReportEndpoint");
1282
1347
  if (!endpoint) {
1283
- console.warn("[IK_TRACKER] No centralReportEndpoint defined. Clearing buffer to prevent memory leak.");
1284
1348
  this.buffer = [];
1285
1349
  return;
1286
1350
  }
@@ -1295,10 +1359,13 @@ var UsageTracker = class _UsageTracker {
1295
1359
  inputTokens: report.input_tokens,
1296
1360
  outputTokens: report.output_tokens,
1297
1361
  optimizedTokens: report.optimized_tokens,
1362
+ tokensSaved: report.tokens_saved,
1363
+ costSaved: report.cost_saved,
1364
+ commissionDue: report.commission_due,
1298
1365
  cqScore: report.cq_score,
1299
1366
  routingPath: report.routingPath,
1300
1367
  clientOrigin: report.clientOrigin,
1301
- trace: report.trace
1368
+ timestamp: report.timestamp
1302
1369
  })
1303
1370
  });
1304
1371
  }
@@ -1406,6 +1473,8 @@ var IKFirewallCore = class _IKFirewallCore {
1406
1473
  this.cloudProvider = new DeepSeekProvider(cloudProvider);
1407
1474
  } else if (mode === "gemini") {
1408
1475
  this.cloudProvider = new GeminiProvider(cloudProvider);
1476
+ } else if (mode === "perplexity") {
1477
+ this.cloudProvider = new PerplexityProvider(cloudProvider);
1409
1478
  } else {
1410
1479
  this.cloudProvider = new OpenAIProvider(cloudProvider);
1411
1480
  }
@@ -1476,6 +1545,10 @@ var IKFirewallCore = class _IKFirewallCore {
1476
1545
  available: !!process.env.GEMINI_API_KEY,
1477
1546
  reason: !!process.env.GEMINI_API_KEY ? void 0 : "Missing GEMINI_API_KEY inside environment."
1478
1547
  };
1548
+ providers["perplexity"] = {
1549
+ available: !!process.env.PERPLEXITY_API_KEY,
1550
+ reason: !!process.env.PERPLEXITY_API_KEY ? void 0 : "Missing PERPLEXITY_API_KEY inside environment."
1551
+ };
1479
1552
  return providers;
1480
1553
  }
1481
1554
  /**
@@ -1520,6 +1593,14 @@ var IKFirewallCore = class _IKFirewallCore {
1520
1593
  async analyze(input, provider, personaName = "professional", locale, instanceId) {
1521
1594
  if (instanceId) {
1522
1595
  this.configManager.ensureInstanceConfig(instanceId);
1596
+ await this.configManager.syncRemoteConfig(instanceId);
1597
+ }
1598
+ const mergedConfig = this.configManager.getMergedConfig(instanceId);
1599
+ if (instanceId && mergedConfig?.isVerified === false) {
1600
+ if (mergedConfig?.billingStatus === "blocked") {
1601
+ this.hooks?.onStatus?.("\u26A0\uFE0F [IK_FIREWALL] Subscription inactive. Optimization bypassed (Fail-Safe mode).");
1602
+ return this.getEmptyMetrics();
1603
+ }
1523
1604
  }
1524
1605
  let auditProvider = this.localProvider;
1525
1606
  let overrideProvider = null;
@@ -1534,6 +1615,8 @@ var IKFirewallCore = class _IKFirewallCore {
1534
1615
  overrideProvider = new DeepSeekProvider(provider);
1535
1616
  } else if (mode === "gemini") {
1536
1617
  overrideProvider = new GeminiProvider(provider);
1618
+ } else if (mode === "perplexity") {
1619
+ overrideProvider = new PerplexityProvider(provider);
1537
1620
  } else {
1538
1621
  overrideProvider = new OpenAIProvider(provider);
1539
1622
  }
@@ -1552,18 +1635,20 @@ var IKFirewallCore = class _IKFirewallCore {
1552
1635
  const ei = (input.match(/[,;.:-]/g) || []).length / (input.split(" ").length || 1);
1553
1636
  let requiresAuditOverride = false;
1554
1637
  let boundaryMetrics = { boundaryScore: 0, requiresHumanIntervention: false };
1555
- if (this.getConfig().safeMode) {
1556
- boundaryMetrics = this.gatekeeper.evaluateBoundaryCommunication(input, this.getConfig().customBoundaryKeywords);
1557
- if (boundaryMetrics.requiresHumanIntervention) {
1638
+ if (this.getConfig()?.safeMode) {
1639
+ boundaryMetrics = this.gatekeeper.evaluateBoundaryCommunication(input, this.getConfig()?.customBoundaryKeywords);
1640
+ if (boundaryMetrics?.requiresHumanIntervention) {
1558
1641
  requiresAuditOverride = true;
1559
1642
  }
1560
1643
  }
1561
1644
  const skipAudit = !requiresAuditOverride && this.getConfig().gatekeeperEnabled && localInsight.complexityScore < _IKFirewallCore.CONSTANTS.HEURISTICS.COMPLEXITY_SKIP_THRESHOLD;
1562
1645
  if (skipAudit) {
1563
1646
  this.hooks?.onStatus?.("\u26A1 IK_GATEKEEPER: Simple request detected. Skipping Deep Audit.");
1564
- const metrics2 = this.createHeuristicMetrics(input, dm, ei, uniqueWords, localInsight.intentMode);
1565
- metrics2.semanticInsights.boundaryScore = boundaryMetrics.boundaryScore;
1566
- metrics2.semanticInsights.requiresHumanIntervention = boundaryMetrics.requiresHumanIntervention;
1647
+ const metrics2 = this.createHeuristicMetrics(input, dm, ei, uniqueWords, localInsight?.intentMode);
1648
+ if (metrics2.semanticInsights) {
1649
+ metrics2.semanticInsights.boundaryScore = boundaryMetrics.boundaryScore;
1650
+ metrics2.semanticInsights.requiresHumanIntervention = boundaryMetrics.requiresHumanIntervention;
1651
+ }
1567
1652
  this.hooks?.onAuditComplete?.(metrics2);
1568
1653
  return metrics2;
1569
1654
  }
@@ -1660,12 +1745,12 @@ var IKFirewallCore = class _IKFirewallCore {
1660
1745
  this.configManager.ensureInstanceConfig(instanceId);
1661
1746
  }
1662
1747
  const activeConfig = this.configManager.getMergedConfig(instanceId, configOverride);
1663
- const locale = activeConfig.locale || "en";
1748
+ const locale = activeConfig?.locale || "en";
1664
1749
  const cqScore = this.orchestrator.calculateCQ(input);
1665
1750
  let isAuditTask = false;
1666
- if (activeConfig.safeMode) {
1667
- const boundaryResult = this.gatekeeper.evaluateBoundaryCommunication(input, activeConfig.customBoundaryKeywords);
1668
- if (boundaryResult.requiresHumanIntervention) {
1751
+ if (activeConfig?.safeMode) {
1752
+ const boundaryResult = this.gatekeeper.evaluateBoundaryCommunication(input, activeConfig?.customBoundaryKeywords);
1753
+ if (boundaryResult?.requiresHumanIntervention) {
1669
1754
  isAuditTask = true;
1670
1755
  }
1671
1756
  }
@@ -1683,6 +1768,8 @@ var IKFirewallCore = class _IKFirewallCore {
1683
1768
  overrideProvider = new DeepSeekProvider(provider);
1684
1769
  } else if (mode === "gemini") {
1685
1770
  overrideProvider = new GeminiProvider(provider);
1771
+ } else if (mode === "perplexity") {
1772
+ overrideProvider = new PerplexityProvider(provider);
1686
1773
  } else {
1687
1774
  overrideProvider = new OpenAIProvider(provider);
1688
1775
  }
@@ -1885,7 +1972,11 @@ Any action outside this scope MUST be rejected and flagged as a boundary violati
1885
1972
  inputLength
1886
1973
  };
1887
1974
  }
1888
- crystallize(input, metrics, locale = "en") {
1975
+ crystallize(input, metrics, locale = "en", instanceId) {
1976
+ const mergedConfig = this.configManager.getMergedConfig(instanceId);
1977
+ if (instanceId && mergedConfig?.isVerified === false) {
1978
+ return input;
1979
+ }
1889
1980
  const archetype = metrics?.semanticInsights?.archetype || "DYNAMIC";
1890
1981
  const tolerance = metrics?.semanticInsights?.ambiguityTolerance || 0.5;
1891
1982
  const blueprint = metrics?.semanticInsights?.conceptualBlueprint;
@@ -2408,6 +2499,7 @@ export {
2408
2499
  IKFirewallCore,
2409
2500
  LocalProvider,
2410
2501
  OpenAIProvider,
2502
+ PerplexityProvider,
2411
2503
  StressTester,
2412
2504
  SurgicalTester
2413
2505
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ik-firewall/core",
3
- "version": "1.0.1",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
5
  "description": "The core IK Firewall engine for semantic-driven AI optimization.",
6
6
  "main": "./dist/index.js",
@@ -8,18 +8,20 @@
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
+ "types": "./dist/index.d.ts",
11
12
  "import": "./dist/index.js",
12
- "require": "./dist/index.cjs",
13
- "types": "./dist/index.d.ts"
13
+ "require": "./dist/index.cjs"
14
14
  }
15
15
  },
16
16
  "files": [
17
17
  "dist",
18
18
  "scripts"
19
19
  ],
20
+ "api_endpoint": "https://ik-firewall-backend.vercel.app/api/v1",
20
21
  "scripts": {
21
22
  "prepare": "npm run build",
22
23
  "build": "tsup src/index.ts --format cjs,esm --dts --clean",
24
+ "build:wasm": "cd wasm-core && wasm-pack build --target nodejs",
23
25
  "dev": "tsup src/index.ts --format cjs,esm --watch --dts",
24
26
  "lint": "eslint src/**/*.ts",
25
27
  "test": "jest",
@@ -35,7 +37,7 @@
35
37
  "optimization"
36
38
  ],
37
39
  "author": "Stefan Vasic",
38
- "license": "MIT",
40
+ "license": "BSL-1.1",
39
41
  "devDependencies": {
40
42
  "@types/node": "^25.0.10",
41
43
  "tsup": "^8.0.0",
@@ -1,44 +1,83 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
+ import readline from 'node:readline';
4
5
 
5
6
  const __filename = fileURLToPath(import.meta.url);
6
7
  const __dirname = path.dirname(__filename);
7
8
 
8
9
  // Constants
9
10
  const BIN_DIR = path.join(__dirname, '..', '.bin');
10
- const RUNTIME_NAME = process.platform === 'win32' ? 'ik-runtime.exe' : 'ik-runtime';
11
- const MODEL_NAME = 'ik-local-1b-v1.gguf';
11
+ const REGISTRY_DIR = path.join(process.cwd(), '.ik-adapter');
12
+ const REGISTRY_FILE = path.join(REGISTRY_DIR, 'registry.json');
13
+
14
+ async function askEmail() {
15
+ if (process.env.IK_EMAIL) return process.env.IK_EMAIL;
16
+
17
+ const rl = readline.createInterface({
18
+ input: process.stdin,
19
+ output: process.stdout
20
+ });
21
+
22
+ const prompt = (query) => new Promise((resolve) => rl.question(query, resolve));
23
+
24
+ let email = "";
25
+ while (!email || !email.includes('@')) {
26
+ email = await prompt('\nšŸ“§ IK_FIREWALL: [MANDATORY] Please enter your email to activate your account: ');
27
+ if (!email || !email.includes('@')) {
28
+ console.log('āš ļø Invalid email. Email is required for license generation.');
29
+ }
30
+ }
31
+ rl.close();
32
+ return email;
33
+ }
12
34
 
13
35
  async function setup() {
14
- if (process.env.CI || process.env.VERCEL) {
15
- console.log('ā­ļø IK_FIREWALL: CI/Vercel environment detected. Skipping local runtime setup.');
36
+ // Vercel / Netlify / AWS Lambda often have read-only file systems
37
+ if (process.env.VERCEL || process.env.RENDER || process.env.AWS_LAMBDA_FUNCTION_NAME) {
38
+ console.log('\nā­ļø IK_FIREWALL: Read-only Serverless environment detected.');
39
+ console.log('šŸ’” NOTE: Registry & Local Binary setup will be bypassed. Ensure you set "centralReportEndpoint" via code config.');
16
40
  return;
17
41
  }
18
- console.log('šŸš€ IK_FIREWALL: Initiating Zero-Config Runtime Setup...');
42
+ console.log('\nšŸš€ IK_FIREWALL: Initiating Zero-Config Runtime Setup...');
19
43
 
20
- // 1. Create .bin directory
21
- if (!fs.existsSync(BIN_DIR)) {
22
- fs.mkdirSync(BIN_DIR, { recursive: true });
23
- console.log(`āœ… Created directory: ${BIN_DIR}`);
44
+ // 1. Create .bin and .ik-adapter directories
45
+ [BIN_DIR, REGISTRY_DIR].forEach(dir => {
46
+ if (!fs.existsSync(dir)) {
47
+ fs.mkdirSync(dir, { recursive: true });
48
+ }
49
+ });
50
+
51
+ // 2. Initialize Trial Registry
52
+ if (!fs.existsSync(REGISTRY_FILE)) {
53
+ const email = await askEmail();
54
+ const initialRegistry = {
55
+ "default": {
56
+ "licenseKey": "TRIAL-" + Math.random().toString(36).substring(7).toUpperCase(),
57
+ "billingStatus": "trial",
58
+ "isVerified": true,
59
+ "firstUseDate": new Date().toISOString(),
60
+ "ownerEmail": email,
61
+ "isEnabled": true
62
+ }
63
+ };
64
+ fs.writeFileSync(REGISTRY_FILE, JSON.stringify(initialRegistry, null, 2));
65
+ console.log(`✨ Trial activated for: ${email}`);
24
66
  }
25
67
 
26
- // 2. Detect OS & Architecture
68
+ // 3. Detect OS & Architecture
27
69
  const platform = process.platform;
28
70
  const arch = process.arch;
29
71
  console.log(`šŸ” Detected environment: ${platform}-${arch}`);
30
72
 
31
- // 3. Inform user about the Emulator (since binary is OS-dependent)
32
- const emulatorPath = path.join(__dirname, 'runtime-emulator.js');
33
- console.log(`\nšŸ’” TIP: For this prototype on Windows, use the Node.js Emulator instead of the .exe:`);
34
- console.log(` node scripts/runtime-emulator.js`);
35
-
36
- // Create a helper dev script in package.json context or similar
37
- console.log('\nšŸ“¦ IK-Runtime environment prepared.');
38
- console.log('✨ IK Adapter is now ready for local escalation testing!');
73
+ // 4. Usage Tips
74
+ console.log(`\nšŸ“¦ IK-Runtime environment prepared.`);
75
+ console.log('šŸ’” TIP: For local optimization (Sidecar), use "npm run start-ai".');
76
+ console.log('šŸ› ļø DEV: If building locally, you can use the Node.js Emulator: node scripts/runtime-emulator.js');
77
+ console.log('✨ IK Adapter is now ready for production-grade AI protection!\n');
39
78
  }
40
79
 
41
80
  setup().catch(err => {
42
81
  console.error('āŒ IK_FIREWALL Setup Failed:', err.message);
43
- process.exit(1);
82
+ process.exit(0); // Don't block install on setup error
44
83
  });