@syke1/mcp-server 1.4.2 → 1.4.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/dist/index.js CHANGED
@@ -75,12 +75,15 @@ function resolveFilePath(fileArg, projectRoot, sourceDir) {
75
75
  let licenseStatus = { plan: "free", source: "default" };
76
76
  // Free tier limits
77
77
  const FREE_MAX_FILES = 50;
78
+ function isPro() {
79
+ return isPro() || licenseStatus.plan === "pro_trial";
80
+ }
78
81
  /**
79
82
  * Check if a resolved file path is within the free tier limit.
80
83
  * Free set = first 50 files sorted alphabetically by relative path.
81
84
  */
82
85
  function isFileInFreeSet(resolvedPath, graph) {
83
- if (licenseStatus.plan === "pro")
86
+ if (isPro())
84
87
  return true;
85
88
  const allFiles = [...graph.files].sort();
86
89
  const idx = allFiles.indexOf(resolvedPath);
@@ -357,7 +360,7 @@ async function main() {
357
360
  }
358
361
  case "get_hub_files": {
359
362
  // Pro-only feature
360
- if (licenseStatus.plan !== "pro") {
363
+ if (!isPro()) {
361
364
  return {
362
365
  content: [{ type: "text", text: getProToolError("get_hub_files") }],
363
366
  };
@@ -392,7 +395,7 @@ async function main() {
392
395
  case "ai_analyze": {
393
396
  // BYOK: allow if user has their own AI key, even on Free plan
394
397
  const hasAIKey = !!(0, provider_1.getAIProvider)();
395
- if (licenseStatus.plan !== "pro" && !hasAIKey) {
398
+ if (!isPro() && !hasAIKey) {
396
399
  return {
397
400
  content: [{ type: "text", text: getProToolError("ai_analyze") + "\n\nOr set GEMINI_KEY / OPENAI_KEY / ANTHROPIC_KEY to use ai_analyze with your own API key." }],
398
401
  };
@@ -417,7 +420,7 @@ async function main() {
417
420
  const aiResult = await (0, analyzer_1.analyzeWithAI)(resolved, impactResult, graph);
418
421
  // Free tier: append partial analysis warning
419
422
  let resultText = aiResult;
420
- if (licenseStatus.plan !== "pro" && graph.files.size > FREE_MAX_FILES) {
423
+ if (!isPro() && graph.files.size > FREE_MAX_FILES) {
421
424
  resultText += `\n\n---\n⚠️ Free tier: analyzing ${FREE_MAX_FILES}/${graph.files.size} files. Some dependencies may be missing. Upgrade to Pro for full analysis: https://syke.cloud/dashboard/`;
422
425
  }
423
426
  return {
@@ -426,7 +429,7 @@ async function main() {
426
429
  }
427
430
  case "check_warnings": {
428
431
  // Pro-only feature (real-time monitoring)
429
- if (licenseStatus.plan !== "pro") {
432
+ if (!isPro()) {
430
433
  return {
431
434
  content: [{ type: "text", text: getProToolError("check_warnings") }],
432
435
  };
@@ -498,7 +501,7 @@ async function main() {
498
501
  else {
499
502
  console.error(`[syke] AI: disabled (set GEMINI_KEY, OPENAI_KEY, or ANTHROPIC_KEY)`);
500
503
  }
501
- if (licenseStatus.plan === "pro") {
504
+ if (isPro()) {
502
505
  console.error(`[syke] Pro activated for: ${licenseStatus.email || "unknown"}`);
503
506
  }
504
507
  else {
@@ -570,7 +573,7 @@ async function main() {
570
573
  catch {
571
574
  licenseStatus = { plan: "free", source: "default" };
572
575
  }
573
- if (licenseStatus.plan === "pro") {
576
+ if (isPro()) {
574
577
  return { success: true, plan: licenseStatus.plan, expiresAt: licenseStatus.expiresAt };
575
578
  }
576
579
  else {
@@ -1,5 +1,5 @@
1
1
  export interface LicenseStatus {
2
- plan: "free" | "pro";
2
+ plan: "free" | "pro" | "pro_trial";
3
3
  email?: string;
4
4
  expiresAt?: string;
5
5
  source: "online" | "cache" | "default";
@@ -170,6 +170,7 @@ async function sendHeartbeat() {
170
170
  await postJSON(HEARTBEAT_URL, {
171
171
  key: currentLicenseKey,
172
172
  deviceId: currentDeviceId,
173
+ deviceName: getDeviceName(),
173
174
  });
174
175
  }
175
176
  catch {
@@ -248,8 +249,9 @@ async function checkLicense() {
248
249
  if (cache && cache.valid && (now - cache.cachedAt) < CACHE_MAX_AGE) {
249
250
  // Cache is fresh — still start heartbeat with cached session
250
251
  startHeartbeat(key, getDeviceFingerprint());
252
+ const cachedPlan = cache.plan === "pro_trial" ? "pro_trial" : "pro";
251
253
  return {
252
- plan: "pro",
254
+ plan: cachedPlan,
253
255
  email: cache.email,
254
256
  expiresAt: cache.expiresAt,
255
257
  source: "cache",
@@ -272,8 +274,9 @@ async function checkLicense() {
272
274
  });
273
275
  // Start heartbeat
274
276
  startHeartbeat(key, getDeviceFingerprint());
277
+ const onlinePlan = result.plan === "pro_trial" ? "pro_trial" : "pro";
275
278
  return {
276
- plan: "pro",
279
+ plan: onlinePlan,
277
280
  email: result.email,
278
281
  expiresAt: result.expiresAt,
279
282
  source: "online",
@@ -290,8 +293,9 @@ async function checkLicense() {
290
293
  // Online validation failed — check if we have a grace-period cache
291
294
  if (cache && cache.valid && (now - cache.cachedAt) < CACHE_GRACE_PERIOD) {
292
295
  startHeartbeat(key, getDeviceFingerprint());
296
+ const gracePlan = cache.plan === "pro_trial" ? "pro_trial" : "pro";
293
297
  return {
294
- plan: "pro",
298
+ plan: gracePlan,
295
299
  email: cache.email,
296
300
  expiresAt: cache.expiresAt,
297
301
  source: "cache",
@@ -3595,10 +3595,13 @@ function updateLicenseBadge(plan, expiresAt) {
3595
3595
 
3596
3596
  badge.className = "license-badge";
3597
3597
 
3598
- if (plan === "pro") {
3598
+ if (plan === "pro" || plan === "pro_trial") {
3599
3599
  if (expiresAt && new Date(expiresAt) < new Date()) {
3600
3600
  badge.classList.add("expired");
3601
3601
  badge.textContent = "EXPIRED";
3602
+ } else if (plan === "pro_trial") {
3603
+ badge.classList.add("pro");
3604
+ badge.textContent = "TRIAL-PRO";
3602
3605
  } else {
3603
3606
  badge.classList.add("pro");
3604
3607
  badge.textContent = "PRO";
@@ -3817,11 +3820,27 @@ function setupLicenseModal() {
3817
3820
  const statusEl = document.getElementById("license-modal-status");
3818
3821
  if (!btn || !modal) return;
3819
3822
 
3820
- function openModal() {
3823
+ async function openModal() {
3821
3824
  modal.classList.remove("hidden");
3822
3825
  input.value = "";
3823
3826
  statusEl.textContent = "";
3824
3827
  statusEl.className = "";
3828
+ // Fetch current key status
3829
+ try {
3830
+ const res = await fetch("/api/project-info");
3831
+ const info = await res.json();
3832
+ if (info.licenseKey) {
3833
+ input.placeholder = info.licenseKey;
3834
+ statusEl.className = "success";
3835
+ statusEl.textContent = "ACTIVE — " + (info.plan || "free").toUpperCase();
3836
+ deactivateBtn.style.display = "";
3837
+ } else {
3838
+ input.placeholder = "SYKE-XXXX-XXXX-XXXX-XXXX";
3839
+ deactivateBtn.style.display = "none";
3840
+ }
3841
+ } catch {
3842
+ input.placeholder = "SYKE-XXXX-XXXX-XXXX-XXXX";
3843
+ }
3825
3844
  input.focus();
3826
3845
  }
3827
3846
  function closeModal() {
@@ -3850,11 +3869,11 @@ function setupLicenseModal() {
3850
3869
  body: JSON.stringify({ key }),
3851
3870
  });
3852
3871
  const data = await res.json();
3853
- if (data.success && data.plan === "pro") {
3872
+ if (data.success && (data.plan === "pro" || data.plan === "pro_trial")) {
3854
3873
  statusEl.className = "success";
3855
- statusEl.textContent = "PRO ACTIVATED";
3856
- updateLicenseBadge("pro", data.expiresAt);
3857
- updateLicenseButton("pro");
3874
+ statusEl.textContent = data.plan === "pro_trial" ? "TRIAL-PRO ACTIVATED" : "PRO ACTIVATED";
3875
+ updateLicenseBadge(data.plan, data.expiresAt);
3876
+ updateLicenseButton(data.plan);
3858
3877
  setTimeout(closeModal, 1200);
3859
3878
  } else {
3860
3879
  statusEl.className = "error";
@@ -3901,7 +3920,7 @@ function setupLicenseModal() {
3901
3920
  function updateLicenseButton(plan) {
3902
3921
  const btn = document.getElementById("btn-license");
3903
3922
  if (!btn) return;
3904
- btn.textContent = plan === "pro" ? "LICENSED" : "LICENSE";
3923
+ btn.textContent = (plan === "pro" || plan === "pro_trial") ? "LICENSED" : "LICENSE";
3905
3924
  }
3906
3925
 
3907
3926
  function setupProjectModal() {
@@ -48,6 +48,7 @@ const plugin_1 = require("../languages/plugin");
48
48
  const analyze_impact_1 = require("../tools/analyze-impact");
49
49
  const analyzer_1 = require("../ai/analyzer");
50
50
  const realtime_analyzer_1 = require("../ai/realtime-analyzer");
51
+ const config_1 = require("../config");
51
52
  function resolveFilePath(fileArg, projectRoot, sourceDir) {
52
53
  const srcDir = sourceDir || path.join(projectRoot, "lib");
53
54
  const srcDirName = path.basename(srcDir); // "lib" or "src"
@@ -739,6 +740,11 @@ function createWebServer(getGraphFn, initialFileCache, switchProjectFn, getProje
739
740
  sykeVersion = pkg.version || "unknown";
740
741
  }
741
742
  catch { }
743
+ // Read current license key (masked for display)
744
+ const rawKey = (0, config_1.getConfig)("licenseKey", "SYKE_LICENSE_KEY") || "";
745
+ const maskedKey = rawKey.length > 10
746
+ ? rawKey.substring(0, 9) + "····" + rawKey.substring(rawKey.length - 4)
747
+ : "";
742
748
  res.json({
743
749
  projectRoot: getProjectRoot ? getProjectRoot() : graph.projectRoot,
744
750
  packageName: getPackageName ? getPackageName() : "",
@@ -748,6 +754,7 @@ function createWebServer(getGraphFn, initialFileCache, switchProjectFn, getProje
748
754
  plan: license?.plan || "free",
749
755
  planSource: license?.source || "default",
750
756
  expiresAt: license?.expiresAt || null,
757
+ licenseKey: maskedKey,
751
758
  freeFileLimit: 50,
752
759
  sykeVersion,
753
760
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syke1/mcp-server",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "mcpName": "io.github.khalomsky/syke",
5
5
  "description": "AI code impact analysis MCP server — dependency graphs, cascade detection, and a mandatory build gate for AI coding agents",
6
6
  "main": "dist/index.js",