@jlcpcb/mcp 0.1.1 → 0.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.js CHANGED
@@ -21903,6 +21903,10 @@ import { existsSync as existsSync3 } from "fs";
21903
21903
  import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
21904
21904
  import { homedir as homedir2, platform as platform2 } from "os";
21905
21905
  import { join as join4 } from "path";
21906
+ import { createServer } from "http";
21907
+ import { readFileSync } from "fs";
21908
+ import { join as join5, dirname as dirname3 } from "path";
21909
+ import { fileURLToPath } from "url";
21906
21910
  var __defProp2 = Object.defineProperty;
21907
21911
  var __export2 = (target, all) => {
21908
21912
  for (var name in all)
@@ -22513,7 +22517,8 @@ var DEFAULT_LIBRARY_DESCRIPTION = "JLC-MCP Component Library (LCSC/EasyEDA)";
22513
22517
  var init_lib_table = () => {};
22514
22518
  var exports_global_lib_table = {};
22515
22519
  __export2(exports_global_lib_table, {
22516
- ensureGlobalLibraryTables: () => ensureGlobalLibraryTables
22520
+ ensureGlobalLibraryTables: () => ensureGlobalLibraryTables,
22521
+ ensureGlobalEasyEDALibrary: () => ensureGlobalEasyEDALibrary
22517
22522
  });
22518
22523
  function detectKicadVersion() {
22519
22524
  const home = homedir();
@@ -22690,6 +22695,51 @@ async function ensureLibraryStubs(version2) {
22690
22695
  }
22691
22696
  return { symbolsCreated, directoriesCreated };
22692
22697
  }
22698
+ function getEasyEDASymbolLibUri() {
22699
+ return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/symbols/${EASYEDA_SYMBOL_LIBRARY_NAME}`;
22700
+ }
22701
+ function getEasyEDAFootprintLibUri() {
22702
+ return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/footprints/${EASYEDA_FOOTPRINT_LIBRARY_NAME}`;
22703
+ }
22704
+ async function ensureGlobalEasyEDALibrary() {
22705
+ const version2 = detectKicadVersion();
22706
+ const configDir = getKicadConfigDir(version2);
22707
+ await mkdir2(configDir, { recursive: true });
22708
+ const symTablePath = join3(configDir, "sym-lib-table");
22709
+ if (existsSync2(symTablePath)) {
22710
+ let content = await readFile3(symTablePath, "utf-8");
22711
+ if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
22712
+ const uri = getEasyEDASymbolLibUri();
22713
+ content = addLibraryToTable(content, EASYEDA_LIBRARY_NAME, uri, "sym", EASYEDA_LIBRARY_DESCRIPTION);
22714
+ await writeFile3(symTablePath, content, "utf-8");
22715
+ }
22716
+ } else {
22717
+ const uri = getEasyEDASymbolLibUri();
22718
+ const content = `(sym_lib_table
22719
+ (version 7)
22720
+ (lib (name "${EASYEDA_LIBRARY_NAME}")(type "KiCad")(uri "${uri}")(options "")(descr "${EASYEDA_LIBRARY_DESCRIPTION}"))
22721
+ )
22722
+ `;
22723
+ await writeFile3(symTablePath, content, "utf-8");
22724
+ }
22725
+ const fpTablePath = join3(configDir, "fp-lib-table");
22726
+ if (existsSync2(fpTablePath)) {
22727
+ let content = await readFile3(fpTablePath, "utf-8");
22728
+ if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
22729
+ const uri = getEasyEDAFootprintLibUri();
22730
+ content = addLibraryToTable(content, EASYEDA_LIBRARY_NAME, uri, "fp", EASYEDA_LIBRARY_DESCRIPTION);
22731
+ await writeFile3(fpTablePath, content, "utf-8");
22732
+ }
22733
+ } else {
22734
+ const uri = getEasyEDAFootprintLibUri();
22735
+ const content = `(fp_lib_table
22736
+ (version 7)
22737
+ (lib (name "${EASYEDA_LIBRARY_NAME}")(type "KiCad")(uri "${uri}")(options "")(descr "${EASYEDA_LIBRARY_DESCRIPTION}"))
22738
+ )
22739
+ `;
22740
+ await writeFile3(fpTablePath, content, "utf-8");
22741
+ }
22742
+ }
22693
22743
  async function ensureGlobalLibraryTables() {
22694
22744
  const errors22 = [];
22695
22745
  const version2 = detectKicadVersion();
@@ -22742,6 +22792,10 @@ var LIBRARY_PREFIX2 = "JLC-MCP";
22742
22792
  var LIBRARY_NAMESPACE = "jlc_mcp";
22743
22793
  var KICAD_3RD_PARTY_VAR = "${KICAD9_3RD_PARTY}";
22744
22794
  var LIBRARY_DESCRIPTION = "Autogenerated by JLC-MCP";
22795
+ var EASYEDA_LIBRARY_NAME = "EasyEDA";
22796
+ var EASYEDA_SYMBOL_LIBRARY_NAME = "EasyEDA.kicad_sym";
22797
+ var EASYEDA_FOOTPRINT_LIBRARY_NAME = "EasyEDA.pretty";
22798
+ var EASYEDA_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library";
22745
22799
  var init_global_lib_table = __esm(() => {
22746
22800
  init_category_router();
22747
22801
  init_lib_table();
@@ -27478,7 +27532,7 @@ function parseFootprintShapes(shapes) {
27478
27532
  }
27479
27533
  }
27480
27534
  }
27481
- const type = pads.some((p) => p.holeRadius > 0) ? "tht" : "smd";
27535
+ const type = pads.some((p) => p.holeRadius > 0 || p.shape === "POLYGON" && p.isPlated) ? "tht" : "smd";
27482
27536
  return {
27483
27537
  name: "Unknown",
27484
27538
  type,
@@ -29542,8 +29596,8 @@ var KI_PAD_LAYER_SMD = {
29542
29596
  11: '"*.Cu" "*.Paste" "*.Mask"'
29543
29597
  };
29544
29598
  var KI_PAD_LAYER_THT = {
29545
- 1: '"F.Cu" "F.Mask"',
29546
- 2: '"B.Cu" "B.Mask"',
29599
+ 1: '"*.Cu" "*.Mask"',
29600
+ 2: '"*.Cu" "*.Mask"',
29547
29601
  11: '"*.Cu" "*.Mask"'
29548
29602
  };
29549
29603
  var KI_PAD_SHAPE = {
@@ -29753,15 +29807,20 @@ class FootprintConverter {
29753
29807
  `;
29754
29808
  return output;
29755
29809
  }
29756
- generatePolygonPad(pad, origin, layers) {
29810
+ generatePolygonPad(pad, origin, _layers) {
29757
29811
  const x = convertX(pad.centerX, origin.x);
29758
29812
  const y = convertY(pad.centerY, origin.y);
29759
29813
  const points = parsePoints(pad.points);
29760
29814
  if (points.length < 3) {
29761
29815
  return this.generatePad({ ...pad, shape: "RECT", points: "" }, origin);
29762
29816
  }
29763
- const isSmd = pad.holeRadius === 0;
29817
+ let holeRadius = pad.holeRadius;
29818
+ if (holeRadius === 0 && pad.isPlated && pad.shape === "POLYGON") {
29819
+ holeRadius = this.calculateDrillRadiusFromPolygon(points, pad.centerX, pad.centerY);
29820
+ }
29821
+ const isSmd = holeRadius === 0;
29764
29822
  const padType = isSmd ? "smd" : "thru_hole";
29823
+ const layers = getPadLayers(pad.layerId, isSmd);
29765
29824
  const polyPoints = points.map((p) => ({
29766
29825
  x: roundTo(toMM(p.x - pad.centerX), 2),
29767
29826
  y: roundTo(toMM(p.y - pad.centerY), 2)
@@ -29773,7 +29832,7 @@ class FootprintConverter {
29773
29832
  output += ` (size 0.01 0.01)
29774
29833
  `;
29775
29834
  if (!isSmd) {
29776
- const drillDiameter = roundTo(toMM(pad.holeRadius * 2), 4);
29835
+ const drillDiameter = roundTo(toMM(holeRadius * 2), 4);
29777
29836
  if (pad.holeLength && pad.holeLength > 0) {
29778
29837
  const holeH = roundTo(toMM(pad.holeLength), 4);
29779
29838
  output += ` (drill oval ${drillDiameter} ${holeH})
@@ -29807,6 +29866,25 @@ class FootprintConverter {
29807
29866
  `;
29808
29867
  return output;
29809
29868
  }
29869
+ calculateDrillRadiusFromPolygon(points, centerX, centerY) {
29870
+ if (points.length < 3)
29871
+ return 0;
29872
+ let minX = Infinity, maxX = -Infinity;
29873
+ let minY = Infinity, maxY = -Infinity;
29874
+ for (const pt of points) {
29875
+ const relX = pt.x - centerX;
29876
+ const relY = pt.y - centerY;
29877
+ minX = Math.min(minX, relX);
29878
+ maxX = Math.max(maxX, relX);
29879
+ minY = Math.min(minY, relY);
29880
+ maxY = Math.max(maxY, relY);
29881
+ }
29882
+ const width = maxX - minX;
29883
+ const height = maxY - minY;
29884
+ const minDim = Math.min(width, height);
29885
+ const drillDiameter = minDim * 0.6;
29886
+ return drillDiameter / 2;
29887
+ }
29810
29888
  generateHole(hole, origin) {
29811
29889
  const x = convertX(hole.centerX, origin.x);
29812
29890
  const y = convertY(hole.centerY, origin.y);
@@ -29933,9 +30011,9 @@ class FootprintConverter {
29933
30011
  const fontSize = roundTo(toMM(text.fontSize), 2);
29934
30012
  const rotation = text.rotation || 0;
29935
30013
  let justify = "";
29936
- if (x < -0.5) {
30014
+ if (text.type === "L") {
29937
30015
  justify = "left";
29938
- } else if (x > 0.5) {
30016
+ } else if (text.type === "R") {
29939
30017
  justify = "right";
29940
30018
  }
29941
30019
  return ` (fp_text user "${this.escapeString(text.text)}"
@@ -29952,6 +30030,9 @@ ${justify ? ` (justify ${justify})
29952
30030
  `;
29953
30031
  }
29954
30032
  generateSolidRegion(region, origin) {
30033
+ if (region.layerId === 99 || region.layerId === 100 || region.layerId === 101) {
30034
+ return "";
30035
+ }
29955
30036
  const layer = getLayer(region.layerId);
29956
30037
  const points = this.parseSvgPathToPoints(region.path, origin);
29957
30038
  if (points.length < 3)
@@ -29984,15 +30065,111 @@ ${justify ? ` (justify ${justify})
29984
30065
  }
29985
30066
  parseSvgPathToPoints(path, origin) {
29986
30067
  const points = [];
29987
- const commandRegex = /([ML])\s*([\d.-]+)[,\s]+([\d.-]+)/gi;
30068
+ let currentX = 0, currentY = 0;
30069
+ let startX = 0, startY = 0;
30070
+ const commandRegex = /([MLHVCSQTAZ])\s*([^MLHVCSQTAZ]*)/gi;
29988
30071
  let match;
29989
30072
  while ((match = commandRegex.exec(path)) !== null) {
29990
- const x = parseFloat(match[2]);
29991
- const y = parseFloat(match[3]);
29992
- points.push({
29993
- x: convertX(x, origin.x),
29994
- y: convertY(y, origin.y)
29995
- });
30073
+ const cmd = match[1].toUpperCase();
30074
+ const args = match[2].trim().split(/[\s,]+/).map(parseFloat).filter((n) => !isNaN(n));
30075
+ switch (cmd) {
30076
+ case "M":
30077
+ if (args.length >= 2) {
30078
+ currentX = args[0];
30079
+ currentY = args[1];
30080
+ if (points.length === 0) {
30081
+ startX = currentX;
30082
+ startY = currentY;
30083
+ }
30084
+ points.push({
30085
+ x: convertX(currentX, origin.x),
30086
+ y: convertY(currentY, origin.y)
30087
+ });
30088
+ }
30089
+ break;
30090
+ case "L":
30091
+ if (args.length >= 2) {
30092
+ currentX = args[0];
30093
+ currentY = args[1];
30094
+ points.push({
30095
+ x: convertX(currentX, origin.x),
30096
+ y: convertY(currentY, origin.y)
30097
+ });
30098
+ }
30099
+ break;
30100
+ case "H":
30101
+ if (args.length >= 1) {
30102
+ currentX = args[0];
30103
+ points.push({
30104
+ x: convertX(currentX, origin.x),
30105
+ y: convertY(currentY, origin.y)
30106
+ });
30107
+ }
30108
+ break;
30109
+ case "V":
30110
+ if (args.length >= 1) {
30111
+ currentY = args[0];
30112
+ points.push({
30113
+ x: convertX(currentX, origin.x),
30114
+ y: convertY(currentY, origin.y)
30115
+ });
30116
+ }
30117
+ break;
30118
+ case "C":
30119
+ if (args.length >= 6) {
30120
+ currentX = args[4];
30121
+ currentY = args[5];
30122
+ points.push({
30123
+ x: convertX(currentX, origin.x),
30124
+ y: convertY(currentY, origin.y)
30125
+ });
30126
+ }
30127
+ break;
30128
+ case "S":
30129
+ if (args.length >= 4) {
30130
+ currentX = args[2];
30131
+ currentY = args[3];
30132
+ points.push({
30133
+ x: convertX(currentX, origin.x),
30134
+ y: convertY(currentY, origin.y)
30135
+ });
30136
+ }
30137
+ break;
30138
+ case "Q":
30139
+ if (args.length >= 4) {
30140
+ currentX = args[2];
30141
+ currentY = args[3];
30142
+ points.push({
30143
+ x: convertX(currentX, origin.x),
30144
+ y: convertY(currentY, origin.y)
30145
+ });
30146
+ }
30147
+ break;
30148
+ case "T":
30149
+ if (args.length >= 2) {
30150
+ currentX = args[0];
30151
+ currentY = args[1];
30152
+ points.push({
30153
+ x: convertX(currentX, origin.x),
30154
+ y: convertY(currentY, origin.y)
30155
+ });
30156
+ }
30157
+ break;
30158
+ case "A":
30159
+ if (args.length >= 7) {
30160
+ currentX = args[5];
30161
+ currentY = args[6];
30162
+ points.push({
30163
+ x: convertX(currentX, origin.x),
30164
+ y: convertY(currentY, origin.y)
30165
+ });
30166
+ }
30167
+ break;
30168
+ case "Z":
30169
+ currentX = startX;
30170
+ currentY = startY;
30171
+ break;
30172
+ }
29996
30173
  }
29997
30174
  return points;
29998
30175
  }
@@ -30160,10 +30337,14 @@ var FOOTPRINT_LIBRARY_NAME = getFootprintDirName();
30160
30337
  var MODELS_3D_NAME = get3DModelsDirName();
30161
30338
  var LIBRARY_NAMESPACE2 = "jlc_mcp";
30162
30339
  var KICAD_VERSIONS3 = ["9.0", "8.0"];
30163
- var EASYEDA_LIBRARY_NAME = "EasyEDA";
30164
- var EASYEDA_SYMBOL_LIBRARY_NAME = "EasyEDA.kicad_sym";
30165
- var EASYEDA_FOOTPRINT_LIBRARY_NAME = "EasyEDA.pretty";
30166
- var EASYEDA_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library";
30340
+ var EASYEDA_LIBRARY_NAME2 = "EasyEDA";
30341
+ var EASYEDA_SYMBOL_LIBRARY_NAME2 = "EasyEDA.kicad_sym";
30342
+ var EASYEDA_FOOTPRINT_LIBRARY_NAME2 = "EasyEDA.pretty";
30343
+ var EASYEDA_LIBRARY_DESCRIPTION2 = "EasyEDA Community Component Library";
30344
+ var EASYEDA_LOCAL_LIBRARY_NAME = "EasyEDA-local";
30345
+ var EASYEDA_LOCAL_SYMBOL_LIBRARY_NAME = "EasyEDA-local.kicad_sym";
30346
+ var EASYEDA_LOCAL_FOOTPRINT_LIBRARY_NAME = "EasyEDA-local.pretty";
30347
+ var EASYEDA_LOCAL_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library (Project-local)";
30167
30348
  function detectKicadVersion2() {
30168
30349
  const home = homedir2();
30169
30350
  const baseDir = join4(home, "Documents", "KiCad");
@@ -30304,10 +30485,7 @@ function createLibraryService() {
30304
30485
  return {
30305
30486
  async install(id, options = {}) {
30306
30487
  const isCommunityComponent = !isLcscId2(id);
30307
- if (isCommunityComponent && !options.projectPath) {
30308
- throw new Error("EasyEDA community components require projectPath for local storage");
30309
- }
30310
- const isGlobal = !isCommunityComponent && !options.projectPath;
30488
+ const isGlobal = !options.projectPath;
30311
30489
  const paths = isGlobal ? getGlobalLibraryPaths() : getProjectLibraryPaths(options.projectPath);
30312
30490
  let component = null;
30313
30491
  if (isLcscId2(id)) {
@@ -30350,13 +30528,27 @@ function createLibraryService() {
30350
30528
  let category;
30351
30529
  let modelPath;
30352
30530
  if (isCommunityComponent) {
30353
- const librariesDir = join4(options.projectPath, "libraries");
30354
- const symbolsDir = join4(librariesDir, "symbols");
30355
- footprintDir = join4(librariesDir, "footprints", EASYEDA_FOOTPRINT_LIBRARY_NAME);
30356
- models3dDir = join4(librariesDir, "3dmodels", "EasyEDA.3dshapes");
30531
+ const libName = isGlobal ? EASYEDA_LIBRARY_NAME2 : EASYEDA_LOCAL_LIBRARY_NAME;
30532
+ const symLibFile = isGlobal ? EASYEDA_SYMBOL_LIBRARY_NAME2 : EASYEDA_LOCAL_SYMBOL_LIBRARY_NAME;
30533
+ const fpLibDir = isGlobal ? EASYEDA_FOOTPRINT_LIBRARY_NAME2 : EASYEDA_LOCAL_FOOTPRINT_LIBRARY_NAME;
30534
+ const libDesc = isGlobal ? EASYEDA_LIBRARY_DESCRIPTION2 : EASYEDA_LOCAL_LIBRARY_DESCRIPTION;
30535
+ const models3dDirName = isGlobal ? "EasyEDA.3dshapes" : "EasyEDA-local.3dshapes";
30536
+ let symbolsDir;
30537
+ let easyedaModelsDir;
30538
+ if (isGlobal) {
30539
+ symbolsDir = paths.symbolsDir;
30540
+ footprintDir = join4(paths.footprintsDir, fpLibDir);
30541
+ easyedaModelsDir = join4(paths.models3dDir, models3dDirName);
30542
+ } else {
30543
+ const librariesDir = join4(options.projectPath, "libraries");
30544
+ symbolsDir = join4(librariesDir, "symbols");
30545
+ footprintDir = join4(librariesDir, "footprints", fpLibDir);
30546
+ easyedaModelsDir = join4(librariesDir, "3dmodels", models3dDirName);
30547
+ }
30548
+ models3dDir = easyedaModelsDir;
30357
30549
  await ensureDir(symbolsDir);
30358
30550
  await ensureDir(footprintDir);
30359
- symbolFile = join4(symbolsDir, EASYEDA_SYMBOL_LIBRARY_NAME);
30551
+ symbolFile = join4(symbolsDir, symLibFile);
30360
30552
  symbolName = component.info.name.replace(/[^a-zA-Z0-9_-]/g, "_");
30361
30553
  const include3d = options.include3d ?? true;
30362
30554
  let modelRelativePath;
@@ -30367,21 +30559,30 @@ function createLibraryService() {
30367
30559
  const modelFilename = `${symbolName}.step`;
30368
30560
  modelPath = join4(models3dDir, modelFilename);
30369
30561
  await writeBinary(modelPath, model);
30370
- modelRelativePath = `\${KIPRJMOD}/libraries/3dmodels/EasyEDA.3dshapes/${modelFilename}`;
30562
+ if (isGlobal) {
30563
+ modelRelativePath = `\${KICAD9_3RD_PARTY}/${LIBRARY_NAMESPACE2}/3dmodels/${models3dDirName}/${modelFilename}`;
30564
+ } else {
30565
+ modelRelativePath = `\${KIPRJMOD}/libraries/3dmodels/${models3dDirName}/${modelFilename}`;
30566
+ }
30371
30567
  }
30372
30568
  }
30373
30569
  const footprint = footprintConverter.convert(component, {
30374
- libraryName: EASYEDA_LIBRARY_NAME,
30570
+ libraryName: libName,
30375
30571
  include3DModel: !!modelRelativePath,
30376
30572
  modelPath: modelRelativePath
30377
30573
  });
30378
30574
  footprintPath = join4(footprintDir, `${symbolName}.kicad_mod`);
30379
- footprintRef = `${EASYEDA_LIBRARY_NAME}:${symbolName}`;
30575
+ footprintRef = `${libName}:${symbolName}`;
30380
30576
  await writeText(footprintPath, footprint);
30381
30577
  component.info.package = footprintRef;
30382
- await ensureSymLibTable(options.projectPath, symbolFile, EASYEDA_LIBRARY_NAME, EASYEDA_LIBRARY_DESCRIPTION);
30383
- await ensureFpLibTable(options.projectPath, footprintDir, EASYEDA_LIBRARY_NAME, EASYEDA_LIBRARY_DESCRIPTION);
30384
- symbolRef = `${EASYEDA_LIBRARY_NAME}:${symbolName}`;
30578
+ if (isGlobal) {
30579
+ const { ensureGlobalEasyEDALibrary: ensureGlobalEasyEDALibrary2 } = await Promise.resolve().then(() => (init_global_lib_table(), exports_global_lib_table));
30580
+ await ensureGlobalEasyEDALibrary2();
30581
+ } else {
30582
+ await ensureSymLibTable(options.projectPath, symbolFile, libName, libDesc);
30583
+ await ensureFpLibTable(options.projectPath, footprintDir, libName, libDesc);
30584
+ }
30585
+ symbolRef = `${libName}:${symbolName}`;
30385
30586
  } else {
30386
30587
  category = getLibraryCategory(component.info.prefix, component.info.category, component.info.description);
30387
30588
  const symbolLibraryFilename = getLibraryFilename(category);
@@ -30428,7 +30629,7 @@ function createLibraryService() {
30428
30629
  if (symbolConverter.symbolExistsInLibrary(existingContent, component.info.name)) {
30429
30630
  if (options.force) {
30430
30631
  symbolContent = symbolConverter.replaceInLibrary(existingContent, component, {
30431
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
30632
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
30432
30633
  symbolName: isCommunityComponent ? symbolName : undefined
30433
30634
  });
30434
30635
  symbolAction = "replaced";
@@ -30438,14 +30639,14 @@ function createLibraryService() {
30438
30639
  }
30439
30640
  } else {
30440
30641
  symbolContent = symbolConverter.appendToLibrary(existingContent, component, {
30441
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
30642
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
30442
30643
  symbolName: isCommunityComponent ? symbolName : undefined
30443
30644
  });
30444
30645
  symbolAction = "appended";
30445
30646
  }
30446
30647
  } else {
30447
30648
  symbolContent = symbolConverter.convert(component, {
30448
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
30649
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
30449
30650
  symbolName: isCommunityComponent ? symbolName : undefined
30450
30651
  });
30451
30652
  symbolAction = "created";
@@ -30570,12 +30771,236 @@ function createLibraryService() {
30570
30771
  fpLibTable: fpLibTablePath
30571
30772
  }
30572
30773
  };
30774
+ },
30775
+ async isEasyEDAInstalled(componentName) {
30776
+ const paths = getGlobalLibraryPaths();
30777
+ const easyedaLibPath = join4(paths.symbolsDir, EASYEDA_SYMBOL_LIBRARY_NAME2);
30778
+ if (!existsSync3(easyedaLibPath)) {
30779
+ return false;
30780
+ }
30781
+ try {
30782
+ const content = await readFile4(easyedaLibPath, "utf-8");
30783
+ const sanitizedName = componentName.replace(/[^a-zA-Z0-9_-]/g, "_");
30784
+ const pattern = new RegExp(`\\(symbol\\s+"${sanitizedName}"`, "m");
30785
+ return pattern.test(content);
30786
+ } catch {
30787
+ return false;
30788
+ }
30573
30789
  }
30574
30790
  };
30575
30791
  }
30576
30792
  init_lib_table();
30577
30793
  init_category_router();
30578
30794
  init_global_lib_table();
30795
+ var logger7 = createLogger("http-routes");
30796
+ var __filename2 = fileURLToPath(import.meta.url);
30797
+ var __dirname2 = dirname3(__filename2);
30798
+ var htmlCache = null;
30799
+ function getHtmlPage() {
30800
+ if (htmlCache)
30801
+ return htmlCache;
30802
+ try {
30803
+ const possiblePaths = [
30804
+ join5(__dirname2, "assets/search.html"),
30805
+ join5(__dirname2, "../dist/assets/search.html"),
30806
+ join5(__dirname2, "../assets/search.html"),
30807
+ join5(__dirname2, "../assets/search-built.html"),
30808
+ join5(process.cwd(), "dist/assets/search.html"),
30809
+ join5(process.cwd(), "packages/core/dist/assets/search.html"),
30810
+ join5(__dirname2, "../../dist/assets/search.html"),
30811
+ join5(__dirname2, "../../../core/dist/assets/search.html"),
30812
+ join5(__dirname2, "../../../../packages/core/dist/assets/search.html"),
30813
+ join5(__dirname2, "../../node_modules/@jlcpcb/core/dist/assets/search.html")
30814
+ ];
30815
+ for (const path of possiblePaths) {
30816
+ try {
30817
+ htmlCache = readFileSync(path, "utf-8");
30818
+ logger7.debug(`Loaded HTML from: ${path}`);
30819
+ return htmlCache;
30820
+ } catch {}
30821
+ }
30822
+ throw new Error("HTML file not found");
30823
+ } catch (error2) {
30824
+ logger7.error("Failed to load HTML page:", error2);
30825
+ return `<!DOCTYPE html>
30826
+ <html>
30827
+ <head><title>Error</title></head>
30828
+ <body>
30829
+ <h1>Error: Search page not found</h1>
30830
+ <p>The search page has not been built. Run: bun run build</p>
30831
+ </body>
30832
+ </html>`;
30833
+ }
30834
+ }
30835
+ async function handleRequest(req, res) {
30836
+ const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
30837
+ const pathname = url.pathname;
30838
+ logger7.debug(`${req.method} ${pathname}`);
30839
+ if (pathname === "/" || pathname === "/index.html") {
30840
+ const query = url.searchParams.get("q") || undefined;
30841
+ serveHtml(res, query);
30842
+ } else if (pathname === "/api/search") {
30843
+ await handleSearch(url, res);
30844
+ } else if (pathname.startsWith("/api/component/")) {
30845
+ const uuid2 = pathname.replace("/api/component/", "");
30846
+ await handleComponent(uuid2, res);
30847
+ } else if (pathname === "/health") {
30848
+ res.writeHead(200, { "Content-Type": "application/json" });
30849
+ res.end(JSON.stringify({ status: "ok" }));
30850
+ } else {
30851
+ res.writeHead(404, { "Content-Type": "application/json" });
30852
+ res.end(JSON.stringify({ error: "Not found" }));
30853
+ }
30854
+ }
30855
+ function serveHtml(res, initialQuery) {
30856
+ let html = getHtmlPage();
30857
+ if (initialQuery) {
30858
+ const script = `<script>window.__INITIAL_QUERY__ = ${JSON.stringify(initialQuery)};</script>`;
30859
+ html = html.replace("</head>", `${script}</head>`);
30860
+ }
30861
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
30862
+ res.end(html);
30863
+ }
30864
+ async function handleSearch(url, res) {
30865
+ const query = url.searchParams.get("q") || "";
30866
+ const source = url.searchParams.get("source") || "user";
30867
+ const page = parseInt(url.searchParams.get("page") || "1", 10);
30868
+ const limit = parseInt(url.searchParams.get("limit") || "20", 10);
30869
+ if (!query) {
30870
+ res.writeHead(400, { "Content-Type": "application/json" });
30871
+ res.end(JSON.stringify({ error: "Missing query parameter" }));
30872
+ return;
30873
+ }
30874
+ try {
30875
+ const allResults = await easyedaCommunityClient.search({
30876
+ query,
30877
+ source,
30878
+ limit: Math.min(limit * page + limit, 100)
30879
+ });
30880
+ const startIndex = (page - 1) * limit;
30881
+ const endIndex = startIndex + limit;
30882
+ const results = allResults.slice(startIndex, endIndex);
30883
+ const totalPages = Math.ceil(allResults.length / limit);
30884
+ res.writeHead(200, { "Content-Type": "application/json" });
30885
+ res.end(JSON.stringify({
30886
+ results,
30887
+ pagination: {
30888
+ page,
30889
+ limit,
30890
+ total: allResults.length,
30891
+ totalPages,
30892
+ hasNext: page < totalPages,
30893
+ hasPrev: page > 1
30894
+ }
30895
+ }));
30896
+ } catch (error2) {
30897
+ logger7.error("Search error:", error2);
30898
+ res.writeHead(500, { "Content-Type": "application/json" });
30899
+ res.end(JSON.stringify({ error: "Search failed" }));
30900
+ }
30901
+ }
30902
+ function communityToComponentData(community) {
30903
+ const cPara = community.symbol.head?.c_para ?? {};
30904
+ const fpCPara = community.footprint.head?.c_para ?? {};
30905
+ return {
30906
+ info: {
30907
+ name: community.title || cPara.name || "Unknown",
30908
+ prefix: cPara.pre || cPara.Prefix || "U",
30909
+ package: fpCPara.package || community.footprint.name,
30910
+ manufacturer: cPara.Manufacturer || cPara.BOM_Manufacturer,
30911
+ description: community.description || cPara.BOM_Manufacturer_Part,
30912
+ category: cPara.package
30913
+ },
30914
+ symbol: community.symbol,
30915
+ footprint: community.footprint,
30916
+ model3d: community.model3d,
30917
+ rawData: community.rawData
30918
+ };
30919
+ }
30920
+ async function handleComponent(uuid2, res) {
30921
+ if (!uuid2) {
30922
+ res.writeHead(400, { "Content-Type": "application/json" });
30923
+ res.end(JSON.stringify({ error: "Missing UUID" }));
30924
+ return;
30925
+ }
30926
+ try {
30927
+ const component2 = await easyedaCommunityClient.getComponent(uuid2);
30928
+ if (!component2) {
30929
+ res.writeHead(404, { "Content-Type": "application/json" });
30930
+ res.end(JSON.stringify({ error: "Component not found" }));
30931
+ return;
30932
+ }
30933
+ const componentData = communityToComponentData(component2);
30934
+ let symbolSexpr = "";
30935
+ let footprintSexpr = "";
30936
+ try {
30937
+ symbolSexpr = symbolConverter.convertToSymbolEntry(componentData);
30938
+ } catch (e) {
30939
+ logger7.warn("Symbol conversion failed:", e);
30940
+ }
30941
+ try {
30942
+ footprintSexpr = footprintConverter.convert(componentData);
30943
+ } catch (e) {
30944
+ logger7.warn("Footprint conversion failed:", e);
30945
+ }
30946
+ res.writeHead(200, { "Content-Type": "application/json" });
30947
+ res.end(JSON.stringify({
30948
+ uuid: component2.uuid,
30949
+ title: component2.title,
30950
+ description: component2.description,
30951
+ owner: component2.owner,
30952
+ symbolSexpr,
30953
+ footprintSexpr,
30954
+ model3d: component2.model3d
30955
+ }));
30956
+ } catch (error2) {
30957
+ logger7.error("Component fetch error:", error2);
30958
+ res.writeHead(500, { "Content-Type": "application/json" });
30959
+ res.end(JSON.stringify({ error: "Failed to fetch component" }));
30960
+ }
30961
+ }
30962
+ var logger8 = createLogger("http-server");
30963
+ var DEFAULT_PORT = 3847;
30964
+ var serverInstance = null;
30965
+ function startHttpServer(options = {}) {
30966
+ if (serverInstance) {
30967
+ logger8.debug("HTTP server already running");
30968
+ const port2 = options.port ?? parseInt(process.env.JLC_MCP_HTTP_PORT || String(DEFAULT_PORT), 10);
30969
+ options.onReady?.(`http://localhost:${port2}`);
30970
+ return port2;
30971
+ }
30972
+ const port = options.port ?? parseInt(process.env.JLC_MCP_HTTP_PORT || String(DEFAULT_PORT), 10);
30973
+ serverInstance = createServer(async (req, res) => {
30974
+ res.setHeader("Access-Control-Allow-Origin", "*");
30975
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
30976
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
30977
+ if (req.method === "OPTIONS") {
30978
+ res.writeHead(204);
30979
+ res.end();
30980
+ return;
30981
+ }
30982
+ try {
30983
+ await handleRequest(req, res);
30984
+ } catch (error2) {
30985
+ logger8.error("Request error:", error2);
30986
+ res.writeHead(500, { "Content-Type": "application/json" });
30987
+ res.end(JSON.stringify({ error: "Internal server error" }));
30988
+ }
30989
+ });
30990
+ serverInstance.listen(port, () => {
30991
+ const url = `http://localhost:${port}`;
30992
+ logger8.info(`HTTP server listening on ${url}`);
30993
+ options.onReady?.(url);
30994
+ });
30995
+ serverInstance.on("error", (error2) => {
30996
+ if (error2.code === "EADDRINUSE") {
30997
+ logger8.warn(`Port ${port} already in use, HTTP server not started`);
30998
+ } else {
30999
+ logger8.error("HTTP server error:", error2);
31000
+ }
31001
+ });
31002
+ return port;
31003
+ }
30579
31004
 
30580
31005
  // src/tools/search.ts
30581
31006
  var searchComponentsTool = {
@@ -30907,14 +31332,14 @@ ${model.toString("base64").slice(0, 500)}...`
30907
31332
  import { existsSync as existsSync4, readdirSync } from "fs";
30908
31333
  import { readFile as readFile5 } from "fs/promises";
30909
31334
  import { homedir as homedir3, platform as platform3 } from "os";
30910
- import { join as join5 } from "path";
31335
+ import { join as join6 } from "path";
30911
31336
  var KICAD_VERSIONS = ["9.0", "8.0"];
30912
31337
  var LIBRARY_NAMESPACE3 = "jlc_mcp";
30913
31338
  function detectKicadVersion3() {
30914
31339
  const home = homedir3();
30915
- const baseDir = join5(home, "Documents", "KiCad");
31340
+ const baseDir = join6(home, "Documents", "KiCad");
30916
31341
  for (const version2 of KICAD_VERSIONS) {
30917
- if (existsSync4(join5(baseDir, version2))) {
31342
+ if (existsSync4(join6(baseDir, version2))) {
30918
31343
  return version2;
30919
31344
  }
30920
31345
  }
@@ -30922,11 +31347,11 @@ function detectKicadVersion3() {
30922
31347
  }
30923
31348
  function getLibraryPaths(projectPath) {
30924
31349
  if (projectPath) {
30925
- const librariesDir = join5(projectPath, "libraries");
31350
+ const librariesDir = join6(projectPath, "libraries");
30926
31351
  return {
30927
- symbolsDir: join5(librariesDir, "symbols"),
30928
- footprintDir: join5(librariesDir, "footprints", getFootprintDirName()),
30929
- models3dDir: join5(librariesDir, "3dmodels", get3DModelsDirName())
31352
+ symbolsDir: join6(librariesDir, "symbols"),
31353
+ footprintDir: join6(librariesDir, "footprints", getFootprintDirName()),
31354
+ models3dDir: join6(librariesDir, "3dmodels", get3DModelsDirName())
30930
31355
  };
30931
31356
  }
30932
31357
  const home = homedir3();
@@ -30934,14 +31359,14 @@ function getLibraryPaths(projectPath) {
30934
31359
  const plat = platform3();
30935
31360
  let base;
30936
31361
  if (plat === "linux") {
30937
- base = join5(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE3);
31362
+ base = join6(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE3);
30938
31363
  } else {
30939
- base = join5(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE3);
31364
+ base = join6(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE3);
30940
31365
  }
30941
31366
  return {
30942
- symbolsDir: join5(base, "symbols"),
30943
- footprintDir: join5(base, "footprints", getFootprintDirName()),
30944
- models3dDir: join5(base, "3dmodels", get3DModelsDirName())
31367
+ symbolsDir: join6(base, "symbols"),
31368
+ footprintDir: join6(base, "footprints", getFootprintDirName()),
31369
+ models3dDir: join6(base, "3dmodels", get3DModelsDirName())
30945
31370
  };
30946
31371
  }
30947
31372
  function extractLcscIdsFromLibrary(content) {
@@ -30967,7 +31392,7 @@ function findJlcLibraries(symbolsDir) {
30967
31392
  }
30968
31393
  try {
30969
31394
  const files = readdirSync(symbolsDir);
30970
- return files.filter((f) => f.startsWith("JLC-MCP-") && f.endsWith(".kicad_sym")).map((f) => join5(symbolsDir, f));
31395
+ return files.filter((f) => f.startsWith("JLC-MCP-") && f.endsWith(".kicad_sym")).map((f) => join6(symbolsDir, f));
30971
31396
  } catch {
30972
31397
  return [];
30973
31398
  }
@@ -31034,7 +31459,7 @@ async function handleUpdateLibrary(args) {
31034
31459
  libraries_to_create: allCategories.map((cat) => ({
31035
31460
  category: cat,
31036
31461
  filename: getLibraryFilename(cat),
31037
- path: join5(paths.symbolsDir, getLibraryFilename(cat))
31462
+ path: join6(paths.symbolsDir, getLibraryFilename(cat))
31038
31463
  }))
31039
31464
  }, null, 2)
31040
31465
  }]
@@ -31047,7 +31472,7 @@ async function handleUpdateLibrary(args) {
31047
31472
  const createdLibraries = [];
31048
31473
  for (const category of allCategories) {
31049
31474
  const filename = getLibraryFilename(category);
31050
- const filepath = join5(paths.symbolsDir, filename);
31475
+ const filepath = join6(paths.symbolsDir, filename);
31051
31476
  await writeText(filepath, emptyContent);
31052
31477
  createdLibraries.push(filepath);
31053
31478
  }
@@ -31124,7 +31549,7 @@ async function handleUpdateLibrary(args) {
31124
31549
  if (!params.dry_run && footprintResult.type === "generated") {
31125
31550
  await ensureDir(paths.footprintDir);
31126
31551
  const footprintName = footprintResult.name + "_" + lcscId;
31127
- const footprintPath = join5(paths.footprintDir, `${footprintName}.kicad_mod`);
31552
+ const footprintPath = join6(paths.footprintDir, `${footprintName}.kicad_mod`);
31128
31553
  await writeText(footprintPath, footprintResult.content);
31129
31554
  }
31130
31555
  results.push({
@@ -31151,7 +31576,7 @@ async function handleUpdateLibrary(args) {
31151
31576
  await ensureDir(paths.symbolsDir);
31152
31577
  for (const [category, entries] of categorySymbols) {
31153
31578
  const filename = getLibraryFilename(category);
31154
- const filepath = join5(paths.symbolsDir, filename);
31579
+ const filepath = join6(paths.symbolsDir, filename);
31155
31580
  const header = `(kicad_symbol_lib
31156
31581
  (version 20241209)
31157
31582
  (generator "jlc-mcp")
@@ -31186,7 +31611,7 @@ async function handleUpdateLibrary(args) {
31186
31611
  },
31187
31612
  by_category: Object.fromEntries(byCategory),
31188
31613
  footprint_stats: footprintStats,
31189
- libraries_written: params.dry_run ? [] : Array.from(categorySymbols.keys()).map((cat) => join5(paths.symbolsDir, getLibraryFilename(cat))),
31614
+ libraries_written: params.dry_run ? [] : Array.from(categorySymbols.keys()).map((cat) => join6(paths.symbolsDir, getLibraryFilename(cat))),
31190
31615
  failed_components: failed.map((f) => ({
31191
31616
  lcsc_id: f.lcscId,
31192
31617
  error: f.error
@@ -31200,13 +31625,13 @@ async function handleUpdateLibrary(args) {
31200
31625
  import { existsSync as existsSync6 } from "fs";
31201
31626
  import { readFile as readFile7 } from "fs/promises";
31202
31627
  import { homedir as homedir5 } from "os";
31203
- import { join as join7 } from "path";
31628
+ import { join as join8 } from "path";
31204
31629
  var KICAD_VERSIONS4 = ["9.0", "8.0"];
31205
31630
  function detectKicadVersion5() {
31206
31631
  const home = homedir5();
31207
- const baseDir = join7(home, "Documents", "KiCad");
31632
+ const baseDir = join8(home, "Documents", "KiCad");
31208
31633
  for (const version2 of KICAD_VERSIONS4) {
31209
- if (existsSync6(join7(baseDir, version2))) {
31634
+ if (existsSync6(join8(baseDir, version2))) {
31210
31635
  return version2;
31211
31636
  }
31212
31637
  }
@@ -31214,18 +31639,18 @@ function detectKicadVersion5() {
31214
31639
  }
31215
31640
  function getLibraryPaths2(projectPath) {
31216
31641
  if (projectPath) {
31217
- const librariesDir = join7(projectPath, "libraries");
31642
+ const librariesDir = join8(projectPath, "libraries");
31218
31643
  return {
31219
- symbolsDir: join7(librariesDir, "symbols"),
31220
- footprintDir: join7(librariesDir, "footprints", getFootprintDirName())
31644
+ symbolsDir: join8(librariesDir, "symbols"),
31645
+ footprintDir: join8(librariesDir, "footprints", getFootprintDirName())
31221
31646
  };
31222
31647
  }
31223
31648
  const home = homedir5();
31224
31649
  const version2 = detectKicadVersion5();
31225
- const base = join7(home, "Documents", "KiCad", version2);
31650
+ const base = join8(home, "Documents", "KiCad", version2);
31226
31651
  return {
31227
- symbolsDir: join7(base, "symbols"),
31228
- footprintDir: join7(base, "footprints", getFootprintDirName())
31652
+ symbolsDir: join8(base, "symbols"),
31653
+ footprintDir: join8(base, "footprints", getFootprintDirName())
31229
31654
  };
31230
31655
  }
31231
31656
  var PinElectricalType = exports_external.enum([
@@ -31393,7 +31818,7 @@ async function handleFixLibrary(args) {
31393
31818
  }
31394
31819
  const category = getLibraryCategory(component.info.prefix, component.info.category, component.info.description);
31395
31820
  const symbolLibraryFilename = getLibraryFilename(category);
31396
- const symbolFile = join7(paths.symbolsDir, symbolLibraryFilename);
31821
+ const symbolFile = join8(paths.symbolsDir, symbolLibraryFilename);
31397
31822
  if (!existsSync6(symbolFile) && !params.force) {
31398
31823
  return {
31399
31824
  content: [{
@@ -31495,7 +31920,7 @@ async function handleFixLibrary(args) {
31495
31920
  const footprintName = footprintResult.name + "_" + params.lcsc_id;
31496
31921
  footprintRef = getFootprintReference2(footprintName);
31497
31922
  await ensureDir(paths.footprintDir);
31498
- const footprintPath = join7(paths.footprintDir, `${footprintName}.kicad_mod`);
31923
+ const footprintPath = join8(paths.footprintDir, `${footprintName}.kicad_mod`);
31499
31924
  await writeText(footprintPath, footprintResult.content);
31500
31925
  }
31501
31926
  component.info.package = footprintRef;
@@ -31532,7 +31957,7 @@ async function handleFixLibrary(args) {
31532
31957
  }
31533
31958
 
31534
31959
  // src/tools/easyeda.ts
31535
- import { join as join8 } from "path";
31960
+ import { join as join9 } from "path";
31536
31961
  import { execSync as execSync2 } from "child_process";
31537
31962
  import { tmpdir } from "os";
31538
31963
  var easyedaSearchTool = {
@@ -31676,7 +32101,7 @@ ${model.toString("base64").slice(0, 500)}...`
31676
32101
  async function generateHtmlPreview(query, results) {
31677
32102
  const timestamp = Date.now();
31678
32103
  const filename = `easyeda-search-${timestamp}.html`;
31679
- const filepath = join8(tmpdir(), filename);
32104
+ const filepath = join9(tmpdir(), filename);
31680
32105
  const noImageSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 150" style="background:#2a2a2a"><text x="100" y="75" text-anchor="middle" fill="#666" font-size="12">No Preview</text></svg>`;
31681
32106
  const noImageDataUri = `data:image/svg+xml,${encodeURIComponent(noImageSvg)}`;
31682
32107
  const cardsPromises = results.slice(0, 10).map(async (r) => {
@@ -32014,213 +32439,8 @@ var toolHandlers = {
32014
32439
  easyeda_get_3d_model: handleEasyedaGet3DModel
32015
32440
  };
32016
32441
 
32017
- // src/http/server.ts
32018
- import { createServer } from "http";
32019
-
32020
- // src/http/routes.ts
32021
- import { readFileSync } from "fs";
32022
- import { join as join9, dirname as dirname2 } from "path";
32023
- import { fileURLToPath } from "url";
32024
- var logger2 = createLogger("http-routes");
32025
- var __filename2 = fileURLToPath(import.meta.url);
32026
- var __dirname2 = dirname2(__filename2);
32027
- var htmlCache = null;
32028
- function getHtmlPage() {
32029
- if (htmlCache)
32030
- return htmlCache;
32031
- try {
32032
- const possiblePaths = [
32033
- join9(__dirname2, "assets/search.html"),
32034
- join9(__dirname2, "../dist/assets/search.html"),
32035
- join9(__dirname2, "../assets/search.html"),
32036
- join9(__dirname2, "../assets/search-built.html"),
32037
- join9(process.cwd(), "dist/assets/search.html"),
32038
- join9(process.cwd(), "packages/jlc-mcp/dist/assets/search.html")
32039
- ];
32040
- for (const path of possiblePaths) {
32041
- try {
32042
- htmlCache = readFileSync(path, "utf-8");
32043
- logger2.debug(`Loaded HTML from: ${path}`);
32044
- return htmlCache;
32045
- } catch {}
32046
- }
32047
- throw new Error("HTML file not found");
32048
- } catch (error2) {
32049
- logger2.error("Failed to load HTML page:", error2);
32050
- return `<!DOCTYPE html>
32051
- <html>
32052
- <head><title>Error</title></head>
32053
- <body>
32054
- <h1>Error: Search page not found</h1>
32055
- <p>The search page has not been built. Run: bun run build</p>
32056
- </body>
32057
- </html>`;
32058
- }
32059
- }
32060
- async function handleRequest(req, res) {
32061
- const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
32062
- const pathname = url.pathname;
32063
- logger2.debug(`${req.method} ${pathname}`);
32064
- if (pathname === "/" || pathname === "/index.html") {
32065
- serveHtml(res);
32066
- } else if (pathname === "/api/search") {
32067
- await handleSearch(url, res);
32068
- } else if (pathname.startsWith("/api/component/")) {
32069
- const uuid2 = pathname.replace("/api/component/", "");
32070
- await handleComponent(uuid2, res);
32071
- } else if (pathname === "/health") {
32072
- res.writeHead(200, { "Content-Type": "application/json" });
32073
- res.end(JSON.stringify({ status: "ok" }));
32074
- } else {
32075
- res.writeHead(404, { "Content-Type": "application/json" });
32076
- res.end(JSON.stringify({ error: "Not found" }));
32077
- }
32078
- }
32079
- function serveHtml(res) {
32080
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
32081
- res.end(getHtmlPage());
32082
- }
32083
- async function handleSearch(url, res) {
32084
- const query = url.searchParams.get("q") || "";
32085
- const source = url.searchParams.get("source") || "user";
32086
- const page = parseInt(url.searchParams.get("page") || "1", 10);
32087
- const limit = parseInt(url.searchParams.get("limit") || "20", 10);
32088
- if (!query) {
32089
- res.writeHead(400, { "Content-Type": "application/json" });
32090
- res.end(JSON.stringify({ error: "Missing query parameter" }));
32091
- return;
32092
- }
32093
- try {
32094
- const allResults = await easyedaCommunityClient.search({
32095
- query,
32096
- source,
32097
- limit: Math.min(limit * page + limit, 100)
32098
- });
32099
- const startIndex = (page - 1) * limit;
32100
- const endIndex = startIndex + limit;
32101
- const results = allResults.slice(startIndex, endIndex);
32102
- const totalPages = Math.ceil(allResults.length / limit);
32103
- res.writeHead(200, { "Content-Type": "application/json" });
32104
- res.end(JSON.stringify({
32105
- results,
32106
- pagination: {
32107
- page,
32108
- limit,
32109
- total: allResults.length,
32110
- totalPages,
32111
- hasNext: page < totalPages,
32112
- hasPrev: page > 1
32113
- }
32114
- }));
32115
- } catch (error2) {
32116
- logger2.error("Search error:", error2);
32117
- res.writeHead(500, { "Content-Type": "application/json" });
32118
- res.end(JSON.stringify({ error: "Search failed" }));
32119
- }
32120
- }
32121
- function communityToComponentData(community) {
32122
- const cPara = community.symbol.head?.c_para ?? {};
32123
- const fpCPara = community.footprint.head?.c_para ?? {};
32124
- return {
32125
- info: {
32126
- name: community.title || cPara.name || "Unknown",
32127
- prefix: cPara.pre || cPara.Prefix || "U",
32128
- package: fpCPara.package || community.footprint.name,
32129
- manufacturer: cPara.Manufacturer || cPara.BOM_Manufacturer,
32130
- description: community.description || cPara.BOM_Manufacturer_Part,
32131
- category: cPara.package
32132
- },
32133
- symbol: community.symbol,
32134
- footprint: community.footprint,
32135
- model3d: community.model3d,
32136
- rawData: community.rawData
32137
- };
32138
- }
32139
- async function handleComponent(uuid2, res) {
32140
- if (!uuid2) {
32141
- res.writeHead(400, { "Content-Type": "application/json" });
32142
- res.end(JSON.stringify({ error: "Missing UUID" }));
32143
- return;
32144
- }
32145
- try {
32146
- const component = await easyedaCommunityClient.getComponent(uuid2);
32147
- if (!component) {
32148
- res.writeHead(404, { "Content-Type": "application/json" });
32149
- res.end(JSON.stringify({ error: "Component not found" }));
32150
- return;
32151
- }
32152
- const componentData = communityToComponentData(component);
32153
- let symbolSexpr = "";
32154
- let footprintSexpr = "";
32155
- try {
32156
- symbolSexpr = symbolConverter.convertToSymbolEntry(componentData);
32157
- } catch (e) {
32158
- logger2.warn("Symbol conversion failed:", e);
32159
- }
32160
- try {
32161
- footprintSexpr = footprintConverter.convert(componentData);
32162
- } catch (e) {
32163
- logger2.warn("Footprint conversion failed:", e);
32164
- }
32165
- res.writeHead(200, { "Content-Type": "application/json" });
32166
- res.end(JSON.stringify({
32167
- uuid: component.uuid,
32168
- title: component.title,
32169
- description: component.description,
32170
- owner: component.owner,
32171
- symbolSexpr,
32172
- footprintSexpr,
32173
- model3d: component.model3d
32174
- }));
32175
- } catch (error2) {
32176
- logger2.error("Component fetch error:", error2);
32177
- res.writeHead(500, { "Content-Type": "application/json" });
32178
- res.end(JSON.stringify({ error: "Failed to fetch component" }));
32179
- }
32180
- }
32181
-
32182
- // src/http/server.ts
32183
- var logger7 = createLogger("http-server");
32184
- var DEFAULT_PORT = 3847;
32185
- var serverInstance = null;
32186
- function startHttpServer() {
32187
- if (serverInstance) {
32188
- logger7.debug("HTTP server already running");
32189
- return DEFAULT_PORT;
32190
- }
32191
- const port = parseInt(process.env.JLC_MCP_HTTP_PORT || String(DEFAULT_PORT), 10);
32192
- serverInstance = createServer(async (req, res) => {
32193
- res.setHeader("Access-Control-Allow-Origin", "*");
32194
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
32195
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
32196
- if (req.method === "OPTIONS") {
32197
- res.writeHead(204);
32198
- res.end();
32199
- return;
32200
- }
32201
- try {
32202
- await handleRequest(req, res);
32203
- } catch (error2) {
32204
- logger7.error("Request error:", error2);
32205
- res.writeHead(500, { "Content-Type": "application/json" });
32206
- res.end(JSON.stringify({ error: "Internal server error" }));
32207
- }
32208
- });
32209
- serverInstance.listen(port, () => {
32210
- logger7.info(`HTTP server listening on http://localhost:${port}`);
32211
- });
32212
- serverInstance.on("error", (error2) => {
32213
- if (error2.code === "EADDRINUSE") {
32214
- logger7.warn(`Port ${port} already in use, HTTP server not started`);
32215
- } else {
32216
- logger7.error("HTTP server error:", error2);
32217
- }
32218
- });
32219
- return port;
32220
- }
32221
-
32222
32442
  // src/index.ts
32223
- var logger9 = createLogger("jlc-mcp");
32443
+ var logger2 = createLogger("jlc-mcp");
32224
32444
  var server = new Server({
32225
32445
  name: "jlc-mcp",
32226
32446
  version: "0.1.0"
@@ -32230,12 +32450,12 @@ var server = new Server({
32230
32450
  }
32231
32451
  });
32232
32452
  server.setRequestHandler(ListToolsRequestSchema, async () => {
32233
- logger9.debug("Listing tools");
32453
+ logger2.debug("Listing tools");
32234
32454
  return { tools };
32235
32455
  });
32236
32456
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
32237
32457
  const { name, arguments: args } = request.params;
32238
- logger9.debug(`Tool call: ${name}`, args);
32458
+ logger2.debug(`Tool call: ${name}`, args);
32239
32459
  const handler = toolHandlers[name];
32240
32460
  if (!handler) {
32241
32461
  return {
@@ -32246,7 +32466,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
32246
32466
  try {
32247
32467
  return await handler(args);
32248
32468
  } catch (error2) {
32249
- logger9.error(`Tool error: ${name}`, error2);
32469
+ logger2.error(`Tool error: ${name}`, error2);
32250
32470
  return {
32251
32471
  content: [{
32252
32472
  type: "text",
@@ -32259,26 +32479,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
32259
32479
  async function main() {
32260
32480
  const registration = await ensureGlobalLibraryTables();
32261
32481
  if (!registration.success) {
32262
- logger9.error("Failed to register JLC libraries in KiCad global tables", {
32482
+ logger2.error("Failed to register JLC libraries in KiCad global tables", {
32263
32483
  errors: registration.errors
32264
32484
  });
32265
32485
  process.exit(1);
32266
32486
  }
32267
32487
  const { symLibTable, fpLibTable, libraryStubs, version: version2 } = registration;
32268
32488
  if (symLibTable.created || symLibTable.modified || fpLibTable.created || fpLibTable.modified) {
32269
- logger9.info(`JLC libraries registered in KiCad ${version2}`, {
32489
+ logger2.info(`JLC libraries registered in KiCad ${version2}`, {
32270
32490
  symLibTable: symLibTable.created ? `created with ${symLibTable.entriesAdded} entries` : symLibTable.modified ? `added ${symLibTable.entriesAdded} entries` : "already configured",
32271
32491
  fpLibTable: fpLibTable.created ? "created" : fpLibTable.modified ? "updated" : "already configured",
32272
32492
  stubsCreated: libraryStubs.symbolsCreated.length + libraryStubs.directoriesCreated.length
32273
32493
  });
32274
32494
  }
32275
32495
  const httpPort = startHttpServer();
32276
- logger9.info(`Component browser available at http://localhost:${httpPort}`);
32496
+ logger2.info(`Component browser available at http://localhost:${httpPort}`);
32277
32497
  const transport = new StdioServerTransport;
32278
32498
  await server.connect(transport);
32279
- logger9.info("JLC MCP server running on stdio");
32499
+ logger2.info("JLC MCP server running on stdio");
32280
32500
  }
32281
32501
  main().catch((error2) => {
32282
- logger9.error("Server error", error2);
32502
+ logger2.error("Server error", error2);
32283
32503
  process.exit(1);
32284
32504
  });