@vcad/mcp 0.10.0-main.98 → 0.10.0-main.99

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/index.mjs CHANGED
@@ -41844,6 +41844,82 @@ var init_CHANGELOG = __esm({
41844
41844
  CHANGELOG_default = {
41845
41845
  $schema: "./changelog.schema.json",
41846
41846
  entries: [
41847
+ {
41848
+ id: "2026-10-20-vendor-verified-power-imu-footprints",
41849
+ version: "0.10.0",
41850
+ date: "2026-10-20",
41851
+ category: "feat",
41852
+ title: "Vendor-verified power, IMU and XT/LCB footprints",
41853
+ summary: "BMI088, ICM-42688-P, TO-277A, XT90PW, XT60PB and AMASS LCB footprints added; TOLL, TDSON/TSDSON, XT30/XT60 and Micro-Fit lands corrected to vendor drawings.",
41854
+ features: [
41855
+ "pcb",
41856
+ "footprints"
41857
+ ],
41858
+ mcpTools: [
41859
+ "search_footprints",
41860
+ "get_footprint"
41861
+ ]
41862
+ },
41863
+ {
41864
+ id: "2026-10-20-schematic-power-flags",
41865
+ version: "0.10.0",
41866
+ date: "2026-10-20",
41867
+ category: "feat",
41868
+ title: "Schematic power flags (PWR_FLAG)",
41869
+ summary: "Mark a net as driven when it is fed through a ferrite, FET or connector. ERC treats it as powered, flags never reach the BOM, and KiCad PWR_FLAG symbols import as flags.",
41870
+ features: [
41871
+ "schematic",
41872
+ "ecad",
41873
+ "erc"
41874
+ ],
41875
+ mcpTools: [
41876
+ "create_schematic",
41877
+ "update_schematic",
41878
+ "run_erc"
41879
+ ]
41880
+ },
41881
+ {
41882
+ id: "2026-10-20-live-part-stock-check",
41883
+ version: "0.10.0",
41884
+ date: "2026-10-20",
41885
+ category: "feat",
41886
+ title: "Orderable parts DB + live LCSC/JLCPCB stock check",
41887
+ summary: "Curated MPN DB with verified LCSC numbers and footprint mapping; check_part_stock and bom_check_stock fetch live stock, price breaks and basic/extended status, and flag missing parts.",
41888
+ features: [
41889
+ "parts",
41890
+ "bom",
41891
+ "ecad",
41892
+ "sourcing"
41893
+ ],
41894
+ mcpTools: [
41895
+ "check_part_stock",
41896
+ "bom_check_stock",
41897
+ "search_electronic_parts",
41898
+ "resolve_part",
41899
+ "find_alternatives"
41900
+ ]
41901
+ },
41902
+ {
41903
+ id: "2026-10-20-hierarchical-schematic-sheets",
41904
+ version: "0.10.0",
41905
+ date: "2026-10-20",
41906
+ category: "feat",
41907
+ title: "Hierarchical schematic sheets",
41908
+ summary: "Schematics can hold reusable child sheets placed many times (CAN x6), with ports, per-instance refs and local nets, global nets, and flattening for ERC, BOM, board and KiCad.",
41909
+ features: [
41910
+ "schematic",
41911
+ "ecad",
41912
+ "erc"
41913
+ ],
41914
+ mcpTools: [
41915
+ "create_schematic",
41916
+ "update_schematic",
41917
+ "inspect_schematic",
41918
+ "run_erc",
41919
+ "place_components",
41920
+ "export_kicad"
41921
+ ]
41922
+ },
41847
41923
  {
41848
41924
  id: "2026-10-19-plane-stub-drc-exemption",
41849
41925
  version: "0.10.0",
@@ -57185,6 +57261,8 @@ var init_tool_metadata = __esm({
57185
57261
  bom_add_line: { title: "Add BOM Line", annotations: RW },
57186
57262
  bom_export: { title: "Export BOM", annotations: RW },
57187
57263
  search_mechanical_parts: { title: "Search Mechanical Parts", annotations: RO_NET },
57264
+ check_part_stock: { title: "Check Part Stock", annotations: RO_NET },
57265
+ bom_check_stock: { title: "Check BOM Stock", annotations: RO_NET },
57188
57266
  // ── Live review window ─────────────────────────────────────────────────
57189
57267
  share_session: { title: "Share Session", annotations: RW },
57190
57268
  unshare_session: { title: "Unshare Session", annotations: RW },
@@ -57315,6 +57393,8 @@ var init_tool_metadata = __esm({
57315
57393
  export_video: { title: "Export Video", annotations: RO },
57316
57394
  // ── ECAD (PCB) ─────────────────────────────────────────────────────────
57317
57395
  create_schematic: { title: "Create Schematic", annotations: RW },
57396
+ update_schematic: { title: "Update Schematic", annotations: RW },
57397
+ inspect_schematic: { title: "Inspect Schematic", annotations: RO },
57318
57398
  place_components: { title: "Place Components", annotations: RW },
57319
57399
  route_nets: { title: "Route Nets", annotations: RW },
57320
57400
  add_coil: { title: "Add Coil", annotations: RW },
@@ -60844,6 +60924,528 @@ var init_mech_parts = __esm({
60844
60924
  }
60845
60925
  });
60846
60926
 
60927
+ // src/tools/orderable-catalog.generated.ts
60928
+ var ORDERABLE_CATALOG_JSON;
60929
+ var init_orderable_catalog_generated = __esm({
60930
+ "src/tools/orderable-catalog.generated.ts"() {
60931
+ "use strict";
60932
+ ORDERABLE_CATALOG_JSON = `{"schema":"vcad-orderable-parts/1","version":"1","checked":"2026-10-20","notes":["Curated ORDERABLE electronic parts: real manufacturer part numbers with","package -> vcad footprint mapping, key parameters, datasheet, and the LCSC","part number as verified against LCSC / JLCPCB public listings on \`checked\`.","This is the sourcing sibling of the generative passive catalog and the","jellybean pinout database: it answers 'what exactly do I order, and from","where', not 'what are the pins'. Stock and price are NEVER stored here \u2014","they go stale in days; \`check_part_stock\` fetches them live (with this","file as the offline fallback for identity and LCSC mapping).","verification.status: verified = MPN and LCSC# confirmed on a vendor page;","corrected = the source BOM's MPN or LCSC# was wrong, \`bom_mpn\` / \`bom_lcsc\`","keep the original; flagged = could not be confirmed orderable \u2014 see note.","Seeded from the rose limbic board BOM (mini/limbic). Bundled into","@vcad/mcp at build time by scripts/gen-orderable-catalog.mjs."],"parts":[{"id":"infineon.ipt012n08n5","mpn":"IPT012N08N5","manufacturer":"Infineon","category":"mosfet","description":"80 V 1.2 mOhm N-channel OptiMOS 5 power MOSFET, TOLL","footprint":"Infineon_PG-HSOF-8-1_TOLL","pins":9,"pads":3,"footprint_status":"generated","params":{"vds_v":80,"id_a":300,"rds_on_mohm":1.2,"vgs_th_v":3,"pd_w":375,"channel":"N"},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_2005292218_Infineon-Technologies-IPT012N08N5_C531199.pdf","lcsc":"C531199","jlc_library":"extended","aliases":["IPT012N08N5ATMA1"],"bom_mpn":"IPT012N08N5ATMA1","verification":{"status":"corrected","source":"JLCPCB/LCSC listing C531199 (IPT012N08N5) and C122550 (IPT012N08N5ATMA1)","note":"Real part. The ATMA1 reel code is listed as C122550 but carries zero stock; the same die under the base code IPT012N08N5 is C531199 and stocked. Order C531199."},"alternatives":[{"mpn":"IAUT300N08S5N012ATMA2","lcsc":"C3278548","note":"80 V 1.2 mOhm automotive TOLL (HSOF-8), same footprint"},{"mpn":"IPT015N10N5","lcsc":"C108964","note":"100 V 1.5 mOhm TOLL, same footprint, more Vds headroom"}]},{"id":"infineon.bsc026n08ns5","mpn":"BSC026N08NS5ATMA1","manufacturer":"Infineon","category":"mosfet","description":"80 V 2.6 mOhm N-channel OptiMOS 5, SuperSO8 (TDSON-8 5x6)","footprint":"Infineon_PG-TDSON-8","pins":9,"pads":3,"footprint_status":"generated","params":{"vds_v":80,"id_a":184,"rds_on_mohm":2.6,"vgs_th_v":3.8,"qg_nc":74,"pd_w":156,"channel":"N"},"datasheet_url":"https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2404171116_Infineon-Technologies-BSC026N08NS5ATMA1_C5955453.pdf","lcsc":"C5955453","jlc_library":"extended","aliases":["BSC026N08NS5"],"verification":{"status":"verified","source":"JLCPCB/LCSC listing C5955453"},"alternatives":[{"mpn":"BSC026N08NS5","lcsc":"C534311","note":"same part, non-reel code"}]},{"id":"ti.tps48110aq","mpn":"TPS48110AQDGXRQ1","manufacturer":"Texas Instruments","category":"hot_swap_controller","description":"3.5-80 V automotive smart high-side driver with protection + diagnostics (auto-retry), back-to-back FET gate drive","footprint":"VSSOP-19_3x4.9mm_P0.5mm_TI_DGX","pins":19,"footprint_status":"placeholder","params":{"vin_min_v":3.5,"vin_max_v":80,"iq_ua":700,"fault_mode":"auto-retry","aec_q100":true},"datasheet_url":"https://www.ti.com/lit/ds/symlink/tps4811-q1.pdf","lcsc":"C17556513","jlc_library":"extended","aliases":["TPS48110-Q1","TPS48110"],"verification":{"status":"verified","source":"ti.com part-details page + JLCPCB/LCSC C17556513"}},{"id":"ti.tps48111lq","mpn":"TPS48111LQDGXRQ1","manufacturer":"Texas Instruments","category":"hot_swap_controller","description":"3.5-80 V automotive smart high-side driver, latch-off variant of TPS4811-Q1","footprint":"VSSOP-19_3x4.9mm_P0.5mm_TI_DGX","pins":19,"footprint_status":"placeholder","params":{"vin_min_v":3.5,"vin_max_v":80,"fault_mode":"latch-off","aec_q100":true},"datasheet_url":"https://www.ti.com/lit/ds/symlink/tps4811-q1.pdf","lcsc":"C17558994","jlc_library":"extended","aliases":["TPS48111-Q1","TPS48111","TPS48111AQDGXRQ1"],"bom_mpn":"TPS48111AQDGXRQ1","verification":{"status":"corrected","source":"ti.com/product/TPS4811-Q1 orderable list; part-details/TPS48111AQDGXRQ1 returns 404","note":"TPS48111AQDGXRQ1 does not exist. TI's only TPS48111 orderable is TPS48111LQDGXRQ1 (same DGX package). Confirm the latch-off fault behaviour is what the design wants."}},{"id":"st.stm32h723vgt6","mpn":"STM32H723VGT6","manufacturer":"STMicroelectronics","category":"mcu","description":"Arm Cortex-M7 550 MHz, 1 MB flash, 564 KB RAM, LQFP-100","footprint":"LQFP-100_14x14mm_P0.5mm","pins":100,"footprint_status":"generated","params":{"core":"Cortex-M7","fmax_mhz":550,"flash_kb":1024,"ram_kb":564,"vdd_v":[1.71,3.6],"interfaces":["FDCAN x3","Ethernet MAC","USB OTG HS/FS","OCTOSPI","SPI","I2C","UART"]},"datasheet_url":"https://www.st.com/resource/en/datasheet/stm32h723vg.pdf","lcsc":"C730142","jlc_library":"extended","bom_lcsc":"C730194","verification":{"status":"corrected","source":"LCSC product detail C730194 + JLCPCB listing C730142","note":"BOM's C730194 is an STM32H747AGI6 (UFBGA-169) \u2014 wrong part. STM32H723VGT6 is C730142."}},{"id":"ti.tcan1462vq1","mpn":"TCAN1462VDRQ1","manufacturer":"Texas Instruments","category":"can_transceiver","description":"Automotive CAN FD SIC transceiver, 8 Mbps, VIO pin, SOIC-8","footprint":"SOIC-8_3.9x4.9mm_P1.27mm","pins":8,"footprint_status":"generated","params":{"data_rate_mbps":8,"vcc_v":[4.5,5.5],"vio":true,"bus_fault_v":58,"aec_q100":true,"interfaces":["CAN FD (SIC)"]},"datasheet_url":"https://www.ti.com/lit/ds/symlink/tcan1462-q1.pdf","lcsc":"C7399456","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C7399456"},"alternatives":[{"mpn":"TCAN1044AVDRQ1","lcsc":"C3234993","note":"CAN FD (non-SIC) with VIO, SOIC-8, deep LCSC stock; confirm pin 5/8 functions match"}]},{"id":"st.stm32g474ret6","mpn":"STM32G474RET6","manufacturer":"STMicroelectronics","category":"mcu","description":"Arm Cortex-M4F 170 MHz, 512 KB flash, HRTIM, LQFP-64","footprint":"LQFP-64_10x10mm_P0.5mm","pins":64,"footprint_status":"generated","params":{"core":"Cortex-M4F","fmax_mhz":170,"flash_kb":512,"ram_kb":128,"vdd_v":[1.71,3.6],"interfaces":["FDCAN x3","USB FS","SPI","I2C","UART","HRTIM"]},"datasheet_url":"https://www.st.com/resource/en/datasheet/stm32g474re.pdf","lcsc":"C521608","jlc_library":"extended","verification":{"status":"verified","source":"LCSC product detail C521608"}},{"id":"bosch.bmi088","mpn":"BMI088","manufacturer":"Bosch Sensortec","category":"imu","description":"6-axis IMU (separate accel/gyro dies), high vibration robustness","footprint":"BMI088_LGA-16","pins":16,"footprint_status":"generated","params":{"accel_range_g":24,"gyro_range_dps":2000,"vdd_v":[2.4,3.6],"interfaces":["SPI","I2C"]},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_1811031202_Bosch-Sensortec-BMI088_C194919.pdf","lcsc":"C194919","jlc_library":"extended","verification":{"status":"verified","source":"LCSC product detail C194919"}},{"id":"tdk.icm42688p","mpn":"ICM-42688-P","manufacturer":"TDK InvenSense","category":"imu","description":"6-axis low-noise IMU, LGA-14 2.5x3","footprint":"ICM-42688-P_LGA-14","pins":14,"footprint_status":"generated","params":{"accel_range_g":16,"gyro_range_dps":2000,"vdd_v":[1.71,3.6],"interfaces":["SPI","I2C","I3C"]},"datasheet_url":"https://invensense.tdk.com/wp-content/uploads/2020/04/ds-000347_icm-42688-p-datasheet.pdf","lcsc":"C1850418","jlc_library":"extended","verification":{"status":"verified","source":"LCSC product detail C1850418"}},{"id":"panasonic.eehza1j101p","mpn":"EEH-ZA1J101P","manufacturer":"Panasonic","category":"capacitor_polymer_hybrid","description":"100 uF 63 V conductive-polymer hybrid aluminium SMD, 10x10.2 (as specified by BOM)","footprint":"CP_Elec_10x10.5","pins":2,"footprint_status":"placeholder","params":{"capacitance_uf":100,"voltage_v":63},"datasheet_url":"https://industrial.panasonic.com/cdbs/www-data/pdf/RDF0000/ABA0000C1259.pdf","lcsc":null,"jlc_library":null,"aliases":["EEHZA1J101P"],"verification":{"status":"flagged","source":"JLCPCB/LCSC search (no hit for EEHZA1J101P); distributor web search","note":"No orderable listing found. The ZA series appears to top out at 56 uF in 10x10.2 at 63 V (EEHZA1J560P, C178638, out of stock), and EEHZA1H101P is 50 V, which is too low for a 48 V bus. Re-pick: 2x EEHZA1J560P per site, or a larger-can 63 V hybrid."},"alternatives":[{"mpn":"EEHZA1J560P","lcsc":"C178638","note":"56 uF 63 V ZA, same 10x10.2 can; use two per 100 uF site"},{"mpn":"RYHV63V100UF10*10","lcsc":"C48970691","note":"Honor 63 V 100 uF 10x10; check whether it is polymer or plain electrolytic (ESR/ripple) before substituting"}]},{"id":"bourns.pwr263s3533r0f","mpn":"PWR263S-35-33R0F","manufacturer":"Bourns","category":"resistor_power","description":"33 Ohm 35 W 1% thick-film power resistor, TO-263 (D2PAK)","footprint":"TO-263-2","pins":2,"footprint_status":"generated","params":{"resistance_ohm":33,"power_w":35,"tolerance_pct":1,"tcr_ppm":100},"datasheet_url":"https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2304140030_BOURNS-PWR263S-35-33R0F_C1356479.pdf","lcsc":"C1356479","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C1356479","note":"MPN is real and listed, but LCSC stock was 0 on the check date \u2014 source from Digi-Key/Mouser or pre-order. Pulse energy rating still needs checking against the bus-precharge energy."}},{"id":"ti.tps26631","mpn":"TPS26631PWPR","manufacturer":"Texas Instruments","category":"efuse","description":"4.5-60 V 6 A industrial eFuse with integrated 31 mOhm FET, HTSSOP-20","footprint":"HTSSOP-20-1EP_4.4x6.5mm_P0.65mm","pins":21,"footprint_status":"generated","params":{"vin_min_v":4.5,"vin_max_v":60,"rds_on_mohm":31,"ilim_max_a":6},"datasheet_url":"https://www.ti.com/lit/ds/symlink/tps2663.pdf","lcsc":"C2866319","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C2866319"}},{"id":"ti.lm5145","mpn":"LM5145RGYR","manufacturer":"Texas Instruments","category":"buck_controller","description":"6-75 V synchronous buck controller, VQFN-20","footprint":"VQFN-20-1EP_3.5x4.5mm_P0.5mm","pins":21,"footprint_status":"generated","params":{"vin_min_v":6,"vin_max_v":75,"fsw_max_khz":1000},"datasheet_url":"https://www.ti.com/lit/ds/symlink/lm5145.pdf","lcsc":"C485912","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C485912"}},{"id":"bourns.css2h2512rl500f","mpn":"CSS2H-2512R-L500F","manufacturer":"Bourns","category":"resistor_shunt","description":"0.5 mOhm 6 W 1% metal-strip current-sense resistor, 2512","footprint":"R_2512_6332Metric","pins":2,"footprint_status":"generated","params":{"resistance_mohm":0.5,"power_w":6,"tolerance_pct":1},"datasheet_url":"https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2304140030_BOURNS-CSS2H-2512R-L500F_C2073667.pdf","lcsc":"C2073667","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C2073667"},"alternatives":[{"mpn":"CSS2H-2512R-L500FE","lcsc":"C1848841","note":"same part, alternate packaging suffix"}]},{"id":"bourns.css2h2512r1l00f","mpn":"CSS2H-2512R-1L00F","manufacturer":"Bourns","category":"resistor_shunt","description":"1 mOhm 5 W 1% metal-strip current-sense resistor, 2512","footprint":"R_2512_6332Metric","pins":2,"footprint_status":"generated","params":{"resistance_mohm":1,"power_w":5,"tolerance_pct":1},"datasheet_url":"https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2401221722_BOURNS-CSS2H-2512R-1L00F_C4175647.pdf","lcsc":"C4175647","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C4175647"}},{"id":"ti.lm5050-1","mpn":"LM5050MK-1/NOPB","manufacturer":"Texas Instruments","category":"ideal_diode_controller","description":"5-75 V high-side OR-ing FET controller, TSOT-23-6","footprint":"TSOT-23-6","pins":6,"footprint_status":"generated","params":{"vin_min_v":5,"vin_max_v":75},"datasheet_url":"https://www.ti.com/lit/ds/symlink/lm5050-1.pdf","lcsc":"C129323","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C129323"}},{"id":"ti.tps7a16","mpn":"TPS7A1601DGNR","manufacturer":"Texas Instruments","category":"ldo","description":"60 V 100 mA 5 uA-Iq adjustable LDO, MSOP-8 EP","footprint":"MSOP-8-1EP_3x3mm_P0.65mm_EP1.68x1.88mm","pins":8,"footprint_status":"generated","params":{"vin_max_v":60,"iout_ma":100,"iq_ua":5,"vout_v":[1.193,18.5]},"datasheet_url":"https://www.ti.com/lit/ds/symlink/tps7a16.pdf","lcsc":"C55770","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C55770","note":"Listed, but LCSC stock was 0 on the check date."}},{"id":"molex.43045-0412","mpn":"43045-0412","manufacturer":"Molex","category":"connector","description":"Micro-Fit 3.0 header, 2x2, right angle, through hole","footprint":"Molex_Micro-Fit_3.0_43045-0412_2x02_P3.00mm_Horizontal","pins":4,"footprint_status":"generated","params":{"pitch_mm":3,"positions":4,"current_a":8.5,"voltage_v":600},"datasheet_url":"https://www.molex.com/en-us/products/part-detail/430450412","lcsc":"C277721","jlc_library":"extended","aliases":["430450412","0430450412","Molex 43045-0412"],"verification":{"status":"verified","source":"JLCPCB/LCSC listing C277721 (430450412)"},"alternatives":[{"mpn":"C3030WV-2x2P","lcsc":"C239454","note":"CJT Micro-Fit-compatible 2x2 RA clone"}]},{"id":"ti.lmr38020","mpn":"LMR38020SDDAR","manufacturer":"Texas Instruments","category":"buck_converter","description":"4.2-80 V 2 A synchronous buck converter, SO-8 PowerPAD","footprint":"TI_SO-8-1EP_3.9x4.9mm_P1.27mm_EP2.41x3.1mm","pins":8,"footprint_status":"generated","params":{"vin_min_v":4.2,"vin_max_v":80,"iout_a":2,"fsw_max_khz":2200},"datasheet_url":"https://www.ti.com/lit/ds/symlink/lmr38020.pdf","lcsc":"C3192337","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C3192337"}},{"id":"bourns.srp1265a-100m","mpn":"SRP1265A-100M","manufacturer":"Bourns","category":"inductor_power","description":"10 uH 10 A shielded molded power inductor, 13.5x12.5","footprint":"L_Bourns_SRP1265A","pins":2,"footprint_status":"placeholder","params":{"inductance_uh":10,"isat_a":15.5,"irms_a":10,"dcr_mohm":16.5},"datasheet_url":"https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2202281730_BOURNS-SRP1265A-100M_C840531.pdf","lcsc":"C840531","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C840531","note":"Listed, but LCSC stock was 0 on the check date."},"alternatives":[{"mpn":"MDA1365-100M","lcsc":"C2847583","note":"KOHERelec 10 uH 13 A, 13.5x12.6 \u2014 check pad fit"},{"mpn":"FAUL1365-100MT","lcsc":"C5298351","note":"Changjiang 10 uH 15 A, 13.5x12.6"}]},{"id":"amass.xt30pb22m","mpn":"XT30PB(2+2)-M","manufacturer":"Changzhou Amass","category":"connector","description":"XT30 2+2 (power + signal pair) male, right angle PCB","footprint":"AMASS_XT30PB_2+2_-M_Horizontal","pins":4,"footprint_status":"generated","params":{"power_current_a":15,"positions":4},"datasheet_url":"https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2304140030_Changzhou-Amass-Elec-XT30PB-2-2-M_C3040128.pdf","lcsc":"C3040128","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C3040128","note":"Signal-pair pin order vs the RobStride harness is still unverified (mechanical, not sourcing)."}},{"id":"amass.xt60pt-m","mpn":"XT60PT-M","manufacturer":"Changzhou Amass","category":"connector","description":"XT60 male, vertical PCB mount","footprint":"AMASS_XT60PT-M_Vertical","pins":2,"footprint_status":"placeholder","params":{"current_a":30,"positions":2},"datasheet_url":"https://www.lcsc.com/product-detail/C54431743.html","lcsc":"C54431743","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C54431743 (stocked); older listing C5155958 has no stock"}},{"id":"amass.xt30pw-m","mpn":"XT30PW-M","manufacturer":"Changzhou Amass","category":"connector","description":"XT30 male, right angle PCB","footprint":"AMASS_XT30PW-M_Horizontal","pins":2,"footprint_status":"generated","params":{"current_a":15,"positions":2},"datasheet_url":"https://www.lcsc.com/product-detail/C53373776.html","lcsc":"C53373776","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C53373776","note":"Real listing but zero LCSC stock on the check date; widely stocked elsewhere (AliExpress/Amass direct). JLC consigned entries C9900183122 exist."}},{"id":"microchip.lan8742a","mpn":"LAN8742A-CZ-TR","manufacturer":"Microchip","category":"ethernet_phy","description":"10/100 Ethernet PHY, RMII, QFN-24 4x4","footprint":"QFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm","pins":25,"footprint_status":"generated","params":{"vdd_v":[1.8,3.3],"interfaces":["RMII","10BASE-T","100BASE-TX"],"temp_c":[0,70]},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_2011201138_Microchip-Tech-LAN8742A-CZ-TR_C621424.pdf","lcsc":"C621424","jlc_library":"extended","bom_lcsc":"C621066","verification":{"status":"corrected","source":"LCSC product detail C621066 + JLCPCB listing C621424","note":"BOM's C621066 is a UNI-ROYAL 1210 8.2 Ohm resistor. LAN8742A-CZ-TR is C621424. Commercial temp (0-70 C) \u2014 LAN8742AI-CZ-TR (C621425) is the industrial-range drop-in."},"alternatives":[{"mpn":"LAN8742AI-CZ-TR","lcsc":"C621425","note":"-40..85 C drop-in"}]},{"id":"winbond.w25q128jvsiq","mpn":"W25Q128JVSIQ","manufacturer":"Winbond","category":"flash","description":"128 Mbit SPI/QSPI NOR flash, SOIC-8 208 mil","footprint":"SOIC-8_5.23x5.23mm_P1.27mm","pins":8,"footprint_status":"generated","params":{"density_mbit":128,"fmax_mhz":133,"vcc_v":[2.7,3.6],"interfaces":["SPI","QSPI"]},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_1811142111_Winbond-Elec-W25Q128JVSIQ_C97521.pdf","lcsc":"C97521","jlc_library":"basic","verification":{"status":"verified","source":"LCSC product detail C97521; JLCPCB basic library"}},{"id":"generic.smcj58a","mpn":"SMCJ58A","manufacturer":"multi-source","category":"tvs","description":"58 V standoff 1.5 kW unidirectional TVS, SMC (DO-214AB)","footprint":"D_SMC","pins":2,"footprint_status":"generated","params":{"vrwm_v":58,"vbr_min_v":64.4,"vc_v":93.6,"ppk_w":1500},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_2304140030_Diodes-Incorporated-SMCJ58A-13-F_C135180.pdf","lcsc":"C135180","jlc_library":"extended","aliases":["SMCJ58A-13-F"],"verification":{"status":"verified","source":"JLCPCB/LCSC listing C135180 (Diodes Inc SMCJ58A-13-F); budget sources C151899 / C10474"},"alternatives":[{"mpn":"SMCJ58A (BORN)","lcsc":"C151899","note":"cheaper second source"},{"mpn":"SMCJ58A (RUILON)","lcsc":"C10474","note":"cheaper second source"}]},{"id":"infineon.bsz440n10ns3g","mpn":"BSZ440N10NS3G","manufacturer":"Infineon","category":"mosfet","description":"100 V 44 mOhm N-channel OptiMOS 3, TSDSON-8 (S3O8 3.3x3.3)","footprint":"Infineon_PG-TSDSON-8","pins":9,"pads":3,"footprint_status":"generated","params":{"vds_v":100,"id_a":18,"rds_on_mohm":44,"vgs_th_v":3.5,"pd_w":29,"channel":"N"},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_2304140030_Infineon-Technologies-BSZ440N10NS3G_C476115.pdf","lcsc":"C476115","jlc_library":"extended","aliases":["BSZ440N10NS3GATMA1"],"verification":{"status":"verified","source":"JLCPCB/LCSC listing C476115"}},{"id":"ti.ucc27511a","mpn":"UCC27511ADBVR","manufacturer":"Texas Instruments","category":"gate_driver","description":"4 A/8 A single-channel low-side gate driver, SOT-23-6","footprint":"SOT-23-6","pins":6,"footprint_status":"generated","params":{"vdd_v":[4.5,18],"source_a":4,"sink_a":8},"datasheet_url":"https://www.ti.com/lit/ds/symlink/ucc27511a.pdf","lcsc":"C2676982","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C2676982"}},{"id":"vishay.ss10ph10","mpn":"SS10PH10-M3/86A","manufacturer":"Vishay","category":"diode_schottky","description":"100 V 10 A high-voltage Schottky rectifier, TO-277A (SMPC)","footprint":"TO-277A","pins":3,"pads":2,"footprint_status":"generated","params":{"vrrm_v":100,"if_a":10,"vf_v":0.8,"ifsm_a":200},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_2007091711_Vishay-Intertech-SS10PH10-M3-86A_C554009.pdf","lcsc":"C554009","jlc_library":"extended","aliases":["SS10P10-M3/86A","SS10PH10"],"bom_mpn":"SS10P10-M3/86A","verification":{"status":"corrected","source":"JLCPCB/LCSC search + distributor search","note":"SS10P10-M3/86A has no vendor listing; the Vishay 100 V 10 A TO-277A Schottky is SS10PH10-M3/86A (C554009). V10P10-M3/86A (TMBS, lower Vf, higher leakage) is the other option."},"alternatives":[{"mpn":"V10P10-M3/86A","lcsc":"C50188","note":"Vishay TMBS 100 V 10 A TO-277A, Vf 0.68 V, same footprint"}]},{"id":"panasonic.25svpf100m","mpn":"25SVPF100M","manufacturer":"Panasonic","category":"capacitor_polymer","description":"100 uF 25 V OS-CON polymer, 8 mm can (SMD D8xL6.9)","footprint":"CP_Elec_6.3x7.7","pins":2,"footprint_status":"placeholder","params":{"capacitance_uf":100,"voltage_v":25,"esr_mohm":24,"ripple_a":3.2,"can_mm":[8,6.9]},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_2304140030_PANASONIC-25SVPF100M_C136279.pdf","lcsc":"C136279","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C136279","note":"Footprint mismatch: 25SVPF100M is an 8 mm can (D8xL6.9) but the BOM maps it to CP_Elec_6.3x7.7. Use CP_Elec_8x6.7 or pick a 6.3 mm part."}},{"id":"bourns.srp6540-150m","mpn":"SRP6540-150M","manufacturer":"Bourns","category":"inductor_power","description":"15 uH 2.9 A shielded power inductor, 7.2x6.5","footprint":"L_Bourns_SRP6540","pins":2,"footprint_status":"placeholder","params":{"inductance_uh":15,"isat_a":2.9,"irms_a":2.9,"dcr_mohm":121},"datasheet_url":"https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2309040926_BOURNS-SRP6540-150M_C5369215.pdf","lcsc":"C5369215","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C5369215","note":"Listed, but LCSC stock was 0 on the check date. Rated 2.9 A against the BOM's 3 A label."}},{"id":"ti.tps62a02","mpn":"TPS62A02DRLR","manufacturer":"Texas Instruments","category":"buck_converter","description":"2.5-5.5 V 2 A synchronous buck, SOT-563","footprint":"SOT-563","pins":6,"footprint_status":"placeholder","params":{"vin_min_v":2.5,"vin_max_v":5.5,"iout_a":2,"fsw_khz":2400},"datasheet_url":"https://www.ti.com/lit/ds/symlink/tps62a02.pdf","lcsc":"C5350187","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C5350187"}},{"id":"ti.lm4040-2v5","mpn":"LM4040DIM3-2.5/NOPB","manufacturer":"Texas Instruments","category":"voltage_reference","description":"2.5 V 1% shunt voltage reference, SOT-23","footprint":"SOT-23","pins":3,"footprint_status":"generated","params":{"vref_v":2.5,"tolerance_pct":1,"iz_max_ma":15},"datasheet_url":"https://www.ti.com/lit/ds/symlink/lm4040-n.pdf","lcsc":"C205892","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C205892","note":"Listed, but LCSC stock was 0 on the check date."},"alternatives":[{"mpn":"LM4040AIM3-2.5/NOPB","lcsc":"C139302","note":"0.1% grade, same SOT-23 pinout (also out of LCSC stock on the check date)"}]},{"id":"yxc.x322525mob4si","mpn":"X322525MOB4SI","manufacturer":"YXC","category":"crystal","description":"25 MHz crystal, 3225 4-pad, 12 pF (vendor listing), 10 ppm","footprint":"Crystal_SMD_3225-4Pin_3.2x2.5mm","pins":4,"footprint_status":"generated","params":{"frequency_mhz":25,"load_pf":12,"tolerance_ppm":10},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_2403291504_YXC-Crystal-Oscillators-X322525MOB4SI_C9006.pdf","lcsc":"C9006","jlc_library":"basic","verification":{"status":"verified","source":"JLCPCB basic library C9006","note":"LCSC lists a 12 pF load, but the BOM says 20 pF. Re-check the load caps (C713/C714/C725/C726 = 12 pF)."}},{"id":"yxc.x322516mlb4si","mpn":"X322516MLB4SI","manufacturer":"YXC","category":"crystal","description":"16 MHz crystal, 3225 4-pad, 9 pF, 10 ppm","footprint":"Crystal_SMD_3225-4Pin_3.2x2.5mm","pins":4,"footprint_status":"generated","params":{"frequency_mhz":16,"load_pf":9,"tolerance_ppm":10},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_2403291504_YXC-Crystal-Oscillators-X322516MLB4SI_C13738.pdf","lcsc":"C13738","jlc_library":"basic","verification":{"status":"verified","source":"JLCPCB basic library C13738"}},{"id":"jst.sm04b-ghs-tb","mpn":"SM04B-GHS-TB(LF)(SN)","manufacturer":"JST","category":"connector","description":"GH 1.25 mm 4-pos right-angle SMD header","footprint":"JST_GH_SM04B-GHS-TB_1x04-1MP_P1.25mm_Horizontal","pins":4,"footprint_status":"generated","params":{"pitch_mm":1.25,"positions":4,"current_a":1},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_2304140030_JST-SM04B-GHS-TB-LF-SN_C189895.pdf","lcsc":"C189895","jlc_library":"extended","aliases":["SM04B-GHS-TB","JST SM04B-GHS-TB"],"verification":{"status":"verified","source":"JLCPCB/LCSC listing C189895 (the bare SM04B-GHS-TB listing C20584968 has no stock)"},"alternatives":[{"mpn":"XY-SM04B-GHS-TB","lcsc":"C51940118","note":"XYECONN GH-compatible clone, deep stock"}]},{"id":"jst.sm05b-ghs-tb","mpn":"SM05B-GHS-TB(LF)(SN)","manufacturer":"JST","category":"connector","description":"GH 1.25 mm 5-pos right-angle SMD header","footprint":"JST_GH_SM05B-GHS-TB_1x05-1MP_P1.25mm_Horizontal","pins":5,"footprint_status":"generated","params":{"pitch_mm":1.25,"positions":5,"current_a":1},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_2304140030_JST-SM05B-GHS-TB-LF-SN_C189896.pdf","lcsc":"C189896","jlc_library":"extended","aliases":["SM05B-GHS-TB","JST SM05B-GHS-TB"],"verification":{"status":"verified","source":"JLCPCB/LCSC listing C189896"}},{"id":"st.usblc6-2sc6","mpn":"USBLC6-2SC6","manufacturer":"STMicroelectronics","category":"esd","description":"2-line USB ESD protection + VBUS clamp, SOT-23-6","footprint":"SOT-23-6","pins":6,"footprint_status":"generated","params":{"vrwm_v":5.25,"vc_v":17,"c_pf":3.5},"datasheet_url":"https://www.st.com/resource/en/datasheet/usblc6-2.pdf","lcsc":"C7519","jlc_library":"extended","verification":{"status":"verified","source":"LCSC product detail C7519"},"alternatives":[{"mpn":"USBLC6-2SC6 (UMW)","lcsc":"C2687116","note":"pin-compatible second source"}]},{"id":"ti.lmv331","mpn":"LMV331IDBVR","manufacturer":"Texas Instruments","category":"comparator","description":"Single low-voltage open-drain comparator, SOT-23-5","footprint":"SOT-23-5","pins":5,"footprint_status":"generated","params":{"vcc_v":[2.7,5.5],"output":"open-drain"},"datasheet_url":"https://www.ti.com/lit/ds/symlink/lmv331.pdf","lcsc":"C34731","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C34731"}},{"id":"generic.mmbt3904","mpn":"MMBT3904","manufacturer":"multi-source","category":"bjt","description":"40 V 200 mA NPN, SOT-23 (used as remote temperature-sense diode)","footprint":"SOT-23","pins":3,"footprint_status":"generated","params":{"vceo_v":40,"ic_ma":200,"type":"NPN"},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_2207041900_Jiangsu-Changjing-Electronics-Technology-Co---Ltd--MMBT3904-RANGE-100-300_C20526.pdf","lcsc":"C20526","jlc_library":"basic","verification":{"status":"verified","source":"JLCPCB basic library C20526 (CJ MMBT3904)"}},{"id":"murata.dfe252012f-1r0m","mpn":"DFE252012F-1R0M=P2","manufacturer":"Murata","category":"inductor_power","description":"1 uH 3.3 A metal-alloy power inductor, 1008 (2520)","footprint":"L_1008_2520Metric","pins":2,"footprint_status":"placeholder","params":{"inductance_uh":1,"isat_a":4.7,"irms_a":3.3,"dcr_mohm":40},"datasheet_url":"https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/1912111437_Murata-Electronics-DFE252012F-1R0M-P2_C435392.pdf","lcsc":"C435392","jlc_library":"extended","aliases":["DFE252012F-1R0M"],"bom_mpn":"DFE252012F-1R0M","verification":{"status":"verified","source":"JLCPCB/LCSC listing C435392","note":"The orderable code carries the =P2 packaging suffix."}},{"id":"murata.ncp18xh103f03rb","mpn":"NCP18XH103F03RB","manufacturer":"Murata","category":"thermistor_ntc","description":"10 kOhm 1% NTC, B25/50 3380 K, 0603","footprint":"R_0603_1608Metric","pins":2,"footprint_status":"generated","params":{"r25_ohm":10000,"tolerance_pct":1,"beta_k":3380},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_1810311113_Murata-Electronics-NCP18XH103F03RB_C13564.pdf","lcsc":"C13564","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C13564"}},{"id":"ti.sn74lvc1g08","mpn":"SN74LVC1G08DBVR","manufacturer":"Texas Instruments","category":"logic","description":"Single 2-input AND gate, SOT-23-5","footprint":"SOT-23-5","pins":5,"footprint_status":"generated","params":{"vcc_v":[1.65,5.5],"function":"AND2"},"datasheet_url":"https://www.ti.com/lit/ds/symlink/sn74lvc1g08.pdf","lcsc":"C7666","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C7666"}},{"id":"ti.sn74lvc1g32","mpn":"SN74LVC1G32DBVR","manufacturer":"Texas Instruments","category":"logic","description":"Single 2-input OR gate, SOT-23-5","footprint":"SOT-23-5","pins":5,"footprint_status":"generated","params":{"vcc_v":[1.65,5.5],"function":"OR2"},"datasheet_url":"https://www.ti.com/lit/ds/symlink/sn74lvc1g32.pdf","lcsc":"C10096","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C10096"}},{"id":"murata.blm18pg601sn1d","mpn":"BLM18PG601SN1D","manufacturer":"Murata","category":"ferrite_bead","description":"600 Ohm @ 100 MHz ferrite bead, 0603","footprint":"L_0603_1608Metric","pins":2,"footprint_status":"generated","params":{"impedance_ohm_100mhz":600,"irated_ma":500},"datasheet_url":"https://www.murata.com/en-us/products/productdetail?partno=BLM18PG601SN1%23","lcsc":null,"jlc_library":null,"verification":{"status":"flagged","source":"JLCPCB/LCSC search","note":"Real Murata part, but LCSC carries it only as a JLC consigned placeholder (C9900168231) with no stock. Substitute a stocked 600 Ohm 0603 bead."},"alternatives":[{"mpn":"BLM18AG601SN1D","lcsc":"C19330","note":"Murata 600 Ohm 0603, lower current rating than the PG series \u2014 check load current"},{"mpn":"GZ1608D601TF","lcsc":"C1002","note":"Sunlord 600 Ohm 0603, JLC basic"}]},{"id":"infineon.ipt015n10n5","mpn":"IPT015N10N5","manufacturer":"Infineon","category":"mosfet","description":"100 V 1.5 mOhm N-channel OptiMOS 5 power MOSFET, TOLL","footprint":"Infineon_PG-HSOF-8-1_TOLL","pins":9,"pads":3,"footprint_status":"generated","params":{"vds_v":100,"id_a":300,"rds_on_mohm":1.5,"vgs_th_v":3.8,"qg_nc":211,"pd_w":375,"channel":"N"},"datasheet_url":"https://www.lcsc.com/datasheet/lcsc_datasheet_1810010717_Infineon-Technologies-IPT015N10N5_C108964.pdf","lcsc":"C108964","jlc_library":"extended","aliases":["IPT015N10N5ATMA1"],"bom_mpn":"IPT015N10N5ATMA1","verification":{"status":"corrected","source":"LCSC product detail C108964; JLCPCB listing C6419630 (IPT015N10N5ATMA1)","note":"Real part. The ATMA1 reel code (C6419630) has no stock; the base code IPT015N10N5 (C108964, the LCSC# the BOM noted) is stocked. Order C108964."},"alternatives":[{"mpn":"IPT012N08N5","lcsc":"C531199","note":"80 V 1.2 mOhm TOLL \u2014 less Vds headroom"}]},{"id":"infineon.bsc027n10ns5","mpn":"BSC027N10NS5","manufacturer":"Infineon","category":"mosfet","description":"100 V 2.7 mOhm N-channel OptiMOS 5, SuperSO8 (TDSON-8 5x6)","footprint":"Infineon_PG-TDSON-8","pins":9,"pads":3,"footprint_status":"generated","params":{"vds_v":100,"rds_on_mohm":2.7,"channel":"N"},"datasheet_url":"https://www.lcsc.com/product-detail/C534315.html","lcsc":"C534315","jlc_library":"extended","aliases":["BSC027N10NS5ATMA1"],"bom_mpn":"BSC027N10NS5ATMA1","verification":{"status":"corrected","source":"LCSC product detail C534315; JLCPCB listing C7086601 (ATMA1)","note":"The ATMA1 reel code (C7086601) has no stock; BSC027N10NS5 (C534315, the BOM's noted LCSC#) is stocked, but only ~785 pcs (7 per board)."},"alternatives":[{"mpn":"BSC026N08NS5ATMA1","lcsc":"C5955453","note":"80 V 2.6 mOhm, same footprint, deep stock \u2014 only if 80 V is enough"}]},{"id":"ti.tps7a40","mpn":"TPS7A4001DGNR","manufacturer":"Texas Instruments","category":"ldo","description":"100 V 50 mA adjustable LDO, MSOP-8 PowerPAD","footprint":"MSOP-8-1EP_3x3mm_P0.65mm_EP1.68x1.88mm","pins":8,"footprint_status":"generated","params":{"vin_max_v":100,"iout_ma":50,"vout_v":[1.175,90]},"datasheet_url":"https://www.ti.com/lit/ds/symlink/tps7a40.pdf","lcsc":"C55006","jlc_library":"extended","verification":{"status":"verified","source":"LCSC product detail C55006"}},{"id":"bourns.css2h2512r2l00f","mpn":"CSS2H-2512R-2L00F","manufacturer":"Bourns","category":"resistor_shunt","description":"2 mOhm 2512 metal-strip current-sense resistor (as specified by BOM)","footprint":"R_2512_6332Metric","pins":2,"footprint_status":"generated","params":{"resistance_mohm":2,"tolerance_pct":1},"datasheet_url":"https://www.bourns.com/docs/product-datasheets/css2h-2512.pdf","lcsc":null,"jlc_library":null,"verification":{"status":"flagged","source":"JLCPCB/LCSC search (no hit); distributor search","note":"No listing found for the R-suffix 2 mOhm part. A distributor lists CSS2H-2512K-2L00F (2 mOhm 5 W 1%); confirm the suffix against the Bourns datasheet. Not on LCSC either way."},"alternatives":[{"mpn":"CSS2H-2512K-2L00F","note":"Bourns 2 mOhm 5 W (distributor-listed, not on LCSC)"},{"mpn":"LR2512-22R002F4","lcsc":"C108929","note":"RALEC 2 mOhm 2512 on LCSC \u2014 check its power rating against 20 A"}]},{"id":"amass.xt90pb-m","mpn":"XT90PB-M.G.Y","manufacturer":"Changzhou Amass","category":"connector","description":"XT90 male PCB connector (PB board-mount variant)","footprint":"AMASS_XT90PB-M_Vertical","pins":2,"footprint_status":"placeholder","params":{"current_a":45,"positions":2},"datasheet_url":"https://www.lcsc.com/product-detail/C3040688.html","lcsc":"C3040688","jlc_library":"extended","aliases":["XT90PB-M","AMASS XT90PB-M"],"bom_mpn":"AMASS XT90PB-M (vertical PCB; variant name UNVERIFIED)","verification":{"status":"corrected","source":"JLCPCB/LCSC listing C3040688","note":"The orderable code is XT90PB-M.G.Y (C3040688, stocked); bare 'XT90PB-M' is only a JLC consignment placeholder. Confirm PB (vs PW) gives the vertical orientation against the Amass drawing."},"alternatives":[{"mpn":"XT90PW-M.G.Y","lcsc":"C3040689","note":"PW board-mount variant (other orientation)"}]},{"id":"vishay.wslp1206r0100fea","mpn":"WSLP1206R0100FEA","manufacturer":"Vishay Dale","category":"resistor_shunt","description":"10 mOhm 1% power metal-strip current-sense resistor, 1206","footprint":"R_1206_3216Metric","pins":2,"footprint_status":"generated","params":{"resistance_mohm":10,"tolerance_pct":1,"power_w":1},"datasheet_url":"https://www.vishay.com/docs/30175/wslp.pdf","lcsc":"C844910","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C844910"}},{"id":"jst.sm02b-ghs-tb","mpn":"SM02B-GHS-TB(LF)(SN)","manufacturer":"JST","category":"connector","description":"GH 1.25 mm 2-pos right-angle SMD header","footprint":"JST_GH_SM02B-GHS-TB_1x02-1MP_P1.25mm_Horizontal","pins":2,"footprint_status":"generated","params":{"pitch_mm":1.25,"positions":2,"current_a":1},"datasheet_url":"https://www.lcsc.com/product-detail/C189893.html","lcsc":"C189893","jlc_library":"extended","aliases":["SM02B-GHS-TB","JST SM02B-GHS-TB"],"verification":{"status":"verified","source":"JLCPCB/LCSC listing C189893"}},{"id":"ti.sn74aup1g74","mpn":"SN74AUP1G74DCUR","manufacturer":"Texas Instruments","category":"logic","description":"Single D flip-flop with preset/clear, low power, VSSOP-8 (DCU)","footprint":"VSSOP-8_2.3x2mm_P0.5mm","pins":8,"footprint_status":"generated","params":{"vcc_v":[0.8,3.6],"function":"DFF with PRE/CLR"},"datasheet_url":"https://www.ti.com/lit/ds/symlink/sn74aup1g74.pdf","lcsc":"C140267","jlc_library":"extended","verification":{"status":"verified","source":"JLCPCB/LCSC listing C140267"}},{"id":"class.resistor-chip","mpn":null,"manufacturer":"multi-source","category":"passive_class","description":"Thick-film chip resistor class (0603/0805/1206, 1%). Resolve each value via live search; JLC basic UNI-ROYAL 0603WAF series is the default.","footprint":"R_0603_1608Metric","pins":2,"footprint_status":"generated","params":{"series":"E96","tolerance_pct":1,"example":"0603WAF1002T5E (10k, C25804, basic)"},"datasheet_url":null,"lcsc":null,"jlc_library":"basic","verification":{"status":"verified","source":"JLCPCB basic library (UNI-ROYAL 0603WAF)"}},{"id":"class.capacitor-mlcc","mpn":null,"manufacturer":"multi-source","category":"passive_class","description":"MLCC class (X7R/X5R/C0G). Resolve value + voltage + package via live search. Check the voltage rating; 100 V 1210 parts are extended-library.","footprint":"C_0603_1608Metric","pins":2,"footprint_status":"generated","params":{"example":"CC0603KRX7R9BB104 (100nF 50V, C14663, basic)"},"datasheet_url":null,"lcsc":null,"jlc_library":"basic","verification":{"status":"verified","source":"JLCPCB basic library (YAGEO CC0603)"}},{"id":"class.led-0603","mpn":null,"manufacturer":"multi-source","category":"passive_class","description":"0603 indicator LEDs (green/red). JLC basic red KT-0603R (C2286); pick a stocked green 0603 by live search.","footprint":"LED_0603_1608Metric","pins":2,"footprint_status":"generated","params":{},"datasheet_url":null,"lcsc":null,"jlc_library":"basic","verification":{"status":"verified","source":"JLCPCB basic library"}}]}`;
60933
+ }
60934
+ });
60935
+
60936
+ // src/tools/part-stock.ts
60937
+ function db() {
60938
+ if (!dbCache) dbCache = JSON.parse(ORDERABLE_CATALOG_JSON);
60939
+ return dbCache;
60940
+ }
60941
+ function orderableParts() {
60942
+ return db().parts;
60943
+ }
60944
+ function orderableCheckedDate() {
60945
+ return db().checked;
60946
+ }
60947
+ function normMpn(s) {
60948
+ return s.toUpperCase().replace(/[\s\-_/().,+=]/g, "");
60949
+ }
60950
+ function lookupOrderable(query) {
60951
+ const q2 = query.trim();
60952
+ if (!q2) return null;
60953
+ const parts = orderableParts();
60954
+ if (LCSC_RE.test(q2)) {
60955
+ const c = q2.toUpperCase();
60956
+ return parts.find((p) => p.lcsc === c || p.bom_lcsc === c) ?? null;
60957
+ }
60958
+ const keys = [normMpn(q2)];
60959
+ const lastTok = q2.split(/\s+/).pop();
60960
+ if (lastTok && lastTok !== q2) keys.push(normMpn(lastTok));
60961
+ const noNote = q2.replace(/\s+\([^)]*\)\s*$/, "");
60962
+ if (noNote !== q2) keys.push(normMpn(noNote));
60963
+ for (const key3 of keys) {
60964
+ const hit = parts.find((p) => {
60965
+ const names = [p.mpn, ...p.aliases ?? [], p.bom_mpn].filter(Boolean);
60966
+ return names.some((n) => normMpn(n) === key3);
60967
+ });
60968
+ if (hit) return hit;
60969
+ }
60970
+ return null;
60971
+ }
60972
+ function searchOrderable(query, limit = 5) {
60973
+ const exact = lookupOrderable(query);
60974
+ const toks = query.toLowerCase().split(/[\s,]+/).filter((t2) => t2.length > 1);
60975
+ if (toks.length === 0) return exact ? [exact] : [];
60976
+ const scored = orderableParts().map((p) => {
60977
+ const hay = [p.mpn ?? "", ...p.aliases ?? [], p.manufacturer, p.category, p.description, p.footprint].join(" ").toLowerCase();
60978
+ const score = toks.reduce((s, t2) => s + (hay.includes(t2) ? 1 : 0), 0);
60979
+ return { p, score: p === exact ? 1e6 : score };
60980
+ }).filter((x) => x.score > 0).sort((a, b) => b.score - a.score);
60981
+ return scored.slice(0, limit).map((x) => x.p);
60982
+ }
60983
+ function isPlaceholderRow(c) {
60984
+ return /^C99\d{8,}$/.test(c.componentCode) || c.componentBrandEn === "JLCPCB Assembly";
60985
+ }
60986
+ function unitPriceAt(breaks, qty) {
60987
+ if (breaks.length === 0) return null;
60988
+ const sorted = [...breaks].sort((a, b) => a.qty - b.qty);
60989
+ let price = sorted[0].unit_usd;
60990
+ for (const b of sorted) if (b.qty <= qty) price = b.unit_usd;
60991
+ return price;
60992
+ }
60993
+ function passiveKeyword(spec) {
60994
+ const m = spec.trim().match(/^(RES|CAP|IND|LED)\s+(.+)$/i);
60995
+ if (!m) return null;
60996
+ const kind = m[1].toUpperCase();
60997
+ const rest = m[2].trim();
60998
+ const toks = rest.split(/\s+/);
60999
+ const pkg = toks.find((t2) => /^(0201|0402|0603|0805|1206|1210|1812|2010|2512)$/.test(t2)) ?? "";
61000
+ if (kind === "RES") {
61001
+ let v = toks[0];
61002
+ if (/^\d+(\.\d+)?$/.test(v)) v = `${v}\u03A9`;
61003
+ else if (/^\d+(\.\d+)?[kKmM]$/.test(v)) v = `${v.slice(0, -1)}${v.slice(-1) === "k" || v.slice(-1) === "K" ? "k" : "M"}\u03A9`;
61004
+ return { keyword: `${v} ${pkg}`.trim(), mustContain: [v] };
61005
+ }
61006
+ if (kind === "CAP") {
61007
+ const v = toks[0];
61008
+ const volt = toks.find((t2) => /^\d+(\.\d+)?V$/i.test(t2));
61009
+ const kw = [v, volt, pkg].filter(Boolean).join(" ");
61010
+ return { keyword: kw, mustContain: [v, ...volt ? [volt.toUpperCase()] : []] };
61011
+ }
61012
+ if (kind === "LED") {
61013
+ const color = (toks.find((t2) => /green|red|blue|yellow|white|orange/i.test(t2)) ?? "").toLowerCase();
61014
+ const KNOWN = { green: "KT-0603G", red: "KT-0603R" };
61015
+ if (pkg === "0603" && KNOWN[color]) return { keyword: KNOWN[color], mustContain: [] };
61016
+ return { keyword: `${color} LED ${pkg}`.trim(), mustContain: [] };
61017
+ }
61018
+ return { keyword: rest, mustContain: [] };
61019
+ }
61020
+ function describeHas(c, tok) {
61021
+ const hay = `${c.describe ?? ""} ${c.componentSpecificationEn ?? ""}`.toLowerCase().replace(/µ/g, "u");
61022
+ const t2 = tok.toLowerCase().replace(/µ/g, "u");
61023
+ const i = hay.indexOf(t2);
61024
+ if (i < 0) return false;
61025
+ const before = i === 0 ? " " : hay[i - 1];
61026
+ return !/[\d.]/.test(before);
61027
+ }
61028
+ function extCost(q2, qtyTotal, basis) {
61029
+ const own = basis === "lcsc" ? q2.price_breaks : q2.jlc_price_breaks;
61030
+ const ladder = own.length ? own : basis === "lcsc" ? q2.jlc_price_breaks : q2.price_breaks;
61031
+ const buy = basis === "lcsc" && own.length ? Math.max(qtyTotal, q2.min_order_qty ?? 1) : qtyTotal;
61032
+ const unit = unitPriceAt(ladder, buy);
61033
+ return unit === null ? null : Math.round(unit * buy * 1e4) / 1e4;
61034
+ }
61035
+ async function annotateBomStock(client, lines, builds = [5, 100]) {
61036
+ const out = [];
61037
+ for (const line of lines) {
61038
+ const need = line.qty * Math.max(...builds);
61039
+ const quote = await client.check(line.part, need);
61040
+ const flags = [];
61041
+ if (!quote.found) flags.push("missing");
61042
+ const stock = Math.max(quote.stock_lcsc ?? -1, quote.stock_jlc ?? -1);
61043
+ if (quote.found && quote.source !== "offline") {
61044
+ if (stock <= 0) flags.push("out_of_stock");
61045
+ else if (stock < need) flags.push("low_stock");
61046
+ }
61047
+ if (quote.eol) flags.push("eol");
61048
+ if (quote.db?.verification.status === "flagged") flags.push("db_flagged");
61049
+ if (quote.db?.verification.status === "corrected") flags.push("db_corrected");
61050
+ if (quote.db?.footprint_status === "placeholder") flags.push("footprint_placeholder");
61051
+ if (line.noted_lcsc && quote.lcsc && line.noted_lcsc.toUpperCase() !== quote.lcsc) {
61052
+ flags.push("lcsc_mismatch");
61053
+ quote.warnings.push(`line notes LCSC ${line.noted_lcsc}, actual part is ${quote.lcsc}`);
61054
+ }
61055
+ const cost = { lcsc: {}, jlc_assembly: {} };
61056
+ for (const basis of BASES) {
61057
+ for (const b of builds) cost[basis][String(b)] = extCost(quote, line.qty * b, basis);
61058
+ }
61059
+ if (quote.price_breaks.length === 0 && quote.jlc_price_breaks.length === 0) flags.push("unpriced");
61060
+ out.push({ ...line, quote, cost, flags, alternatives: quote.db?.alternatives ?? [] });
61061
+ }
61062
+ const totals = { lcsc: {}, jlc_assembly: {} };
61063
+ for (const basis of BASES) {
61064
+ for (const b of builds) {
61065
+ let usd2 = 0;
61066
+ let unpriced = 0;
61067
+ for (const l of out) {
61068
+ const c = l.cost[basis][String(b)];
61069
+ if (c === null) unpriced++;
61070
+ else usd2 += c;
61071
+ }
61072
+ totals[basis][String(b)] = { usd: Math.round(usd2 * 100) / 100, unpriced_lines: unpriced };
61073
+ }
61074
+ }
61075
+ const SERIOUS = /* @__PURE__ */ new Set(["missing", "out_of_stock", "low_stock", "eol", "db_flagged", "db_corrected", "lcsc_mismatch"]);
61076
+ const flagged = out.filter((l) => l.flags.some((f) => SERIOUS.has(f))).map((l) => ({
61077
+ name: l.name,
61078
+ part: l.part,
61079
+ flags: l.flags,
61080
+ note: [l.quote.db?.verification.note, ...l.quote.warnings].filter(Boolean).join(" | ")
61081
+ }));
61082
+ return { builds, lines: out, totals, flagged, checked_at: (/* @__PURE__ */ new Date()).toISOString() };
61083
+ }
61084
+ function parseCsv(text) {
61085
+ const rows = [];
61086
+ let row = [];
61087
+ let field = "";
61088
+ let inQ = false;
61089
+ for (let i = 0; i < text.length; i++) {
61090
+ const ch = text[i];
61091
+ if (inQ) {
61092
+ if (ch === '"' && text[i + 1] === '"') {
61093
+ field += '"';
61094
+ i++;
61095
+ } else if (ch === '"') inQ = false;
61096
+ else field += ch;
61097
+ } else if (ch === '"') inQ = true;
61098
+ else if (ch === ",") {
61099
+ row.push(field);
61100
+ field = "";
61101
+ } else if (ch === "\n" || ch === "\r") {
61102
+ if (ch === "\r" && text[i + 1] === "\n") i++;
61103
+ row.push(field);
61104
+ rows.push(row);
61105
+ row = [];
61106
+ field = "";
61107
+ } else field += ch;
61108
+ }
61109
+ if (field !== "" || row.length) {
61110
+ row.push(field);
61111
+ rows.push(row);
61112
+ }
61113
+ return rows.filter((r) => r.some((c) => c.trim() !== ""));
61114
+ }
61115
+ function bomCsvToStockLines(csv2) {
61116
+ const rows = parseCsv(csv2);
61117
+ if (rows.length < 2) return [];
61118
+ const hdr = rows[0].map((h) => h.trim().toLowerCase());
61119
+ const col = (n) => hdr.indexOf(n);
61120
+ const iKind = col("kind");
61121
+ const iName = col("name");
61122
+ const iPn = col("example_pn") >= 0 ? col("example_pn") : col("mpn");
61123
+ const iQty = col("qty");
61124
+ const iNotes = col("notes");
61125
+ const lines = [];
61126
+ for (const r of rows.slice(1)) {
61127
+ if (iKind >= 0 && r[iKind]?.trim() === "manufactured") continue;
61128
+ const pn = (r[iPn] ?? "").trim();
61129
+ if (!pn || /footprint only/i.test(pn)) continue;
61130
+ const noted = (r[iNotes] ?? "").match(/\bLCSC\s+(C\d+)/i)?.[1] ?? null;
61131
+ lines.push({
61132
+ name: (r[iName] ?? pn).trim(),
61133
+ part: pn,
61134
+ qty: Number(r[iQty]) || 1,
61135
+ noted_lcsc: noted
61136
+ });
61137
+ }
61138
+ return lines;
61139
+ }
61140
+ function stockClient() {
61141
+ if (!sharedClient) sharedClient = new PartStockClient({ offline: process.env.VCAD_PARTS_OFFLINE === "1" });
61142
+ return sharedClient;
61143
+ }
61144
+ async function checkPartStock(args) {
61145
+ const qs = Array.isArray(args.queries) ? args.queries.map(String) : typeof args.query === "string" ? [args.query] : [];
61146
+ if (qs.length === 0) {
61147
+ return { content: [{ type: "text", text: JSON.stringify({ error: "pass `query` or `queries`" }) }], isError: true };
61148
+ }
61149
+ const need = typeof args.qty === "number" ? Math.max(1, Math.round(args.qty)) : 1;
61150
+ const client = stockClient();
61151
+ const results = [];
61152
+ for (const q2 of qs) results.push(await client.check(q2, need));
61153
+ const payload = { count: results.length, results, note: STOCK_NOTE };
61154
+ return { content: [{ type: "text", text: JSON.stringify(payload) }], structuredContent: payload };
61155
+ }
61156
+ function setBomLinesResolver(r) {
61157
+ bomResolver = r;
61158
+ }
61159
+ async function bomCheckStock(args, ctx) {
61160
+ let lines = null;
61161
+ if (typeof args.csv === "string") lines = bomCsvToStockLines(args.csv);
61162
+ else if (Array.isArray(args.lines)) {
61163
+ lines = args.lines.map((l) => ({
61164
+ name: String(l.name ?? l.part),
61165
+ part: String(l.part),
61166
+ qty: Number(l.qty) || 1,
61167
+ noted_lcsc: typeof l.noted_lcsc === "string" ? l.noted_lcsc : null
61168
+ }));
61169
+ } else if (typeof args.bom_id === "string") {
61170
+ lines = bomResolver ? bomResolver(args.bom_id, ctx) : null;
61171
+ if (!lines) {
61172
+ return { content: [{ type: "text", text: JSON.stringify({ error: `Unknown bom_id "${args.bom_id}"` }) }], isError: true };
61173
+ }
61174
+ }
61175
+ if (!lines || lines.length === 0) {
61176
+ return { content: [{ type: "text", text: JSON.stringify({ error: "pass bom_id, csv, or lines (no checkable lines found)" }) }], isError: true };
61177
+ }
61178
+ const builds = Array.isArray(args.builds) && args.builds.length ? args.builds.map((n) => Math.max(1, Math.round(Number(n)))).filter(Boolean) : [5, 100];
61179
+ const report = await annotateBomStock(stockClient(), lines, builds);
61180
+ const payload = { ...report, note: STOCK_NOTE };
61181
+ return { content: [{ type: "text", text: JSON.stringify(payload) }], structuredContent: payload };
61182
+ }
61183
+ var dbCache, LCSC_RE, JLC_SEARCH_URL, LCSC_DETAIL_URL, UA, PartStockClient, BASES, sharedClient, STOCK_NOTE, checkPartStockSchema, bomCheckStockSchema, bomResolver, toolDefs15;
61184
+ var init_part_stock = __esm({
61185
+ "src/tools/part-stock.ts"() {
61186
+ "use strict";
61187
+ init_orderable_catalog_generated();
61188
+ init_tool_def();
61189
+ dbCache = null;
61190
+ LCSC_RE = /^C\d{2,}$/i;
61191
+ JLC_SEARCH_URL = "https://jlcpcb.com/api/overseas-pcb-order/v1/shoppingCart/smtGood/selectSmtComponentList";
61192
+ LCSC_DETAIL_URL = "https://wmsc.lcsc.com/ftps/wm/product/detail?productCode=";
61193
+ UA = "vcad-mcp parts-stock (+https://vcad.io)";
61194
+ PartStockClient = class {
61195
+ fetchImpl;
61196
+ now;
61197
+ minIntervalMs;
61198
+ timeoutMs;
61199
+ ttlMs;
61200
+ offline;
61201
+ sleep;
61202
+ cache = /* @__PURE__ */ new Map();
61203
+ queue = Promise.resolve();
61204
+ lastRequestAt = -Infinity;
61205
+ /** Outbound request count (observability + tests). */
61206
+ requests = 0;
61207
+ constructor(opts = {}) {
61208
+ const g = globalThis.fetch;
61209
+ this.fetchImpl = opts.fetch ?? (g ? g.bind(globalThis) : null);
61210
+ this.now = opts.now ?? Date.now;
61211
+ this.minIntervalMs = opts.minIntervalMs ?? 400;
61212
+ this.timeoutMs = opts.timeoutMs ?? 1e4;
61213
+ this.ttlMs = opts.ttlMs ?? 15 * 6e4;
61214
+ this.offline = opts.offline ?? false;
61215
+ this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
61216
+ }
61217
+ /** Serialize + space requests; returns parsed JSON or throws. */
61218
+ async request(url, init2) {
61219
+ if (!this.fetchImpl || this.offline) throw new Error("offline");
61220
+ const run = async () => {
61221
+ const wait = this.lastRequestAt + this.minIntervalMs - this.now();
61222
+ if (wait > 0) await this.sleep(wait);
61223
+ this.lastRequestAt = this.now();
61224
+ this.requests++;
61225
+ const ctl = typeof AbortController !== "undefined" ? new AbortController() : null;
61226
+ const timer2 = setTimeout(() => ctl?.abort(), this.timeoutMs);
61227
+ try {
61228
+ const timeout = new Promise(
61229
+ (_, rej) => setTimeout(() => rej(new Error(`timeout after ${this.timeoutMs} ms`)), this.timeoutMs)
61230
+ );
61231
+ const res = await Promise.race([
61232
+ this.fetchImpl(url, { ...init2, signal: ctl?.signal }),
61233
+ timeout
61234
+ ]);
61235
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
61236
+ return await res.json();
61237
+ } finally {
61238
+ clearTimeout(timer2);
61239
+ }
61240
+ };
61241
+ const p = this.queue.then(run, run);
61242
+ this.queue = p.then(
61243
+ () => void 0,
61244
+ () => void 0
61245
+ );
61246
+ return p;
61247
+ }
61248
+ async cached(key3, fn) {
61249
+ const c = this.cache.get(key3);
61250
+ if (c && this.now() - c.at < this.ttlMs) return { value: c.value, hit: true };
61251
+ const value = await fn();
61252
+ this.cache.set(key3, { at: this.now(), value });
61253
+ return { value, hit: false };
61254
+ }
61255
+ /** JLCPCB parts-library keyword search. */
61256
+ async jlcSearch(keyword) {
61257
+ const { value, hit } = await this.cached(`jlc:${keyword}`, async () => {
61258
+ const r = await this.request(JLC_SEARCH_URL, {
61259
+ method: "POST",
61260
+ headers: { "Content-Type": "application/json", "User-Agent": UA },
61261
+ body: JSON.stringify({ keyword, currentPage: 1, pageSize: 10 })
61262
+ });
61263
+ if (r.code !== 200) throw new Error(`JLC search code ${r.code}`);
61264
+ return r.data?.componentPageInfo?.list ?? [];
61265
+ });
61266
+ return { rows: value, hit };
61267
+ }
61268
+ /** LCSC product detail by LCSC#. Null when LCSC has no such product. */
61269
+ async lcscDetail(code) {
61270
+ const { value, hit } = await this.cached(`lcsc:${code}`, async () => {
61271
+ const r = await this.request(LCSC_DETAIL_URL + encodeURIComponent(code), {
61272
+ headers: { "User-Agent": UA }
61273
+ });
61274
+ if (r.code !== 200) throw new Error(`LCSC detail code ${r.code}`);
61275
+ return r.result && r.result.productCode ? r.result : null;
61276
+ });
61277
+ return { detail: value, hit };
61278
+ }
61279
+ /**
61280
+ * Check one part. `query` is an LCSC#, an MPN (DB-known or not), or a
61281
+ * passive spec line ("RES 10k 0603"). `need` (default 1) picks the best
61282
+ * stocked candidate for spec queries.
61283
+ */
61284
+ async check(query, need = 1) {
61285
+ const q2 = query.trim();
61286
+ const dbPart = lookupOrderable(q2);
61287
+ const base = {
61288
+ query: q2,
61289
+ found: false,
61290
+ lcsc: dbPart?.lcsc ?? (LCSC_RE.test(q2) ? q2.toUpperCase() : null),
61291
+ mpn: dbPart?.mpn ?? null,
61292
+ manufacturer: dbPart?.manufacturer ?? null,
61293
+ package: dbPart?.footprint ?? null,
61294
+ description: dbPart?.description ?? null,
61295
+ stock_lcsc: null,
61296
+ stock_jlc: null,
61297
+ jlc_library: dbPart?.jlc_library ?? null,
61298
+ min_order_qty: null,
61299
+ price_breaks: [],
61300
+ jlc_price_breaks: [],
61301
+ lifecycle: null,
61302
+ eol: false,
61303
+ source: "live",
61304
+ fetched_at: null,
61305
+ db: dbPart ? {
61306
+ id: dbPart.id,
61307
+ mpn: dbPart.mpn,
61308
+ footprint: dbPart.footprint,
61309
+ footprint_status: dbPart.footprint_status,
61310
+ verification: dbPart.verification,
61311
+ alternatives: dbPart.alternatives
61312
+ } : null,
61313
+ warnings: []
61314
+ };
61315
+ if (dbPart?.verification.status === "flagged") {
61316
+ base.warnings.push(`DB flags this part: ${dbPart.verification.note ?? "unverified"}`);
61317
+ }
61318
+ if (dbPart?.verification.status === "corrected" && dbPart.bom_mpn && normMpn(dbPart.bom_mpn) === normMpn(q2)) {
61319
+ base.warnings.push(`'${q2}' was corrected to '${dbPart.mpn}': ${dbPart.verification.note ?? ""}`.trim());
61320
+ }
61321
+ if (dbPart?.bom_lcsc && dbPart.bom_lcsc === q2.toUpperCase()) {
61322
+ base.warnings.push(`LCSC ${q2} is NOT this part's number; the correct LCSC# is ${dbPart.lcsc}. ${dbPart.verification.note ?? ""}`.trim());
61323
+ }
61324
+ let anyHit = false;
61325
+ let anyMiss = false;
61326
+ try {
61327
+ let row;
61328
+ const spec = !dbPart && !LCSC_RE.test(q2) ? passiveKeyword(q2) : null;
61329
+ const keyword = base.lcsc ?? spec?.keyword ?? dbPart?.mpn ?? q2;
61330
+ if (dbPart && !dbPart.mpn && !base.lcsc) {
61331
+ base.warnings.push("DB entry is a part class, not an orderable MPN \u2014 query a concrete value");
61332
+ return this.offlineFallback(base, "class entry");
61333
+ }
61334
+ const s = await this.jlcSearch(keyword);
61335
+ s.hit ? anyHit = true : anyMiss = true;
61336
+ const rows = s.rows.filter((r) => !isPlaceholderRow(r));
61337
+ if (base.lcsc) {
61338
+ row = rows.find((r) => r.componentCode === base.lcsc) ?? s.rows.find((r) => r.componentCode === base.lcsc);
61339
+ } else if (spec) {
61340
+ const ok4 = rows.filter((r) => spec.mustContain.every((t2) => describeHas(r, t2)));
61341
+ const pkgTok = q2.match(/\b(0201|0402|0603|0805|1206|1210|1812|2010|2512)\b/)?.[1];
61342
+ const pkgOk = pkgTok ? ok4.filter((r) => (r.componentSpecificationEn ?? "").includes(pkgTok)) : ok4;
61343
+ const rank = (r) => (r.stockCount >= need ? 0 : 2) + (r.componentLibraryType === "base" ? 0 : 1);
61344
+ row = [...pkgOk].sort((a, b) => rank(a) - rank(b) || b.stockCount - a.stockCount)[0];
61345
+ if (row) base.warnings.push(`spec match: picked ${row.componentCode} ${row.componentModelEn} for '${q2}'`);
61346
+ } else {
61347
+ const key3 = normMpn(dbPart?.mpn ?? q2);
61348
+ const exact = rows.filter((r) => normMpn(r.componentModelEn) === key3);
61349
+ row = exact.find((r) => r.stockCount > 0) ?? exact[0] ?? rows.find((r) => normMpn(r.componentModelEn).startsWith(key3));
61350
+ }
61351
+ if (row) {
61352
+ base.found = true;
61353
+ base.lcsc = row.componentCode;
61354
+ base.mpn = base.mpn ?? row.componentModelEn;
61355
+ base.manufacturer = base.manufacturer ?? row.componentBrandEn;
61356
+ base.package = base.package ?? row.componentSpecificationEn ?? null;
61357
+ base.description = base.description ?? row.describe ?? null;
61358
+ base.stock_jlc = row.stockCount;
61359
+ base.jlc_library = row.componentLibraryType === "base" ? "basic" : "extended";
61360
+ base.jlc_price_breaks = (row.componentPrices ?? []).map((p) => ({ qty: p.startNumber, unit_usd: p.productPrice }));
61361
+ if (isPlaceholderRow(row)) base.warnings.push(`${row.componentCode} is a JLC consignment placeholder, not stocked`);
61362
+ }
61363
+ if (base.lcsc) {
61364
+ const d = await this.lcscDetail(base.lcsc);
61365
+ d.hit ? anyHit = true : anyMiss = true;
61366
+ if (d.detail) {
61367
+ const det = d.detail;
61368
+ base.found = true;
61369
+ if (base.mpn && normMpn(det.productModel) !== normMpn(base.mpn) && !dbPart?.aliases?.some((a) => normMpn(a) === normMpn(det.productModel))) {
61370
+ base.warnings.push(`LCSC ${base.lcsc} is '${det.productModel}' (${det.brandNameEn}), not '${base.mpn}'`);
61371
+ }
61372
+ base.mpn = base.mpn ?? det.productModel;
61373
+ base.manufacturer = base.manufacturer ?? det.brandNameEn;
61374
+ base.stock_lcsc = det.stockNumber;
61375
+ base.min_order_qty = det.minBuyNumber ?? null;
61376
+ base.lifecycle = det.productCycle ?? null;
61377
+ base.eol = /discontinu|obsolete|eol|nrnd|not.?recommend|end.?of.?life/i.test(det.productCycle ?? "");
61378
+ const ladder = (det.productPriceList ?? []).map((p) => ({ qty: p.ladder, unit_usd: p.usdPrice }));
61379
+ base.price_breaks = ladder;
61380
+ }
61381
+ }
61382
+ if (!base.found) base.warnings.push(`no LCSC/JLCPCB listing found for '${q2}'`);
61383
+ base.source = anyHit && !anyMiss ? "cache" : "live";
61384
+ base.fetched_at = new Date(this.now()).toISOString();
61385
+ return base;
61386
+ } catch (e) {
61387
+ return this.offlineFallback(base, e.message);
61388
+ }
61389
+ }
61390
+ offlineFallback(base, why) {
61391
+ base.source = "offline";
61392
+ base.found = base.db !== null && base.lcsc !== null;
61393
+ base.warnings.push(
61394
+ `live check unavailable (${why}); identity from the offline DB (verified ${orderableCheckedDate()}), no stock/price`
61395
+ );
61396
+ return base;
61397
+ }
61398
+ };
61399
+ BASES = ["lcsc", "jlc_assembly"];
61400
+ sharedClient = null;
61401
+ STOCK_NOTE = "Stock and prices are a live snapshot from public LCSC/JLCPCB listings (USD, excl. shipping/tax/JLC setup fees) \u2014 re-check before ordering.";
61402
+ checkPartStockSchema = {
61403
+ type: "object",
61404
+ properties: {
61405
+ query: { type: "string", description: "MPN (e.g. 'TPS48110AQDGXRQ1'), LCSC# (e.g. 'C7519'), or a passive spec line ('RES 10k 0603', 'CAP 100nF 50V 0603')." },
61406
+ queries: { type: "array", items: { type: "string" }, description: "Several queries at once (rate-limited, cached)." },
61407
+ qty: { type: "integer", description: "Quantity needed \u2014 spec queries prefer a candidate with at least this much stock. Default 1." }
61408
+ }
61409
+ };
61410
+ bomCheckStockSchema = {
61411
+ type: "object",
61412
+ properties: {
61413
+ bom_id: { type: "string", description: "A BOM from bom_create (its COTS lines are checked)." },
61414
+ csv: { type: "string", description: "BOM CSV text in the bom_export csv shape (kind,name,\u2026,example_pn,\u2026,qty,\u2026,notes)." },
61415
+ lines: {
61416
+ type: "array",
61417
+ items: {
61418
+ type: "object",
61419
+ properties: { name: { type: "string" }, part: { type: "string" }, qty: { type: "number" }, noted_lcsc: { type: "string" } },
61420
+ required: ["part", "qty"]
61421
+ },
61422
+ description: "Inline lines: part = MPN / LCSC# / passive spec, qty per board."
61423
+ },
61424
+ builds: { type: "array", items: { type: "integer" }, description: "Board build quantities to cost (default [5, 100])." }
61425
+ }
61426
+ };
61427
+ bomResolver = null;
61428
+ toolDefs15 = [
61429
+ {
61430
+ name: "check_part_stock",
61431
+ pack: "bom",
61432
+ description: "Live stock + price check for electronic parts against public LCSC and JLCPCB listings (no credentials). Accepts an MPN, an LCSC#, or a passive spec line ('RES 10k 0603'). Returns LCSC and JLC-assembly stock, the USD price ladder, the JLC basic/extended library tier, lifecycle/EOL, and, when the part is in vcad's curated orderable DB, its verified LCSC#, footprint, verification record, and alternatives. Cached (15 min), rate-limited, and with timeouts. When offline it falls back to the DB's identity data and reports no stock.",
61433
+ inputSchema: checkPartStockSchema,
61434
+ handler: (a) => checkPartStock(a),
61435
+ behavior: behavior({})
61436
+ },
61437
+ {
61438
+ name: "bom_check_stock",
61439
+ pack: "bom",
61440
+ description: "Annotate a BOM with live LCSC/JLCPCB stock and price. Takes a bom_id, BOM CSV text, or inline lines (MPN / LCSC# / passive spec + qty per board). Returns per-line stock, price ladder, and extended cost at each build quantity (default 5 and 100 boards, honouring LCSC minimum order), totals, and flags: missing, out_of_stock, low_stock, eol, db_flagged/db_corrected (the curated DB found the MPN wrong or unorderable), lcsc_mismatch (the line's noted LCSC# is a different part), footprint_placeholder. Flagged lines include alternatives.",
61441
+ inputSchema: bomCheckStockSchema,
61442
+ handler: (a, c) => bomCheckStock(a, c),
61443
+ behavior: behavior({})
61444
+ }
61445
+ ];
61446
+ }
61447
+ });
61448
+
60847
61449
  // src/tools/bom.ts
60848
61450
  import { randomUUID as randomUUID6 } from "node:crypto";
60849
61451
  function ok3(payload) {
@@ -61270,7 +61872,7 @@ function renderCsv(bom) {
61270
61872
  }
61271
61873
  return rows.join("\r\n") + "\r\n";
61272
61874
  }
61273
- var PRICING_NOTE, memBoms, bomKey, toUsd3, usdToMinor, fmtUsd, BOM_ORACLE, str, lineProperties, bomCreateSchema, bomAddLineSchema, bomExportSchema, md, csv, toolDefs15;
61875
+ var PRICING_NOTE, memBoms, bomKey, toUsd3, usdToMinor, fmtUsd, BOM_ORACLE, str, lineProperties, bomCreateSchema, bomAddLineSchema, bomExportSchema, md, csv, toolDefs16;
61274
61876
  var init_bom = __esm({
61275
61877
  "src/tools/bom.ts"() {
61276
61878
  "use strict";
@@ -61278,11 +61880,23 @@ var init_bom = __esm({
61278
61880
  init_pricing();
61279
61881
  init_types4();
61280
61882
  init_mech_parts();
61883
+ init_part_stock();
61281
61884
  init_session_core();
61282
61885
  init_tool_def();
61283
61886
  PRICING_NOTE = "All prices are ESTIMATES (Phase-0 quote estimates, catalog price bands, or caller-supplied numbers) \u2014 not binding quotes.";
61284
61887
  memBoms = /* @__PURE__ */ new Map();
61285
61888
  bomKey = (owner, id) => `${owner}::${id}`;
61889
+ setBomLinesResolver((bomId, ctx) => {
61890
+ const user = ctx?.user ?? null;
61891
+ const bom = getBom(ownerId(user), bomId);
61892
+ if (!bom) return null;
61893
+ return bom.lines.filter((l) => l.kind === "cots").filter((l) => (l.example_pn ?? l.spec ?? "").trim() !== "").map((l) => ({
61894
+ name: l.name,
61895
+ part: l.example_pn ?? l.spec,
61896
+ qty: l.qty,
61897
+ noted_lcsc: l.notes?.match(/\bLCSC\s+(C\d+)/i)?.[1] ?? null
61898
+ }));
61899
+ });
61286
61900
  toUsd3 = (minor) => Math.round(minor) / 100;
61287
61901
  usdToMinor = (usd2) => Math.round(usd2 * 100);
61288
61902
  fmtUsd = (minor) => minor === null ? "\u2014" : `$${(Math.round(minor) / 100).toFixed(2)}`;
@@ -61378,7 +61992,7 @@ var init_bom = __esm({
61378
61992
  const s = String(v);
61379
61993
  return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
61380
61994
  };
61381
- toolDefs15 = [
61995
+ toolDefs16 = [
61382
61996
  {
61383
61997
  name: "bom_create",
61384
61998
  pack: "bom",
@@ -61441,7 +62055,7 @@ async function unshareSession(input, store) {
61441
62055
  note: "Live link revoked \u2014 the link now 404s and no further live updates are broadcast for this session. (A viewer already connected keeps only the events they had already received; their socket isn't force-closed.)"
61442
62056
  });
61443
62057
  }
61444
- var DISABLED, shareSessionSchema, unshareSessionSchema, toolDefs16;
62058
+ var DISABLED, shareSessionSchema, unshareSessionSchema, toolDefs17;
61445
62059
  var init_live_share = __esm({
61446
62060
  "src/tools/live-share.ts"() {
61447
62061
  "use strict";
@@ -61469,7 +62083,7 @@ var init_live_share = __esm({
61469
62083
  },
61470
62084
  required: ["document_id"]
61471
62085
  };
61472
- toolDefs16 = [
62086
+ toolDefs17 = [
61473
62087
  {
61474
62088
  name: "share_session",
61475
62089
  pack: null,
@@ -61597,7 +62211,7 @@ function placePartTool(args, engine) {
61597
62211
  ]
61598
62212
  };
61599
62213
  }
61600
- var searchPartsSchema, placePartSchema, toolDefs17;
62214
+ var searchPartsSchema, placePartSchema, toolDefs18;
61601
62215
  var init_parts2 = __esm({
61602
62216
  "src/tools/parts.ts"() {
61603
62217
  "use strict";
@@ -61653,7 +62267,7 @@ var init_parts2 = __esm({
61653
62267
  },
61654
62268
  required: ["document_id", "path"]
61655
62269
  };
61656
- toolDefs17 = [
62270
+ toolDefs18 = [
61657
62271
  {
61658
62272
  name: "search_parts",
61659
62273
  pack: null,
@@ -61811,7 +62425,7 @@ function exportCad(input, engine) {
61811
62425
  ...scene.instances && scene.instances.length > 0 ? { instances: scene.instances.length } : {}
61812
62426
  });
61813
62427
  }
61814
- var exportCadSchema, toolDefs18;
62428
+ var exportCadSchema, toolDefs19;
61815
62429
  var init_export = __esm({
61816
62430
  "src/tools/export.ts"() {
61817
62431
  "use strict";
@@ -61847,7 +62461,7 @@ var init_export = __esm({
61847
62461
  },
61848
62462
  required: ["filename"]
61849
62463
  };
61850
- toolDefs18 = [
62464
+ toolDefs19 = [
61851
62465
  {
61852
62466
  name: "export_cad",
61853
62467
  pack: null,
@@ -62088,7 +62702,7 @@ function err5(text) {
62088
62702
  isError: true
62089
62703
  };
62090
62704
  }
62091
- var MAX_STEPS_PER_SIDE, JointRangeError, key2, Lattice, checkJointRangeSchema, toolDefs19;
62705
+ var MAX_STEPS_PER_SIDE, JointRangeError, key2, Lattice, checkJointRangeSchema, toolDefs20;
62092
62706
  var init_joint_range = __esm({
62093
62707
  "src/tools/joint-range.ts"() {
62094
62708
  "use strict";
@@ -62186,7 +62800,7 @@ var init_joint_range = __esm({
62186
62800
  },
62187
62801
  required: ["document_id"]
62188
62802
  };
62189
- toolDefs19 = [
62803
+ toolDefs20 = [
62190
62804
  {
62191
62805
  name: "check_joint_range",
62192
62806
  pack: null,
@@ -62466,7 +63080,7 @@ function measureOuterDiameter(args, engine) {
62466
63080
  structuredContent: { outer_diameter: body, document_id: documentId }
62467
63081
  };
62468
63082
  }
62469
- var DEFAULT_LIMIT, r62, vec, ACCURACY_NOTE, inspectFacesSchema, outerDiameterSchema, toolDefs20;
63083
+ var DEFAULT_LIMIT, r62, vec, ACCURACY_NOTE, inspectFacesSchema, outerDiameterSchema, toolDefs21;
62470
63084
  var init_faces = __esm({
62471
63085
  "src/tools/faces.ts"() {
62472
63086
  "use strict";
@@ -62547,7 +63161,7 @@ var init_faces = __esm({
62547
63161
  },
62548
63162
  required: ["document_id"]
62549
63163
  };
62550
- toolDefs20 = [
63164
+ toolDefs21 = [
62551
63165
  {
62552
63166
  name: "inspect_faces",
62553
63167
  pack: null,
@@ -62728,7 +63342,7 @@ function sensitivity(input, engine) {
62728
63342
  claims: report.claims
62729
63343
  });
62730
63344
  }
62731
- var listParametersSchema, setParametersSchema, parameterGradientSchema, sensitivitySchema, toolDefs21;
63345
+ var listParametersSchema, setParametersSchema, parameterGradientSchema, sensitivitySchema, toolDefs22;
62732
63346
  var init_parameters = __esm({
62733
63347
  "src/tools/parameters.ts"() {
62734
63348
  "use strict";
@@ -62826,7 +63440,7 @@ var init_parameters = __esm({
62826
63440
  },
62827
63441
  required: []
62828
63442
  };
62829
- toolDefs21 = [
63443
+ toolDefs22 = [
62830
63444
  {
62831
63445
  name: "list_parameters",
62832
63446
  pack: null,
@@ -62865,6 +63479,181 @@ var init_parameters = __esm({
62865
63479
  }
62866
63480
  });
62867
63481
 
63482
+ // src/tools/schematic-hierarchy.ts
63483
+ function offsetRef(ref, offset) {
63484
+ const m = /^(.*?)(\d+)$/.exec(ref);
63485
+ if (!m) return `${ref}${offset}`;
63486
+ return `${m[1]}${Number(m[2]) + offset}`;
63487
+ }
63488
+ function portsOf(def) {
63489
+ const ports = new Set((def.ports ?? []).map((p) => p.name));
63490
+ for (const l of def.body.labels ?? []) if (l.scope === "Hierarchical") ports.add(l.name);
63491
+ return ports;
63492
+ }
63493
+ async function flattenSchematic(sheet, netlister) {
63494
+ const h = sheet.hierarchy;
63495
+ if (!h) return { sheet, diagnostics: [], instances: [] };
63496
+ const diagnostics = [];
63497
+ const err9 = (message) => diagnostics.push({ severity: "Error", message });
63498
+ const warn = (message) => diagnostics.push({ severity: "Warning", message });
63499
+ const defs = /* @__PURE__ */ new Map();
63500
+ for (const def of h.sheets ?? []) {
63501
+ if (defs.has(def.name)) err9(`Duplicate sheet definition '${def.name}'`);
63502
+ else defs.set(def.name, def);
63503
+ }
63504
+ const globals = new Set(h.global_nets ?? []);
63505
+ for (const l of [...sheet.labels ?? [], ...(h.sheets ?? []).flatMap((d) => d.body.labels ?? [])]) {
63506
+ if (l.scope === "Global") globals.add(l.name);
63507
+ }
63508
+ const rootNames = /* @__PURE__ */ new Set([
63509
+ ...(sheet.labels ?? []).map((l) => l.name),
63510
+ ...Object.keys(sheet.nets ?? {})
63511
+ ]);
63512
+ const useCount = /* @__PURE__ */ new Map();
63513
+ const countUses = (instances2, stack) => {
63514
+ for (const inst of instances2) {
63515
+ useCount.set(inst.sheet, (useCount.get(inst.sheet) ?? 0) + 1);
63516
+ if (stack.includes(inst.sheet)) continue;
63517
+ const def = defs.get(inst.sheet);
63518
+ if (def) countUses(def.instances ?? [], [...stack, def.name]);
63519
+ }
63520
+ };
63521
+ countUses(h.instances ?? [], []);
63522
+ const outComponents = [];
63523
+ const outNets = new Map(Object.entries(sheet.nets ?? {}).map(([k, v]) => [k, [...v]]));
63524
+ const outFlags = [];
63525
+ const instances = [];
63526
+ const unusedPortReported = /* @__PURE__ */ new Set();
63527
+ let autoSlot = 0;
63528
+ const elaborateAll = async (insts, parentPath, parentOffset, resolveParent, stack) => {
63529
+ const seen = /* @__PURE__ */ new Set();
63530
+ const where = parentPath === "" ? "the root sheet" : `sheet instance '${parentPath}'`;
63531
+ for (const inst of insts) {
63532
+ if (!inst.name || inst.name.includes("/")) {
63533
+ err9(`Sheet instance name '${inst.name}' in ${where} must be non-empty and contain no '/'`);
63534
+ continue;
63535
+ }
63536
+ if (seen.has(inst.name)) {
63537
+ err9(`Duplicate sheet instance name '${inst.name}' in ${where}`);
63538
+ continue;
63539
+ }
63540
+ seen.add(inst.name);
63541
+ const def = defs.get(inst.sheet);
63542
+ if (!def) {
63543
+ err9(`Sheet instance '${inst.name}' references unknown sheet '${inst.sheet}'`);
63544
+ continue;
63545
+ }
63546
+ if (stack.includes(def.name)) {
63547
+ err9(`Sheet recursion: instance '${inst.name}' places sheet '${def.name}' inside itself`);
63548
+ continue;
63549
+ }
63550
+ const path = parentPath === "" ? inst.name : `${parentPath}/${inst.name}`;
63551
+ const binding = /* @__PURE__ */ new Map();
63552
+ for (const [port, net] of Object.entries(inst.connections ?? {})) binding.set(port, resolveParent(net));
63553
+ autoSlot += 1;
63554
+ const offset = inst.position ? { x: parentOffset.x + inst.position.x, y: parentOffset.y + inst.position.y } : { x: 0, y: AUTO_INSTANCE_STRIDE * autoSlot };
63555
+ await elaborate(def, inst, path, binding, offset, [...stack, def.name]);
63556
+ }
63557
+ };
63558
+ const elaborate = async (def, inst, path, binding, offset, stack) => {
63559
+ const ports = portsOf(def);
63560
+ for (const port of [...ports].sort()) {
63561
+ if (globals.has(port)) {
63562
+ err9(`Net scope collision: port '${port}' of sheet '${def.name}' is also a global net`);
63563
+ }
63564
+ }
63565
+ for (const port of Object.keys(inst.connections ?? {}).sort()) {
63566
+ if (!ports.has(port)) {
63567
+ err9(`Sheet instance '${path}' connects '${port}', which is not a port of sheet '${def.name}'`);
63568
+ }
63569
+ }
63570
+ for (const port of [...ports].sort()) {
63571
+ if (!binding.has(port)) warn(`Hierarchical port '${port}' of sheet instance '${path}' is not connected`);
63572
+ }
63573
+ if (def.body.hierarchy) {
63574
+ err9(`Sheet '${def.name}' body has its own hierarchy \u2014 nest sheets with \`instances\` instead (ignored)`);
63575
+ }
63576
+ const resolve7 = (name) => {
63577
+ if (ports.has(name)) {
63578
+ const bound = binding.get(name);
63579
+ if (bound !== void 0) return bound;
63580
+ } else if (globals.has(name)) {
63581
+ return name;
63582
+ }
63583
+ return `${path}/${name}`;
63584
+ };
63585
+ const repeated = (useCount.get(def.name) ?? 0) > 1;
63586
+ const refs = {};
63587
+ for (const comp of def.body.components ?? []) {
63588
+ const mapped = inst.ref_map?.[comp.ref];
63589
+ const flatRef = mapped !== void 0 ? mapped : inst.ref_offset != null ? offsetRef(comp.ref, inst.ref_offset) : repeated ? `${comp.ref}_${inst.name}` : comp.ref;
63590
+ refs[comp.ref] = flatRef;
63591
+ outComponents.push({
63592
+ ...comp,
63593
+ ref: flatRef,
63594
+ position: { x: comp.position.x + offset.x, y: comp.position.y + offset.y }
63595
+ });
63596
+ }
63597
+ const body = { ...def.body, hierarchy: void 0 };
63598
+ const nets2 = await netlister(body);
63599
+ const usedNames = /* @__PURE__ */ new Set();
63600
+ for (const [name, pins] of [...nets2.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
63601
+ if (pins.length === 0) continue;
63602
+ if (isAutoName(name) && pins.length < 2) continue;
63603
+ usedNames.add(name);
63604
+ const flat = resolve7(name);
63605
+ if (!ports.has(name) && !globals.has(name) && rootNames.has(flat)) {
63606
+ err9(
63607
+ `Net scope collision: local net '${name}' of sheet instance '${path}' flattens to '${flat}', which the root sheet also uses`
63608
+ );
63609
+ }
63610
+ const entry = outNets.get(flat) ?? [];
63611
+ for (const p of pins) {
63612
+ const dot = p.indexOf(".");
63613
+ const ref = p.slice(0, dot);
63614
+ entry.push(`${refs[ref] ?? ref}${p.slice(dot)}`);
63615
+ }
63616
+ outNets.set(flat, entry);
63617
+ }
63618
+ for (const child of def.instances ?? []) {
63619
+ for (const n of Object.values(child.connections ?? {})) usedNames.add(n);
63620
+ }
63621
+ for (const port of [...ports].sort()) {
63622
+ const key3 = `${def.name}\0${port}`;
63623
+ if (!usedNames.has(port) && !unusedPortReported.has(key3)) {
63624
+ unusedPortReported.add(key3);
63625
+ warn(`Hierarchical port '${port}' of sheet '${def.name}' connects to nothing inside the sheet`);
63626
+ }
63627
+ }
63628
+ for (const flag of def.body.power_flags ?? []) outFlags.push({ ...flag, net: resolve7(flag.net) });
63629
+ instances.push({ path, sheet: def.name, refs });
63630
+ await elaborateAll(def.instances ?? [], path, offset, resolve7, stack);
63631
+ };
63632
+ await elaborateAll(h.instances ?? [], "", { x: 0, y: 0 }, (n) => n, []);
63633
+ const nets = {};
63634
+ for (const [name, pins] of [...outNets.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
63635
+ nets[name] = [...new Set(pins)];
63636
+ }
63637
+ const { hierarchy: _h, ...rest } = sheet;
63638
+ const flatSheet = {
63639
+ ...rest,
63640
+ components: [...sheet.components ?? [], ...outComponents],
63641
+ ...Object.keys(nets).length > 0 ? { nets } : { nets: void 0 },
63642
+ power_flags: [...sheet.power_flags ?? [], ...outFlags]
63643
+ };
63644
+ if (flatSheet.nets === void 0) delete flatSheet.nets;
63645
+ if ((flatSheet.power_flags ?? []).length === 0) delete flatSheet.power_flags;
63646
+ return { sheet: flatSheet, diagnostics, instances };
63647
+ }
63648
+ var AUTO_INSTANCE_STRIDE, isAutoName;
63649
+ var init_schematic_hierarchy = __esm({
63650
+ "src/tools/schematic-hierarchy.ts"() {
63651
+ "use strict";
63652
+ AUTO_INSTANCE_STRIDE = 1e5;
63653
+ isAutoName = (n) => /^NET-\d+$/.test(n);
63654
+ }
63655
+ });
63656
+
62868
63657
  // src/tools/constraint-claims.ts
62869
63658
  function claimIdFor(c) {
62870
63659
  return `${CONSTRAINT_CLAIM_PREFIX}${c.label ?? c.id}`;
@@ -63188,7 +63977,7 @@ function err7(text) {
63188
63977
  isError: true
63189
63978
  };
63190
63979
  }
63191
- var vec3Schema, checkEnclosureFitSchema, toolDefs22;
63980
+ var vec3Schema, checkEnclosureFitSchema, toolDefs23;
63192
63981
  var init_enclosure = __esm({
63193
63982
  "src/tools/enclosure.ts"() {
63194
63983
  "use strict";
@@ -63247,7 +64036,7 @@ var init_enclosure = __esm({
63247
64036
  },
63248
64037
  required: ["document_id", "enclosure_document_id"]
63249
64038
  };
63250
- toolDefs22 = [
64039
+ toolDefs23 = [
63251
64040
  {
63252
64041
  name: "check_enclosure_fit",
63253
64042
  pack: "ecad",
@@ -63743,23 +64532,23 @@ function synthesizedPassivePins() {
63743
64532
  { number: "2", name: "2", pin_type: "Passive", position: { x: 5.08, y: 0 } }
63744
64533
  ];
63745
64534
  }
63746
- async function createSchematic(args) {
64535
+ async function buildSheetBody(args, where = "") {
63747
64536
  const title = args.title || void 0;
63748
64537
  const componentsInput = args.components || [];
63749
64538
  const wiresInput = args.wires || [];
63750
64539
  const labelsInput = args.labels || [];
63751
64540
  const netsInput = args.nets || void 0;
63752
- const coord = (v, where) => {
64541
+ const coord = (v, where2) => {
63753
64542
  if (typeof v !== "number" || !Number.isFinite(v)) {
63754
64543
  throw new Error(
63755
- `create_schematic: ${where} must be a finite number, got ${JSON.stringify(v)}`
64544
+ `create_schematic: ${where2} must be a finite number, got ${JSON.stringify(v)}`
63756
64545
  );
63757
64546
  }
63758
64547
  return v;
63759
64548
  };
63760
- const coordOpt = (v, where) => {
64549
+ const coordOpt = (v, where2) => {
63761
64550
  if (v === void 0 || v === null) return 0;
63762
- return coord(v, where);
64551
+ return coord(v, where2);
63763
64552
  };
63764
64553
  const warnings = [];
63765
64554
  const resolvedParts = [];
@@ -63864,7 +64653,16 @@ async function createSchematic(args) {
63864
64653
  labels,
63865
64654
  ...netsInput ? { nets: netsInput } : {}
63866
64655
  };
63867
- if (netsInput) validateExplicitNets(schematic, netsInput);
64656
+ const flagsInput = parsePowerFlags(args.power_flags, `${where}power_flags`);
64657
+ if (flagsInput.length > 0) schematic.power_flags = flagsInput;
64658
+ if (netsInput) {
64659
+ try {
64660
+ validateExplicitNets(schematic, netsInput);
64661
+ } catch (e) {
64662
+ if (!where) throw e;
64663
+ throw new Error(`${where}${e.message}`);
64664
+ }
64665
+ }
63868
64666
  for (const label of labels) {
63869
64667
  const touchesWire = wires.some(
63870
64668
  (w) => Math.abs(w.start.x - label.position.x) < POSITION_TOLERANCE && Math.abs(w.start.y - label.position.y) < POSITION_TOLERANCE || Math.abs(w.end.x - label.position.x) < POSITION_TOLERANCE && Math.abs(w.end.y - label.position.y) < POSITION_TOLERANCE
@@ -63881,10 +64679,218 @@ async function createSchematic(args) {
63881
64679
  );
63882
64680
  }
63883
64681
  }
64682
+ return {
64683
+ sheet: schematic,
64684
+ warnings: where ? warnings.map((w) => `${where}${w}`) : warnings,
64685
+ resolvedParts
64686
+ };
64687
+ }
64688
+ function parsePowerFlags(raw, where) {
64689
+ if (raw === void 0 || raw === null) return [];
64690
+ if (!Array.isArray(raw)) throw new Error(`${where} must be an array`);
64691
+ return raw.map((f, i) => {
64692
+ if (typeof f === "string" && f) return { net: f };
64693
+ const o = f ?? {};
64694
+ if (typeof o.net !== "string" || !o.net) {
64695
+ throw new Error(`${where}[${i}] must be a net name or {net, note?}`);
64696
+ }
64697
+ return {
64698
+ net: o.net,
64699
+ ...typeof o.note === "string" ? { note: o.note } : {},
64700
+ ...typeof o.x === "number" && typeof o.y === "number" ? { position: { x: o.x, y: o.y } } : {}
64701
+ };
64702
+ });
64703
+ }
64704
+ function parseSheetInstance(raw, where) {
64705
+ if (typeof raw.name !== "string" || typeof raw.sheet !== "string") {
64706
+ throw new Error(`${where} needs string \`name\` and \`sheet\``);
64707
+ }
64708
+ const conns = raw.connections ?? {};
64709
+ const connections = {};
64710
+ for (const [port, net] of Object.entries(conns)) {
64711
+ if (typeof net !== "string") throw new Error(`${where}.connections.${port} must be a net name`);
64712
+ connections[port] = net;
64713
+ }
64714
+ return {
64715
+ name: raw.name,
64716
+ sheet: raw.sheet,
64717
+ connections,
64718
+ ...raw.ref_map ? { ref_map: raw.ref_map } : {},
64719
+ ...typeof raw.ref_offset === "number" ? { ref_offset: raw.ref_offset } : {},
64720
+ ...typeof raw.x === "number" && typeof raw.y === "number" ? { position: { x: raw.x, y: raw.y } } : {}
64721
+ };
64722
+ }
64723
+ async function parseSheetDefinition(raw, i) {
64724
+ if (typeof raw.name !== "string" || !raw.name) throw new Error(`sheets[${i}] needs a \`name\``);
64725
+ const where = `sheet '${raw.name}': `;
64726
+ const built = await buildSheetBody(raw, where);
64727
+ const ports = (raw.ports ?? []).map((p, j) => {
64728
+ if (typeof p === "string") return { name: p, direction: "Passive" };
64729
+ const o = p ?? {};
64730
+ if (typeof o.name !== "string") throw new Error(`${where}ports[${j}] needs a name`);
64731
+ return {
64732
+ name: o.name,
64733
+ direction: o.direction === void 0 ? "Passive" : validatePinType(o.direction, `${where}port ${o.name}`)
64734
+ };
64735
+ });
64736
+ const instances = (raw.instances ?? []).map(
64737
+ (inst, j) => parseSheetInstance(inst, `${where}instances[${j}]`)
64738
+ );
64739
+ return { def: { name: raw.name, ports, body: built.sheet, instances }, built };
64740
+ }
64741
+ async function flattenSheet(sheet) {
64742
+ return flattenSchematic(sheet, async (s) => (await deriveNets(s)).nets);
64743
+ }
64744
+ async function docFlatSchematic(doc) {
64745
+ const sheet = doc.schematic;
64746
+ if (!sheet) return null;
64747
+ return flattenSheet(sheet);
64748
+ }
64749
+ function flaggedNets(sheet) {
64750
+ return new Set((sheet.power_flags ?? []).map((f) => f.net));
64751
+ }
64752
+ async function createSchematic(args) {
64753
+ const root = await buildSheetBody(args);
64754
+ const warnings = [...root.warnings];
64755
+ const resolvedParts = [...root.resolvedParts];
64756
+ const schematic = root.sheet;
64757
+ const sheetsInput = args.sheets ?? [];
64758
+ const instancesInput = args.instances ?? [];
64759
+ if (sheetsInput.length > 0 || instancesInput.length > 0) {
64760
+ const sheets = [];
64761
+ for (let i = 0; i < sheetsInput.length; i++) {
64762
+ const { def, built } = await parseSheetDefinition(sheetsInput[i], i);
64763
+ warnings.push(...built.warnings);
64764
+ resolvedParts.push(...built.resolvedParts.map((r) => ({ ...r, ref: `${def.name}:${r.ref}` })));
64765
+ sheets.push(def);
64766
+ }
64767
+ schematic.hierarchy = {
64768
+ sheets,
64769
+ instances: instancesInput.map((inst, i) => parseSheetInstance(inst, `instances[${i}]`)),
64770
+ global_nets: (args.global_nets ?? []).map(String)
64771
+ };
64772
+ }
63884
64773
  const doc = createDocument();
63885
64774
  doc.schematic = schematic;
63886
64775
  const documentId = registerSession(doc);
63887
- const derived = await deriveNets(schematic);
64776
+ return schematicSummary(schematic, documentId, warnings, resolvedParts);
64777
+ }
64778
+ async function updateSchematic(args) {
64779
+ const ctx = resolveDocInput(args);
64780
+ const doc = ctx.doc;
64781
+ if (!doc.schematic) return ecadError("Document has no schematic \u2014 create one with create_schematic.");
64782
+ const warnings = [];
64783
+ const resolvedParts = [];
64784
+ let schematic = doc.schematic;
64785
+ if (args.root && typeof args.root === "object") {
64786
+ const built = await buildSheetBody(args.root);
64787
+ warnings.push(...built.warnings);
64788
+ resolvedParts.push(...built.resolvedParts);
64789
+ schematic = { ...built.sheet, ...schematic.hierarchy ? { hierarchy: schematic.hierarchy } : {} };
64790
+ }
64791
+ if (args.power_flags !== void 0) {
64792
+ const flags = parsePowerFlags(args.power_flags, "power_flags");
64793
+ if (flags.length > 0) schematic.power_flags = flags;
64794
+ else delete schematic.power_flags;
64795
+ }
64796
+ const touchesHierarchy = ["sheets", "remove_sheets", "instances", "remove_instances", "global_nets"].some(
64797
+ (k) => args[k] !== void 0
64798
+ );
64799
+ if (touchesHierarchy) {
64800
+ const h = {
64801
+ sheets: [...schematic.hierarchy?.sheets ?? []],
64802
+ instances: [...schematic.hierarchy?.instances ?? []],
64803
+ global_nets: [...schematic.hierarchy?.global_nets ?? []]
64804
+ };
64805
+ const removeSheets = new Set((args.remove_sheets ?? []).map(String));
64806
+ h.sheets = h.sheets.filter((d) => !removeSheets.has(d.name));
64807
+ const sheetsInput = args.sheets ?? [];
64808
+ for (let i = 0; i < sheetsInput.length; i++) {
64809
+ const { def, built } = await parseSheetDefinition(sheetsInput[i], i);
64810
+ warnings.push(...built.warnings);
64811
+ resolvedParts.push(...built.resolvedParts.map((r) => ({ ...r, ref: `${def.name}:${r.ref}` })));
64812
+ const at = h.sheets.findIndex((d) => d.name === def.name);
64813
+ if (at >= 0) h.sheets[at] = def;
64814
+ else h.sheets.push(def);
64815
+ }
64816
+ const removeInst = new Set((args.remove_instances ?? []).map(String));
64817
+ h.instances = h.instances.filter((x) => !removeInst.has(x.name));
64818
+ (args.instances ?? []).forEach((raw, i) => {
64819
+ const inst = parseSheetInstance(raw, `instances[${i}]`);
64820
+ const at = h.instances.findIndex((x) => x.name === inst.name);
64821
+ if (at >= 0) h.instances[at] = inst;
64822
+ else h.instances.push(inst);
64823
+ });
64824
+ if (args.global_nets !== void 0) h.global_nets = (args.global_nets ?? []).map(String);
64825
+ if (h.sheets.length === 0 && h.instances.length === 0) delete schematic.hierarchy;
64826
+ else schematic.hierarchy = h;
64827
+ }
64828
+ doc.schematic = schematic;
64829
+ return schematicSummary(schematic, String(args.document_id), warnings, resolvedParts);
64830
+ }
64831
+ async function inspectSchematic(args) {
64832
+ const { doc } = resolveDocInput(args);
64833
+ const flat = await docFlatSchematic(doc);
64834
+ if (!flat) return ecadError("Document has no schematic \u2014 create one with create_schematic.");
64835
+ const sheet = doc.schematic;
64836
+ const only = typeof args.instance === "string" ? args.instance : void 0;
64837
+ if (only && !flat.instances.some((i) => i.path === only)) {
64838
+ return ecadError(
64839
+ `No sheet instance '${only}' (have: ${flat.instances.map((i) => i.path).join(", ") || "none \u2014 the schematic is flat"})`
64840
+ );
64841
+ }
64842
+ const derived = await deriveNets(flat.sheet);
64843
+ const inScope = (net, pins) => {
64844
+ if (!only) return true;
64845
+ if (net.startsWith(`${only}/`)) return true;
64846
+ const refs = new Set(Object.values(flat.instances.find((i) => i.path === only).refs));
64847
+ return pins.some((p) => refs.has(p.slice(0, p.indexOf("."))));
64848
+ };
64849
+ const nets = {};
64850
+ for (const [name, pins] of [...derived.nets.entries()].sort(([a], [b]) => a.localeCompare(b))) {
64851
+ if (inScope(name, pins)) nets[name] = pins;
64852
+ }
64853
+ const groups = /* @__PURE__ */ new Map();
64854
+ for (const c of flat.sheet.components) {
64855
+ if (c.ref.startsWith("#")) continue;
64856
+ const part = c.properties?.part;
64857
+ const key3 = `${c.value}\0${c.footprintId}\0${part ?? ""}`;
64858
+ const g = groups.get(key3) ?? { value: c.value, footprint: c.footprintId, ...part ? { part } : {}, refs: [] };
64859
+ g.refs.push(c.ref);
64860
+ groups.set(key3, g);
64861
+ }
64862
+ const bom = [...groups.values()].map((g) => ({ ...g, qty: g.refs.length, refs: g.refs.sort() })).sort((a, b) => b.qty - a.qty || a.value.localeCompare(b.value));
64863
+ const h = sheet.hierarchy;
64864
+ const payload = {
64865
+ success: true,
64866
+ document_id: args.document_id,
64867
+ hierarchical: !!h,
64868
+ ...h ? {
64869
+ sheets: (h.sheets ?? []).map((d) => ({
64870
+ name: d.name,
64871
+ ports: (d.ports ?? []).map((p) => ({ name: p.name, direction: p.direction ?? "Passive" })),
64872
+ components: d.body.components.length,
64873
+ instances: (d.instances ?? []).map((i) => ({ name: i.name, sheet: i.sheet }))
64874
+ })),
64875
+ global_nets: h.global_nets ?? [],
64876
+ instances: flat.instances.filter((i) => !only || i.path === only)
64877
+ } : {},
64878
+ root_components: sheet.components.length,
64879
+ components: flat.sheet.components.length,
64880
+ nets,
64881
+ power_flags: (flat.sheet.power_flags ?? []).map((f) => ({ net: f.net, ...f.note ? { note: f.note } : {} })),
64882
+ ...flat.diagnostics.length > 0 ? { issues: flat.diagnostics } : {},
64883
+ bom
64884
+ };
64885
+ return { content: [{ type: "text", text: JSON.stringify(payload) }] };
64886
+ }
64887
+ async function schematicSummary(schematic, documentId, warningsIn, resolvedParts) {
64888
+ const warnings = [...warningsIn];
64889
+ const flat = await flattenSheet(schematic);
64890
+ const components = flat.sheet.components;
64891
+ const wires = schematic.wires;
64892
+ const labels = schematic.labels;
64893
+ const derived = await deriveNets(flat.sheet);
63888
64894
  warnings.push(...derived.warnings);
63889
64895
  const netsPreview = {};
63890
64896
  for (const [name, pins] of derived.nets) netsPreview[name] = pins;
@@ -63927,6 +64933,14 @@ async function createSchematic(args) {
63927
64933
  nets: netsPreview,
63928
64934
  ...resolvedParts.length > 0 ? { resolved_parts: resolvedParts } : {},
63929
64935
  ...unconnected.length > 0 ? { unconnected_pins: unconnected } : {},
64936
+ ...schematic.hierarchy ? {
64937
+ hierarchy: {
64938
+ sheets: (schematic.hierarchy.sheets ?? []).map((d) => d.name),
64939
+ instances: flat.instances.map((i) => ({ path: i.path, sheet: i.sheet })),
64940
+ ...flat.diagnostics.length > 0 ? { issues: flat.diagnostics } : {}
64941
+ }
64942
+ } : {},
64943
+ ...(flat.sheet.power_flags ?? []).length > 0 ? { power_flags: (flat.sheet.power_flags ?? []).map((f) => f.net) } : {},
63930
64944
  ...warnings.length > 0 ? { warnings } : {}
63931
64945
  })
63932
64946
  }
@@ -64080,7 +65094,8 @@ async function placeComponents(args) {
64080
65094
  isError: true
64081
65095
  };
64082
65096
  }
64083
- const derived = await deriveNets(doc.schematic);
65097
+ const flatSchematic = (await flattenSheet(doc.schematic)).sheet;
65098
+ const derived = await deriveNets(flatSchematic);
64084
65099
  const netByPin = derived.netByPin;
64085
65100
  const useConnectivity = netByPin.size > 0;
64086
65101
  const warnings = [...derived.warnings];
@@ -64164,7 +65179,7 @@ async function placeComponents(args) {
64164
65179
  };
64165
65180
  const rules = defaultDesignRules();
64166
65181
  const defaultRules = rules.defaultRules;
64167
- const components = doc.schematic.components;
65182
+ const components = flatSchematic.components;
64168
65183
  const strategy = args.strategy || "grid";
64169
65184
  const margin = Math.min(5, extentW / 8, extentH / 8);
64170
65185
  const usableW = extentW - 2 * margin;
@@ -65752,14 +66767,16 @@ async function runErc2(args) {
65752
66767
  isError: true
65753
66768
  };
65754
66769
  }
65755
- const sheet = doc.schematic;
66770
+ const flat = await flattenSheet(doc.schematic);
66771
+ const sheet = flat.sheet;
66772
+ const flagged = flaggedNets(sheet);
65756
66773
  if (await isEcadAvailable2()) {
65757
66774
  const ercOutcome = await runErc(sheet);
65758
66775
  if (ercOutcome.status === "errored") {
65759
66776
  return ecadUnverifiable("run_erc", ercOutcome);
65760
66777
  }
65761
66778
  }
65762
- const violations = [];
66779
+ const violations = flat.diagnostics.map((d) => ({ severity: d.severity, message: d.message }));
65763
66780
  const refs = /* @__PURE__ */ new Map();
65764
66781
  for (const comp of sheet.components) {
65765
66782
  refs.set(comp.ref, (refs.get(comp.ref) || 0) + 1);
@@ -65791,6 +66808,8 @@ async function runErc2(args) {
65791
66808
  verified = true;
65792
66809
  for (const v of kernelOutcome.violations) {
65793
66810
  if (!isPinTypeOrPowerViolation(v.message)) continue;
66811
+ const noSource = /^Power input pin on net '(.*)' has no power source$/.exec(v.message);
66812
+ if (noSource && flagged.has(noSource[1])) continue;
65794
66813
  violations.push({
65795
66814
  severity: v.severity,
65796
66815
  message: v.message,
@@ -65802,6 +66821,16 @@ async function runErc2(args) {
65802
66821
  } else {
65803
66822
  unverifiedReason = `kernel ERC could not parse the schematic: ${kernelOutcome.message}`;
65804
66823
  }
66824
+ if (flagged.size > 0) {
66825
+ for (const net of [...flagged].sort()) {
66826
+ if (!derived.nets.has(net)) {
66827
+ violations.push({
66828
+ severity: "Warning",
66829
+ message: `Power flag on net '${net}', which does not exist`
66830
+ });
66831
+ }
66832
+ }
66833
+ }
65805
66834
  violations.sort((a, b) => {
65806
66835
  const sev = (a.severity === "Error" ? 0 : 1) - (b.severity === "Error" ? 0 : 1);
65807
66836
  return sev !== 0 ? sev : a.message.localeCompare(b.message);
@@ -66046,7 +67075,7 @@ async function exportKicad(args) {
66046
67075
  if (!name) {
66047
67076
  return ecadError("Project filename must have a basename, e.g. 'board.kicad_pro'.");
66048
67077
  }
66049
- const sheet = doc.schematic;
67078
+ const sheet = (await docFlatSchematic(doc))?.sheet;
66050
67079
  if (!sheet) {
66051
67080
  return ecadError(
66052
67081
  "A KiCad project bundle needs a schematic; this document has none. Create one with create_schematic, or export the board alone as .kicad_pcb."
@@ -66069,7 +67098,7 @@ async function exportKicad(args) {
66069
67098
  let format;
66070
67099
  if (ext === "kicad_sch") {
66071
67100
  format = "kicad_sch";
66072
- const sheet = doc.schematic;
67101
+ const sheet = (await docFlatSchematic(doc))?.sheet;
66073
67102
  if (!sheet) {
66074
67103
  return ecadError(
66075
67104
  "Document has no schematic to export. Create one with create_schematic."
@@ -70920,8 +71949,14 @@ async function searchElectronicParts(args) {
70920
71949
  const query = typeof args.query === "string" ? args.query : "";
70921
71950
  const limit = typeof args.limit === "number" ? Math.max(1, Math.round(args.limit)) : 5;
70922
71951
  const results = await searchEcadParts(query, limit);
71952
+ const orderable = searchOrderable(query, limit);
70923
71953
  return {
70924
- content: [{ type: "text", text: JSON.stringify({ query, count: results.length, results }) }]
71954
+ content: [
71955
+ {
71956
+ type: "text",
71957
+ text: JSON.stringify({ query, count: results.length, results, orderable_count: orderable.length, orderable })
71958
+ }
71959
+ ]
70925
71960
  };
70926
71961
  }
70927
71962
  async function resolvePart2(args) {
@@ -70936,6 +71971,12 @@ async function resolvePart2(args) {
70936
71971
  content: [{ type: "text", text: JSON.stringify({ kind: "jellybean", ...def }) }]
70937
71972
  };
70938
71973
  }
71974
+ const orderable = lookupOrderable(query);
71975
+ if (orderable) {
71976
+ return {
71977
+ content: [{ type: "text", text: JSON.stringify({ kind: "orderable", ...orderable }) }]
71978
+ };
71979
+ }
70939
71980
  return {
70940
71981
  content: [
70941
71982
  {
@@ -70949,8 +71990,20 @@ async function resolvePart2(args) {
70949
71990
  async function findAlternatives2(args) {
70950
71991
  const query = typeof args.query === "string" ? args.query : "";
70951
71992
  const alts = await findAlternatives(query);
71993
+ const part = lookupOrderable(query);
71994
+ const orderable_alternatives = part?.alternatives ?? [];
70952
71995
  return {
70953
- content: [{ type: "text", text: JSON.stringify({ query, count: alts.length, alternatives: alts }) }]
71996
+ content: [
71997
+ {
71998
+ type: "text",
71999
+ text: JSON.stringify({
72000
+ query,
72001
+ count: alts.length,
72002
+ alternatives: alts,
72003
+ ...part ? { orderable: { id: part.id, mpn: part.mpn, lcsc: part.lcsc }, orderable_alternatives } : {}
72004
+ })
72005
+ }
72006
+ ]
70954
72007
  };
70955
72008
  }
70956
72009
  async function verifySubstitution2(args) {
@@ -71622,11 +72675,12 @@ async function fixDrc(args, toolCtx) {
71622
72675
  };
71623
72676
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
71624
72677
  }
71625
- var PCB_LAYER_SET, COPPER_LAYERS, COPPER_LAYER_SET, POSITION_TOLERANCE, PIN_SEP, pinKey, createSchematicSchema, docInputProperties, placeComponentsSchema, routeNetsSchema, runDrcSchema, runErcSchema, critiqueRouteSchema, routeDiffPairSchema, lengthMatchTracesSchema, exportGerberSchema, validateForFabSchema, fabPrepSchema, exportKicadSchema, addCoilSchema, boardFromSolidSchema, addCoilArraySchema, windingLayoutSchema, calcImpedanceSchema, sizeImpedanceSchema, sizePdnSchema, calcCoilSchema, sizeCoilSchema, calcRfSchema, COIL_GEOMETRY, MU0, RAIL_PIN_NAMES, PIN_TYPES, CHIP_SIZE_CODES, CONNECTIVITY_GUARD_MAX_NETS, POWER_NET_RE, DRC_CATEGORY, DRC_DELTA_SAMPLE_CAP, DRC_DELTA_FULL_BOARD_MAX, LINT_CRYSTAL_MAX_MM, LINT_DECAP_MAX_MM, LINT_CONNECTOR_EDGE_MAX_MM, LINT_HIGH_CURRENT_SEP_MIN_MM, LINT_GROUND_NET_RE, LINT_SUPPLY_NET_RE, SENSITIVE_NET_RE, HIGH_CURRENT_CLASS_RE, refPrefix, dist2d, round22, sumSq, round33, vec2Schema, setBoardOutlineSchema, FOOTPRINT_FAMILIES, listFootprintsSchema, searchFootprintsSchema, getPadPositionsSchema, getFootprintSchema, ROTATION_CONVENTION, describePcbSchema, DESCRIBE_NET_NAME_CAP, addTraceSchema, addViaSchema, OZ_TO_MM, setStackupSchema, setPlacementSchema, addZoneSchema, deleteElementProps, deleteZoneSchema, deleteTraceSchema, deleteViaSchema, COPPER_KINDS, GET_COPPER_CAP, getCopperSchema, TIE_POSITION_TOL, addNetTieSchema, deleteNetTieSchema, undoSchema, setDesignRulesSchema, sizeTraceForCurrentSchema, addViaArraySchema, MAX_VIA_ARRAY, addMotorWindingSchema, calcMotorSchema, SILICON_STEEL_KNEE_T, sig6, BEARING_FRICTION_MNM, checkSelfStartSchema, solidFromBoardSchema, FR4_DENSITY_KG_M3, COPPER_DENSITY_KG_M3, DEFAULT_COPPER_THICKNESS_MM, loopArea, MOUNTING_FP_RE, KEEPOUT_ECHO_CAP, INLINE_DOC_ECHO_CAP, searchElectronicPartsSchema, resolvePartSchema, findAlternativesSchema, verifySubstitutionSchema, buildReceiptSchema, verifyReceiptSchema, FIX_EPS, FIX_DRC_NEVER, fixDrcSchema, toolDefs23;
72678
+ var PCB_LAYER_SET, COPPER_LAYERS, COPPER_LAYER_SET, POSITION_TOLERANCE, PIN_SEP, pinKey, powerFlagsSchema, sheetInstanceSchema, sheetDefinitionSchema, createSchematicSchema, updateSchematicSchema, inspectSchematicSchema, docInputProperties, placeComponentsSchema, routeNetsSchema, runDrcSchema, runErcSchema, critiqueRouteSchema, routeDiffPairSchema, lengthMatchTracesSchema, exportGerberSchema, validateForFabSchema, fabPrepSchema, exportKicadSchema, addCoilSchema, boardFromSolidSchema, addCoilArraySchema, windingLayoutSchema, calcImpedanceSchema, sizeImpedanceSchema, sizePdnSchema, calcCoilSchema, sizeCoilSchema, calcRfSchema, COIL_GEOMETRY, MU0, RAIL_PIN_NAMES, PIN_TYPES, CHIP_SIZE_CODES, CONNECTIVITY_GUARD_MAX_NETS, POWER_NET_RE, DRC_CATEGORY, DRC_DELTA_SAMPLE_CAP, DRC_DELTA_FULL_BOARD_MAX, LINT_CRYSTAL_MAX_MM, LINT_DECAP_MAX_MM, LINT_CONNECTOR_EDGE_MAX_MM, LINT_HIGH_CURRENT_SEP_MIN_MM, LINT_GROUND_NET_RE, LINT_SUPPLY_NET_RE, SENSITIVE_NET_RE, HIGH_CURRENT_CLASS_RE, refPrefix, dist2d, round22, sumSq, round33, vec2Schema, setBoardOutlineSchema, FOOTPRINT_FAMILIES, listFootprintsSchema, searchFootprintsSchema, getPadPositionsSchema, getFootprintSchema, ROTATION_CONVENTION, describePcbSchema, DESCRIBE_NET_NAME_CAP, addTraceSchema, addViaSchema, OZ_TO_MM, setStackupSchema, setPlacementSchema, addZoneSchema, deleteElementProps, deleteZoneSchema, deleteTraceSchema, deleteViaSchema, COPPER_KINDS, GET_COPPER_CAP, getCopperSchema, TIE_POSITION_TOL, addNetTieSchema, deleteNetTieSchema, undoSchema, setDesignRulesSchema, sizeTraceForCurrentSchema, addViaArraySchema, MAX_VIA_ARRAY, addMotorWindingSchema, calcMotorSchema, SILICON_STEEL_KNEE_T, sig6, BEARING_FRICTION_MNM, checkSelfStartSchema, solidFromBoardSchema, FR4_DENSITY_KG_M3, COPPER_DENSITY_KG_M3, DEFAULT_COPPER_THICKNESS_MM, loopArea, MOUNTING_FP_RE, KEEPOUT_ECHO_CAP, INLINE_DOC_ECHO_CAP, searchElectronicPartsSchema, resolvePartSchema, findAlternativesSchema, verifySubstitutionSchema, buildReceiptSchema, verifyReceiptSchema, FIX_EPS, FIX_DRC_NEVER, fixDrcSchema, toolDefs24;
71626
72679
  var init_ecad2 = __esm({
71627
72680
  "src/tools/ecad.ts"() {
71628
72681
  "use strict";
71629
72682
  init_dist2();
72683
+ init_schematic_hierarchy();
71630
72684
  init_receipt_unified();
71631
72685
  init_clearance();
71632
72686
  init_constraint_claims();
@@ -71635,6 +72689,7 @@ var init_ecad2 = __esm({
71635
72689
  init_session2();
71636
72690
  init_kernel_event();
71637
72691
  init_em_claims();
72692
+ init_part_stock();
71638
72693
  init_enclosure();
71639
72694
  init_pcb_validate();
71640
72695
  init_pcb_layers2();
@@ -71648,6 +72703,80 @@ var init_ecad2 = __esm({
71648
72703
  POSITION_TOLERANCE = 0.01;
71649
72704
  PIN_SEP = String.fromCharCode(0);
71650
72705
  pinKey = (ref, pinNumber) => `${ref}${PIN_SEP}${pinNumber}`;
72706
+ powerFlagsSchema = {
72707
+ type: "array",
72708
+ description: "Power flags (KiCad PWR_FLAG): nets declared as DRIVEN although no PowerOutput pin sits on them \u2014 a rail fed through a ferrite, a load-switch FET or a connector. Each is a net name or {net, note}. ERC counts a flagged net as powered; flags are not parts, so they never reach the BOM or board.",
72709
+ items: {
72710
+ anyOf: [
72711
+ { type: "string" },
72712
+ {
72713
+ type: "object",
72714
+ properties: {
72715
+ net: { type: "string" },
72716
+ note: { type: "string" }
72717
+ },
72718
+ required: ["net"]
72719
+ }
72720
+ ]
72721
+ }
72722
+ };
72723
+ sheetInstanceSchema = {
72724
+ type: "object",
72725
+ properties: {
72726
+ name: { type: "string", description: 'Instance name, e.g. "CAN1" (no "/").' },
72727
+ sheet: { type: "string", description: "Sheet definition name." },
72728
+ connections: {
72729
+ type: "object",
72730
+ description: 'Port \u2192 parent-sheet net, e.g. {"TXD": "FDCAN1_TX"}.',
72731
+ additionalProperties: { type: "string" }
72732
+ },
72733
+ ref_offset: {
72734
+ type: "number",
72735
+ description: "Renumber refs (U1 + 100 \u2192 U101). Default: a sheet placed once keeps its refs; a repeated sheet gets <ref>_<instance> (U1_CAN1)."
72736
+ },
72737
+ ref_map: {
72738
+ type: "object",
72739
+ description: 'Explicit local \u2192 flat refs, e.g. {"U1": "U31"}; wins over ref_offset.',
72740
+ additionalProperties: { type: "string" }
72741
+ }
72742
+ },
72743
+ required: ["name", "sheet"]
72744
+ };
72745
+ sheetDefinitionSchema = {
72746
+ type: "object",
72747
+ properties: {
72748
+ name: { type: "string", description: "Sheet definition name." },
72749
+ title: { type: "string" },
72750
+ ports: {
72751
+ type: "array",
72752
+ description: 'Hierarchical ports: names (or {name, direction}) of nets the parent connects, e.g. ["TXD", "RXD"].',
72753
+ items: {
72754
+ anyOf: [
72755
+ { type: "string" },
72756
+ {
72757
+ type: "object",
72758
+ properties: {
72759
+ name: { type: "string" },
72760
+ direction: { type: "string", description: "Input, Output, Bidirectional, Passive, PowerInput, PowerOutput" }
72761
+ },
72762
+ required: ["name"]
72763
+ }
72764
+ ]
72765
+ }
72766
+ },
72767
+ components: { type: "array", description: "As create_schematic.components.", items: { type: "object" } },
72768
+ nets: {
72769
+ type: "object",
72770
+ description: "As create_schematic.nets, in this sheet's scope.",
72771
+ additionalProperties: { type: "array", items: { type: "string" } }
72772
+ },
72773
+ labels: { type: "array", items: { type: "object" } },
72774
+ wires: { type: "array", items: { type: "object" } },
72775
+ power_flags: powerFlagsSchema,
72776
+ instances: { type: "array", description: "Nested child sheet instances.", items: { type: "object" } }
72777
+ },
72778
+ required: ["name", "components"]
72779
+ };
71651
72780
  createSchematicSchema = {
71652
72781
  type: "object",
71653
72782
  properties: {
@@ -71757,10 +72886,74 @@ var init_ecad2 = __esm({
71757
72886
  type: "array",
71758
72887
  items: { type: "string" }
71759
72888
  }
72889
+ },
72890
+ power_flags: powerFlagsSchema,
72891
+ sheets: {
72892
+ type: "array",
72893
+ description: "Hierarchical child sheets (definitions). Each is a create_schematic-shaped body (components, nets, labels, wires, power_flags) plus `name`, `ports` and nested `instances`. Inside a sheet a net name is LOCAL to each instance (flattens to `<instance path>/<net>`, e.g. `CAN1/CANH`) unless it is a port (resolved through the instance's `connections`) or global (`global_nets`, or a Global label). The root sheet is this call's own components/nets.",
72894
+ items: sheetDefinitionSchema
72895
+ },
72896
+ instances: {
72897
+ type: "array",
72898
+ description: "Sheet instances placed on the root sheet. Place a sheet several times (CAN \xD76) \u2014 each instance gets its own local nets and reference designators.",
72899
+ items: sheetInstanceSchema
72900
+ },
72901
+ global_nets: {
72902
+ type: "array",
72903
+ description: 'Net names shared by every sheet, e.g. ["GND", "+3V3", "VBAT"].',
72904
+ items: { type: "string" }
71760
72905
  }
71761
72906
  },
71762
72907
  required: ["components"]
71763
72908
  };
72909
+ updateSchematicSchema = {
72910
+ type: "object",
72911
+ properties: {
72912
+ document_id: { type: "string", description: "Session id from create_schematic." },
72913
+ root: {
72914
+ type: "object",
72915
+ description: "Replace the ROOT sheet body (create_schematic-shaped: components, nets, labels, wires, power_flags). The hierarchy is kept."
72916
+ },
72917
+ sheets: {
72918
+ type: "array",
72919
+ description: "Sheet definitions to add, or replace by `name`.",
72920
+ items: sheetDefinitionSchema
72921
+ },
72922
+ remove_sheets: {
72923
+ type: "array",
72924
+ description: "Sheet definition names to delete.",
72925
+ items: { type: "string" }
72926
+ },
72927
+ instances: {
72928
+ type: "array",
72929
+ description: "Root sheet instances to add, or replace by `name`.",
72930
+ items: sheetInstanceSchema
72931
+ },
72932
+ remove_instances: {
72933
+ type: "array",
72934
+ description: "Root sheet instance names to delete.",
72935
+ items: { type: "string" }
72936
+ },
72937
+ global_nets: {
72938
+ type: "array",
72939
+ description: "Replace the global net list.",
72940
+ items: { type: "string" }
72941
+ },
72942
+ power_flags: { ...powerFlagsSchema, description: "Replace the ROOT sheet's power flags." }
72943
+ },
72944
+ required: ["document_id"]
72945
+ };
72946
+ inspectSchematicSchema = {
72947
+ type: "object",
72948
+ properties: {
72949
+ document_id: { type: "string", description: "Session id from create_schematic." },
72950
+ instance: {
72951
+ type: "string",
72952
+ description: 'Limit nets/refs to one sheet instance path (e.g. "CAN1" or "PWR/BUCK2").'
72953
+ }
72954
+ },
72955
+ required: ["document_id"]
72956
+ };
71764
72957
  docInputProperties = {
71765
72958
  document_id: {
71766
72959
  type: "string",
@@ -73233,7 +74426,7 @@ var init_ecad2 = __esm({
73233
74426
  },
73234
74427
  required: ["document_id"]
73235
74428
  };
73236
- toolDefs23 = [
74429
+ toolDefs24 = [
73237
74430
  {
73238
74431
  name: "create_schematic",
73239
74432
  pack: "ecad",
@@ -73242,6 +74435,22 @@ var init_ecad2 = __esm({
73242
74435
  handler: (a) => createSchematic(a),
73243
74436
  behavior: behavior({ writesDoc: true })
73244
74437
  },
74438
+ {
74439
+ name: "update_schematic",
74440
+ pack: "ecad",
74441
+ description: "Edit a schematic session in place: replace the root sheet body, add / replace / remove hierarchical sheet definitions and root sheet instances, set the global nets, or set power flags. Returns the re-flattened netlist and any hierarchy issues, like create_schematic.",
74442
+ inputSchema: updateSchematicSchema,
74443
+ handler: (a) => updateSchematic(a),
74444
+ behavior: behavior({ writesDoc: true })
74445
+ },
74446
+ {
74447
+ name: "inspect_schematic",
74448
+ pack: "ecad",
74449
+ description: "Inspect a schematic session: sheet tree, per-instance reference maps, the flattened netlist (optionally one instance's), power flags, hierarchy issues, and a BOM grouping of the flattened parts.",
74450
+ inputSchema: inspectSchematicSchema,
74451
+ handler: (a) => inspectSchematic(a),
74452
+ behavior: behavior({})
74453
+ },
73245
74454
  {
73246
74455
  name: "place_components",
73247
74456
  pack: "ecad",
@@ -73895,7 +75104,7 @@ async function solveConstraints(args) {
73895
75104
  }
73896
75105
  return solveAndReport(ctx.doc, ctx.documentId);
73897
75106
  }
73898
- var anchorDescription, addConstraintSchema, deleteConstraintSchema, listConstraintsSchema, solveConstraintsSchema, toolDefs24;
75107
+ var anchorDescription, addConstraintSchema, deleteConstraintSchema, listConstraintsSchema, solveConstraintsSchema, toolDefs25;
73899
75108
  var init_design_constraints = __esm({
73900
75109
  "src/tools/design-constraints.ts"() {
73901
75110
  "use strict";
@@ -73940,7 +75149,7 @@ var init_design_constraints = __esm({
73940
75149
  },
73941
75150
  required: ["document_id"]
73942
75151
  };
73943
- toolDefs24 = [
75152
+ toolDefs25 = [
73944
75153
  {
73945
75154
  name: "add_constraint",
73946
75155
  pack: null,
@@ -74174,7 +75383,7 @@ function recordMeasurement(input) {
74174
75383
  }
74175
75384
  return jsonResult3(report);
74176
75385
  }
74177
- var MEASURABLE_KINDS, MEASURABLE_AXES, MEASURABLE_FEATURES, predictions, reports, predictPrintSchema, recordMeasurementSchema, jsonResult3, toolDefs25;
75386
+ var MEASURABLE_KINDS, MEASURABLE_AXES, MEASURABLE_FEATURES, predictions, reports, predictPrintSchema, recordMeasurementSchema, jsonResult3, toolDefs26;
74178
75387
  var init_print_check = __esm({
74179
75388
  "src/tools/print-check.ts"() {
74180
75389
  "use strict";
@@ -74277,7 +75486,7 @@ var init_print_check = __esm({
74277
75486
  jsonResult3 = (value) => ({
74278
75487
  content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
74279
75488
  });
74280
- toolDefs25 = [
75489
+ toolDefs26 = [
74281
75490
  {
74282
75491
  name: "predict_print",
74283
75492
  pack: "print",
@@ -75148,7 +76357,7 @@ async function renderStackup(args) {
75148
76357
  });
75149
76358
  return withRenderAssets({ content }, assets);
75150
76359
  }
75151
- var renderViewSchema, SVG_SCALE, DEFAULT_WIDTH_PX, MAX_NODES, MAX_PATTERN_INSTANCES, MAX_RENDER_TRIANGLES, MAX_RASTER_SVG_BYTES, MAX_INLINE_SVG_BYTES, renderPcbSchema, PCB_MARGIN_MM, RATSNEST_COLOR, renderRatsnestSchema, renderStackupSchema, toolDefs26;
76360
+ var renderViewSchema, SVG_SCALE, DEFAULT_WIDTH_PX, MAX_NODES, MAX_PATTERN_INSTANCES, MAX_RENDER_TRIANGLES, MAX_RASTER_SVG_BYTES, MAX_INLINE_SVG_BYTES, renderPcbSchema, PCB_MARGIN_MM, RATSNEST_COLOR, renderRatsnestSchema, renderStackupSchema, toolDefs27;
75152
76361
  var init_render2 = __esm({
75153
76362
  "src/tools/render.ts"() {
75154
76363
  "use strict";
@@ -75311,7 +76520,7 @@ var init_render2 = __esm({
75311
76520
  },
75312
76521
  required: ["document_id"]
75313
76522
  };
75314
- toolDefs26 = [
76523
+ toolDefs27 = [
75315
76524
  {
75316
76525
  name: "render_view",
75317
76526
  pack: null,
@@ -75517,7 +76726,7 @@ async function verifyPart(args) {
75517
76726
  rmSync(scratch, { recursive: true, force: true });
75518
76727
  }
75519
76728
  }
75520
- var execFileAsync, verifyPartSchema, listEvalTasksSchema, toolDefs27;
76729
+ var execFileAsync, verifyPartSchema, listEvalTasksSchema, toolDefs28;
75521
76730
  var init_verify = __esm({
75522
76731
  "src/tools/verify.ts"() {
75523
76732
  "use strict";
@@ -75548,7 +76757,7 @@ var init_verify = __esm({
75548
76757
  }
75549
76758
  }
75550
76759
  };
75551
- toolDefs27 = [
76760
+ toolDefs28 = [
75552
76761
  {
75553
76762
  name: "verify_part",
75554
76763
  pack: "eval",
@@ -75608,7 +76817,7 @@ function verifySpec(input, engine) {
75608
76817
  ]
75609
76818
  };
75610
76819
  }
75611
- var pointSpecSchema, verifySpecSchema, toolDefs28;
76820
+ var pointSpecSchema, verifySpecSchema, toolDefs29;
75612
76821
  var init_verify_spec = __esm({
75613
76822
  "src/tools/verify-spec.ts"() {
75614
76823
  "use strict";
@@ -75672,7 +76881,7 @@ var init_verify_spec = __esm({
75672
76881
  },
75673
76882
  required: ["document_id", "spec"]
75674
76883
  };
75675
- toolDefs28 = [
76884
+ toolDefs29 = [
75676
76885
  {
75677
76886
  name: "verify_spec",
75678
76887
  pack: null,
@@ -75814,7 +77023,7 @@ function topologyOptimizeTool(args, engine) {
75814
77023
  ]
75815
77024
  };
75816
77025
  }
75817
- var regionSchema, topologyOptimizeSchema, round5, toolDefs29;
77026
+ var regionSchema, topologyOptimizeSchema, round5, toolDefs30;
75818
77027
  var init_topopt = __esm({
75819
77028
  "src/tools/topopt.ts"() {
75820
77029
  "use strict";
@@ -75926,7 +77135,7 @@ var init_topopt = __esm({
75926
77135
  }
75927
77136
  };
75928
77137
  round5 = (v) => Number(v.toPrecision(5));
75929
- toolDefs29 = [
77138
+ toolDefs30 = [
75930
77139
  {
75931
77140
  name: "topology_optimize",
75932
77141
  pack: null,
@@ -75950,7 +77159,7 @@ function textResult2(payload) {
75950
77159
  ]
75951
77160
  };
75952
77161
  }
75953
- var paramValue, deviceSpecSchema, parametersSchema, simOptionsSchema, simulateChargedParticlesSchema, optimizeElectrodesSchema, toolDefs30;
77162
+ var paramValue, deviceSpecSchema, parametersSchema, simOptionsSchema, simulateChargedParticlesSchema, optimizeElectrodesSchema, toolDefs31;
75954
77163
  var init_particle = __esm({
75955
77164
  "src/tools/particle.ts"() {
75956
77165
  "use strict";
@@ -76084,7 +77293,7 @@ var init_particle = __esm({
76084
77293
  }
76085
77294
  }
76086
77295
  };
76087
- toolDefs30 = [
77296
+ toolDefs31 = [
76088
77297
  {
76089
77298
  name: "simulate_charged_particles",
76090
77299
  pack: null,
@@ -76135,7 +77344,7 @@ function textResult3(payload) {
76135
77344
  ]
76136
77345
  };
76137
77346
  }
76138
- var paramValue2, distSchema, stackupSpecSchema, toolDefs31;
77347
+ var paramValue2, distSchema, stackupSpecSchema, toolDefs32;
76139
77348
  var init_tolerance = __esm({
76140
77349
  "src/tools/tolerance.ts"() {
76141
77350
  "use strict";
@@ -76223,7 +77432,7 @@ var init_tolerance = __esm({
76223
77432
  }
76224
77433
  }
76225
77434
  };
76226
- toolDefs31 = [
77435
+ toolDefs32 = [
76227
77436
  {
76228
77437
  name: "analyze_tolerance_stackup",
76229
77438
  pack: null,
@@ -76283,7 +77492,7 @@ function textResult4(payload) {
76283
77492
  ]
76284
77493
  };
76285
77494
  }
76286
- var paramValue3, paramVec3, shapeSchema, boundarySchema, thermalSpecSchema, transientSchema, toolDefs32;
77495
+ var paramValue3, paramVec3, shapeSchema, boundarySchema, thermalSpecSchema, transientSchema, toolDefs33;
76287
77496
  var init_thermal = __esm({
76288
77497
  "src/tools/thermal.ts"() {
76289
77498
  "use strict";
@@ -76450,7 +77659,7 @@ var init_thermal = __esm({
76450
77659
  }
76451
77660
  }
76452
77661
  };
76453
- toolDefs32 = [
77662
+ toolDefs33 = [
76454
77663
  {
76455
77664
  name: "solve_thermal",
76456
77665
  pack: null,
@@ -76512,7 +77721,7 @@ function textResult5(payload) {
76512
77721
  ]
76513
77722
  };
76514
77723
  }
76515
- var vec32, shapeSchema2, flowSpecSchema, toolDefs33;
77724
+ var vec32, shapeSchema2, flowSpecSchema, toolDefs34;
76516
77725
  var init_flow = __esm({
76517
77726
  "src/tools/flow.ts"() {
76518
77727
  "use strict";
@@ -76646,7 +77855,7 @@ var init_flow = __esm({
76646
77855
  }
76647
77856
  }
76648
77857
  };
76649
- toolDefs33 = [
77858
+ toolDefs34 = [
76650
77859
  {
76651
77860
  name: "simulate_flow",
76652
77861
  pack: null,
@@ -76728,7 +77937,7 @@ function frequencyGrid(ac) {
76728
77937
  }
76729
77938
  return out;
76730
77939
  }
76731
- var deviceSchema, devicesSchema, toolDefs34;
77940
+ var deviceSchema, devicesSchema, toolDefs35;
76732
77941
  var init_circuit = __esm({
76733
77942
  "src/tools/circuit.ts"() {
76734
77943
  "use strict";
@@ -76765,7 +77974,7 @@ var init_circuit = __esm({
76765
77974
  items: deviceSchema,
76766
77975
  description: "Netlist as data. Node ids are dense integers with 0 = ground; the device's index in this array is its device id (used by sourceId / freeDevices / outNode references)."
76767
77976
  };
76768
- toolDefs34 = [
77977
+ toolDefs35 = [
76769
77978
  {
76770
77979
  name: "simulate_circuit",
76771
77980
  pack: null,
@@ -76956,7 +78165,7 @@ function textResult7(payload) {
76956
78165
  ]
76957
78166
  };
76958
78167
  }
76959
- var regionSchema2, toolDefs35;
78168
+ var regionSchema2, toolDefs36;
76960
78169
  var init_structure = __esm({
76961
78170
  "src/tools/structure.ts"() {
76962
78171
  "use strict";
@@ -76983,7 +78192,7 @@ var init_structure = __esm({
76983
78192
  }
76984
78193
  }
76985
78194
  };
76986
- toolDefs35 = [
78195
+ toolDefs36 = [
76987
78196
  {
76988
78197
  name: "analyze_structure",
76989
78198
  pack: null,
@@ -77202,7 +78411,7 @@ function textResult8(payload) {
77202
78411
  ]
77203
78412
  };
77204
78413
  }
77205
- var paramValue4, rectRegion, bcSchema, electroShape, emSpecSchema, toolDefs36;
78414
+ var paramValue4, rectRegion, bcSchema, electroShape, emSpecSchema, toolDefs37;
77206
78415
  var init_em = __esm({
77207
78416
  "src/tools/em.ts"() {
77208
78417
  "use strict";
@@ -77358,7 +78567,7 @@ var init_em = __esm({
77358
78567
  }
77359
78568
  }
77360
78569
  };
77361
- toolDefs36 = [
78570
+ toolDefs37 = [
77362
78571
  {
77363
78572
  name: "simulate_em",
77364
78573
  pack: null,
@@ -77471,7 +78680,7 @@ function textResult9(payload) {
77471
78680
  ]
77472
78681
  };
77473
78682
  }
77474
- var paramValue5, pointSpec, antennaSpecSchema, toolDefs37;
78683
+ var paramValue5, pointSpec, antennaSpecSchema, toolDefs38;
77475
78684
  var init_antenna = __esm({
77476
78685
  "src/tools/antenna.ts"() {
77477
78686
  "use strict";
@@ -77520,7 +78729,7 @@ var init_antenna = __esm({
77520
78729
  feed_mm: pointSpec
77521
78730
  }
77522
78731
  };
77523
- toolDefs37 = [
78732
+ toolDefs38 = [
77524
78733
  {
77525
78734
  name: "analyze_antenna",
77526
78735
  pack: null,
@@ -77597,7 +78806,7 @@ function textResult10(payload) {
77597
78806
  ]
77598
78807
  };
77599
78808
  }
77600
- var photonicsSpecSchema, toolDefs38;
78809
+ var photonicsSpecSchema, toolDefs39;
77601
78810
  var init_photonics = __esm({
77602
78811
  "src/tools/photonics.ts"() {
77603
78812
  "use strict";
@@ -77683,7 +78892,7 @@ var init_photonics = __esm({
77683
78892
  }
77684
78893
  }
77685
78894
  };
77686
- toolDefs38 = [
78895
+ toolDefs39 = [
77687
78896
  {
77688
78897
  name: "simulate_photonics",
77689
78898
  pack: null,
@@ -77749,7 +78958,7 @@ function textResult11(payload) {
77749
78958
  ]
77750
78959
  };
77751
78960
  }
77752
- var paramValue6, shieldSpecSchema, toolDefs39;
78961
+ var paramValue6, shieldSpecSchema, toolDefs40;
77753
78962
  var init_neutronics = __esm({
77754
78963
  "src/tools/neutronics.ts"() {
77755
78964
  "use strict";
@@ -77820,7 +79029,7 @@ var init_neutronics = __esm({
77820
79029
  }
77821
79030
  }
77822
79031
  };
77823
- toolDefs39 = [
79032
+ toolDefs40 = [
77824
79033
  {
77825
79034
  name: "simulate_neutron_shield",
77826
79035
  pack: null,
@@ -77862,7 +79071,7 @@ function textResult12(payload) {
77862
79071
  ]
77863
79072
  };
77864
79073
  }
77865
- var simSpecSchema, toolDefs40;
79074
+ var simSpecSchema, toolDefs41;
77866
79075
  var init_qcd = __esm({
77867
79076
  "src/tools/qcd.ts"() {
77868
79077
  "use strict";
@@ -77952,7 +79161,7 @@ var init_qcd = __esm({
77952
79161
  }
77953
79162
  }
77954
79163
  };
77955
- toolDefs40 = [
79164
+ toolDefs41 = [
77956
79165
  {
77957
79166
  name: "simulate_lattice_gauge",
77958
79167
  pack: null,
@@ -78094,7 +79303,7 @@ function predictPhysicsTool(args, engine) {
78094
79303
  ]
78095
79304
  };
78096
79305
  }
78097
- var PHYSICS_DOMAIN, ORACLE, TIER_RESOLUTION, regionSchema3, predictPhysicsSchema, round52, toolDefs41;
79306
+ var PHYSICS_DOMAIN, ORACLE, TIER_RESOLUTION, regionSchema3, predictPhysicsSchema, round52, toolDefs42;
78098
79307
  var init_physics2 = __esm({
78099
79308
  "src/tools/physics.ts"() {
78100
79309
  "use strict";
@@ -78207,7 +79416,7 @@ var init_physics2 = __esm({
78207
79416
  }
78208
79417
  };
78209
79418
  round52 = (v) => Number(v.toPrecision(5));
78210
- toolDefs41 = [
79419
+ toolDefs42 = [
78211
79420
  {
78212
79421
  name: "predict_physics",
78213
79422
  pack: null,
@@ -78451,7 +79660,7 @@ function applySetParam(doc, node, path, value) {
78451
79660
  }
78452
79661
  cursor[parts[parts.length - 1]] = value;
78453
79662
  }
78454
- var lastReports, mechanicalProcessEnum, pcbProcessEnum, processEnum, rulesetEnum, dfmCheckSchema, dfmExplainSchema, dfmSuggestFixSchema, dfmApplyFixSchema, toolDefs42;
79663
+ var lastReports, mechanicalProcessEnum, pcbProcessEnum, processEnum, rulesetEnum, dfmCheckSchema, dfmExplainSchema, dfmSuggestFixSchema, dfmApplyFixSchema, toolDefs43;
78455
79664
  var init_dfm2 = __esm({
78456
79665
  "src/tools/dfm.ts"() {
78457
79666
  "use strict";
@@ -78554,7 +79763,7 @@ var init_dfm2 = __esm({
78554
79763
  },
78555
79764
  required: ["issue_id"]
78556
79765
  };
78557
- toolDefs42 = [
79766
+ toolDefs43 = [
78558
79767
  {
78559
79768
  name: "dfm_check",
78560
79769
  pack: "dfm",
@@ -79053,7 +80262,7 @@ function sheetMetalCost(input, engine) {
79053
80262
  summary: `${result.breakdown.currency} ${result.breakdown.total_each.toFixed(2)} each @ qty ${result.breakdown.quantity} (mass ${result.breakdown.mass_kg_each.toFixed(3)} kg, ${result.breakdown.cut_length_m.toFixed(2)} m cut, ${result.breakdown.bends} bend(s)).`
79054
80263
  });
79055
80264
  }
79056
- var sheetMetalCreateSchema, sheetMetalUnfoldSchema, sheetMetalMaterialsSchema, sheetMetalBendTableSchema, sheetMetalCheckSchema, sheetMetalNestSchema, sheetMetalSequenceSchema, sheetMetalSuggestFixSchema, sheetMetalCostSchema, toolDefs43;
80265
+ var sheetMetalCreateSchema, sheetMetalUnfoldSchema, sheetMetalMaterialsSchema, sheetMetalBendTableSchema, sheetMetalCheckSchema, sheetMetalNestSchema, sheetMetalSequenceSchema, sheetMetalSuggestFixSchema, sheetMetalCostSchema, toolDefs44;
79057
80266
  var init_sheet_metal2 = __esm({
79058
80267
  "src/tools/sheet-metal.ts"() {
79059
80268
  "use strict";
@@ -79314,7 +80523,7 @@ var init_sheet_metal2 = __esm({
79314
80523
  },
79315
80524
  required: ["document_id"]
79316
80525
  };
79317
- toolDefs43 = [
80526
+ toolDefs44 = [
79318
80527
  {
79319
80528
  name: "sheet_metal_create",
79320
80529
  pack: "sheet_metal",
@@ -79554,7 +80763,7 @@ function flatPatternFromSolid(input, engine) {
79554
80763
  ]
79555
80764
  };
79556
80765
  }
79557
- var flatPatternFromSolidSchema, toolDefs44;
80766
+ var flatPatternFromSolidSchema, toolDefs45;
79558
80767
  var init_flat_pattern = __esm({
79559
80768
  "src/tools/flat-pattern.ts"() {
79560
80769
  "use strict";
@@ -79594,7 +80803,7 @@ var init_flat_pattern = __esm({
79594
80803
  },
79595
80804
  required: ["document_id"]
79596
80805
  };
79597
- toolDefs44 = [
80806
+ toolDefs45 = [
79598
80807
  {
79599
80808
  name: "flat_pattern_from_solid",
79600
80809
  pack: "sheet_metal",
@@ -79775,7 +80984,7 @@ function simulateStrike(input, engine) {
79775
80984
  }
79776
80985
  return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
79777
80986
  }
79778
- var cents, MATERIAL_ALIASES, simulateStrikeSchema, round23, round4, toolDefs45;
80987
+ var cents, MATERIAL_ALIASES, simulateStrikeSchema, round23, round4, toolDefs46;
79779
80988
  var init_acoustics = __esm({
79780
80989
  "src/tools/acoustics.ts"() {
79781
80990
  "use strict";
@@ -79849,7 +81058,7 @@ var init_acoustics = __esm({
79849
81058
  };
79850
81059
  round23 = (x) => Math.round(x * 100) / 100;
79851
81060
  round4 = (x) => Math.round(x * 1e4) / 1e4;
79852
- toolDefs45 = [
81061
+ toolDefs46 = [
79853
81062
  {
79854
81063
  name: "simulate_strike",
79855
81064
  pack: "sheet_metal",
@@ -80129,7 +81338,7 @@ function importStep(input, engine) {
80129
81338
  ]
80130
81339
  };
80131
81340
  }
80132
- var MAX_STEP_BYTES, STEP_CACHE_DIR, importStepSchema, toolDefs46;
81341
+ var MAX_STEP_BYTES, STEP_CACHE_DIR, importStepSchema, toolDefs47;
80133
81342
  var init_import = __esm({
80134
81343
  "src/tools/import.ts"() {
80135
81344
  "use strict";
@@ -80166,7 +81375,7 @@ var init_import = __esm({
80166
81375
  }
80167
81376
  }
80168
81377
  };
80169
- toolDefs46 = [
81378
+ toolDefs47 = [
80170
81379
  {
80171
81380
  name: "import_step",
80172
81381
  pack: null,
@@ -80456,7 +81665,7 @@ async function importEagle(input) {
80456
81665
  ]
80457
81666
  };
80458
81667
  }
80459
- var MAX_PCB_BYTES, importKicadSchema, importAltiumSchema, importAltiumLibrarySchema, importEagleSchema, toolDefs47;
81668
+ var MAX_PCB_BYTES, importKicadSchema, importAltiumSchema, importAltiumLibrarySchema, importEagleSchema, toolDefs48;
80460
81669
  var init_import_pcb = __esm({
80461
81670
  "src/tools/import-pcb.ts"() {
80462
81671
  "use strict";
@@ -80531,7 +81740,7 @@ var init_import_pcb = __esm({
80531
81740
  }
80532
81741
  }
80533
81742
  };
80534
- toolDefs47 = [
81743
+ toolDefs48 = [
80535
81744
  {
80536
81745
  name: "import_kicad",
80537
81746
  pack: "ecad",
@@ -80644,7 +81853,7 @@ Compressed URL param: ${encoded.length} bytes`
80644
81853
  ]
80645
81854
  };
80646
81855
  }
80647
- var openInBrowserSchema, toolDefs48;
81856
+ var openInBrowserSchema, toolDefs49;
80648
81857
  var init_share = __esm({
80649
81858
  "src/tools/share.ts"() {
80650
81859
  "use strict";
@@ -80668,7 +81877,7 @@ var init_share = __esm({
80668
81877
  }
80669
81878
  }
80670
81879
  };
80671
- toolDefs48 = [
81880
+ toolDefs49 = [
80672
81881
  {
80673
81882
  name: "open_in_browser",
80674
81883
  pack: null,
@@ -81152,7 +82361,7 @@ function batchReset(input) {
81152
82361
  };
81153
82362
  }
81154
82363
  }
81155
- var simulations, MAX_TRAJECTORY, envRecords, batchGroups, INLINE_DOC_DESC, GROUND_SCHEMA_PROPS, SESSION_DOC_DESC, createRobotEnvSchema, gymStepSchema, gymResetSchema, gymObserveSchema, gymCloseSchema, batchCreateEnvsSchema, batchStepSchema, batchResetSchema, toolDefs49;
82364
+ var simulations, MAX_TRAJECTORY, envRecords, batchGroups, INLINE_DOC_DESC, GROUND_SCHEMA_PROPS, SESSION_DOC_DESC, createRobotEnvSchema, gymStepSchema, gymResetSchema, gymObserveSchema, gymCloseSchema, batchCreateEnvsSchema, batchStepSchema, batchResetSchema, toolDefs50;
81156
82365
  var init_gym = __esm({
81157
82366
  "src/tools/gym.ts"() {
81158
82367
  "use strict";
@@ -81354,7 +82563,7 @@ var init_gym = __esm({
81354
82563
  },
81355
82564
  required: ["batch_id"]
81356
82565
  };
81357
- toolDefs49 = [
82566
+ toolDefs50 = [
81358
82567
  {
81359
82568
  name: "create_robot_env",
81360
82569
  pack: "physics",
@@ -81723,7 +82932,7 @@ async function renderMolecule(input) {
81723
82932
  return fail(err9);
81724
82933
  }
81725
82934
  }
81726
- var loadStructureSchema, inspectMoleculeSchema, configSchema, minimizeEnergySchema, mdRunSchema, designMaterialSchema, homogenizeMaterialSchema, renderMoleculeSchema, CPK, DEFAULT_RADIUS, toolDefs50;
82935
+ var loadStructureSchema, inspectMoleculeSchema, configSchema, minimizeEnergySchema, mdRunSchema, designMaterialSchema, homogenizeMaterialSchema, renderMoleculeSchema, CPK, DEFAULT_RADIUS, toolDefs51;
81727
82936
  var init_atoms2 = __esm({
81728
82937
  "src/tools/atoms.ts"() {
81729
82938
  "use strict";
@@ -81858,7 +83067,7 @@ var init_atoms2 = __esm({
81858
83067
  S: 1.05,
81859
83068
  P: 1.07
81860
83069
  };
81861
- toolDefs50 = [
83070
+ toolDefs51 = [
81862
83071
  {
81863
83072
  name: "load_structure",
81864
83073
  pack: "atoms",
@@ -83066,7 +84275,7 @@ function errorResult(text) {
83066
84275
  isError: true
83067
84276
  };
83068
84277
  }
83069
- var recordSimulationSchema, SVG_SCALE2, MAX_STEPS, MIN_STEPS, DEFAULT_WIDTH_PX2, MIN_WIDTH_PX, MAX_WIDTH_PX, DEFAULT_FPS, MIN_FPS, MAX_FPS, toolDefs51;
84278
+ var recordSimulationSchema, SVG_SCALE2, MAX_STEPS, MIN_STEPS, DEFAULT_WIDTH_PX2, MIN_WIDTH_PX, MAX_WIDTH_PX, DEFAULT_FPS, MIN_FPS, MAX_FPS, toolDefs52;
83070
84279
  var init_record2 = __esm({
83071
84280
  "src/tools/record.ts"() {
83072
84281
  "use strict";
@@ -83130,7 +84339,7 @@ var init_record2 = __esm({
83130
84339
  DEFAULT_FPS = 30;
83131
84340
  MIN_FPS = 1;
83132
84341
  MAX_FPS = 60;
83133
- toolDefs51 = [
84342
+ toolDefs52 = [
83134
84343
  {
83135
84344
  name: "record_simulation",
83136
84345
  pack: "physics",
@@ -83223,7 +84432,7 @@ ${text}`
83223
84432
  ]
83224
84433
  };
83225
84434
  }
83226
- var getChangelogSchema, toolDefs52;
84435
+ var getChangelogSchema, toolDefs53;
83227
84436
  var init_changelog2 = __esm({
83228
84437
  "src/tools/changelog.ts"() {
83229
84438
  "use strict";
@@ -83263,7 +84472,7 @@ var init_changelog2 = __esm({
83263
84472
  }
83264
84473
  }
83265
84474
  };
83266
- toolDefs52 = [
84475
+ toolDefs53 = [
83267
84476
  {
83268
84477
  name: "get_changelog",
83269
84478
  pack: null,
@@ -83412,7 +84621,7 @@ function getSimVersion(input) {
83412
84621
  ]
83413
84622
  };
83414
84623
  }
83415
- var getSimReplaySchema, getSimVersionSchema, toolDefs53;
84624
+ var getSimReplaySchema, getSimVersionSchema, toolDefs54;
83416
84625
  var init_sim_replay = __esm({
83417
84626
  "src/tools/sim-replay.ts"() {
83418
84627
  "use strict";
@@ -83440,7 +84649,7 @@ var init_sim_replay = __esm({
83440
84649
  },
83441
84650
  required: ["env_id"]
83442
84651
  };
83443
- toolDefs53 = [
84652
+ toolDefs54 = [
83444
84653
  {
83445
84654
  name: "get_sim_replay",
83446
84655
  pack: null,
@@ -83559,7 +84768,7 @@ async function getOrderFeed(input, store, user) {
83559
84768
  version: version2
83560
84769
  });
83561
84770
  }
83562
- var toUsd4, getOrderFeedSchema, toolDefs54;
84771
+ var toUsd4, getOrderFeedSchema, toolDefs55;
83563
84772
  var init_order_feed = __esm({
83564
84773
  "src/tools/order-feed.ts"() {
83565
84774
  "use strict";
@@ -83578,7 +84787,7 @@ var init_order_feed = __esm({
83578
84787
  },
83579
84788
  required: ["document_id"]
83580
84789
  };
83581
- toolDefs54 = [
84790
+ toolDefs55 = [
83582
84791
  {
83583
84792
  name: "get_order_feed",
83584
84793
  pack: "fabricate",
@@ -84484,7 +85693,7 @@ ${issues.map((i) => i.problem).join("\n")}`
84484
85693
  note: "Rollout compiled to the document timeline. Preview with render_sequence, ship with export_video."
84485
85694
  });
84486
85695
  }
84487
- var MAX_FRAMES, MAX_GEO_SAMPLES, MAX_VERIFY_FRAMES, INLINE_GLB_MAX_BASE64, DEFAULT_WIDTH_PX3, MIN_WIDTH_PX2, MAX_WIDTH_PX2, SVG_SCALE3, KEYFRAMES_DESCRIPTION, animateSchema, renderSequenceSchema, CAMERA_NODE, exportVideoSchema, ffmpegAvailable, timelineFromSimulationSchema, toolDefs55;
85696
+ var MAX_FRAMES, MAX_GEO_SAMPLES, MAX_VERIFY_FRAMES, INLINE_GLB_MAX_BASE64, DEFAULT_WIDTH_PX3, MIN_WIDTH_PX2, MAX_WIDTH_PX2, SVG_SCALE3, KEYFRAMES_DESCRIPTION, animateSchema, renderSequenceSchema, CAMERA_NODE, exportVideoSchema, ffmpegAvailable, timelineFromSimulationSchema, toolDefs56;
84488
85697
  var init_animate = __esm({
84489
85698
  "src/tools/animate.ts"() {
84490
85699
  "use strict";
@@ -84636,7 +85845,7 @@ var init_animate = __esm({
84636
85845
  },
84637
85846
  required: ["env_id", "document_id"]
84638
85847
  };
84639
- toolDefs55 = [
85848
+ toolDefs56 = [
84640
85849
  {
84641
85850
  name: "animate",
84642
85851
  pack: null,
@@ -84699,7 +85908,7 @@ function buildInstructions(kernelPrompt) {
84699
85908
  "- Ship with `export_cad` (STL/GLB/STEP) or `open_in_browser` (vcad.io deep link).",
84700
85909
  "- Fix in place: when geometry is wrong, prefer `update` on the offending node over deleting parts and starting over.",
84701
85910
  "- Cut sheet parts you already modelled: `flat_pattern_from_solid` turns any constant-thickness solid \u2014 extruded sketch, boolean, imported STEP \u2014 into a fab-ready DXF plus a bend table, no sheet-metal re-authoring. Called without `part_id` it does the whole document and groups identical parts into one pattern \xD7 quantity; hand its `nest_input` to `sheet_metal_nest`, then `sheet_metal_cost`, for a quote. It fails closed: the emitted profile re-extruded by the detected thickness must reproduce the solid's volume.",
84702
- "- Deliver the project bill of materials with `bom_create` \u2192 `bom_export` (markdown/CSV/JSON with landed-cost totals): link quote_manufacturing quotes on manufactured lines, and source COTS hardware (bearings, shafts, standoffs, screws, ferrite magnets) with `search_mechanical_parts`. All BOM prices are estimates and flagged as such.",
85911
+ "- Deliver the project bill of materials with `bom_create` \u2192 `bom_export` (markdown/CSV/JSON with landed-cost totals): link quote_manufacturing quotes on manufactured lines, and source COTS hardware (bearings, shafts, standoffs, screws, ferrite magnets) with `search_mechanical_parts`. Check electronic lines against live LCSC/JLCPCB stock and price with `bom_check_stock` (or one part with `check_part_stock`). All BOM prices are estimates and flagged as such.",
84703
85912
  "",
84704
85913
  "",
84705
85914
  "Sessions and server restarts: a `document_id` is a handle to server-side state, NOT storage. If ANY preview goes dark, a widget stops updating, or a call fails with an unknown `document_id`, check `server_info` FIRST \u2014 a low `uptime_s` or a changed `instance_id` means the SERVER RESTARTED and the feature is fine. That is the fastest way to tell a restart from a bug, and it should be the first check for any preview-shaped complaint. On a restart every live document_id dies at once, so previously-working widgets all go dark together \u2014 that is not a renderer bug. When `server_info` reports `durable:false`, sessions are in-memory only and cannot be recovered: keep the authoring source (your `create_cad_loon` call or record of edits) so a document can be rebuilt, and snapshot long-lived work with `checkpoint_document` / `save_document`. Every tool result also carries `_meta['io.vcad/build']` with `boot_token` + `instance_id`, so a changed value flags a restart without polling.",
@@ -85547,6 +86756,7 @@ var init_server3 = __esm({
85547
86756
  init_ordering();
85548
86757
  init_bom();
85549
86758
  init_mech_parts();
86759
+ init_part_stock();
85550
86760
  init_live_share();
85551
86761
  init_parts2();
85552
86762
  init_preview();
@@ -85595,8 +86805,8 @@ var init_server3 = __esm({
85595
86805
  init_order_feed();
85596
86806
  init_animate();
85597
86807
  PKG_VERSION = (() => {
85598
- if ("0.10.0-main.98") {
85599
- return "0.10.0-main.98";
86808
+ if ("0.10.0-main.99") {
86809
+ return "0.10.0-main.99";
85600
86810
  }
85601
86811
  try {
85602
86812
  const req = createRequire2(import.meta.url);
@@ -85605,8 +86815,8 @@ var init_server3 = __esm({
85605
86815
  return "0.0.0";
85606
86816
  }
85607
86817
  })();
85608
- BUILD_SHA = "a9b6b278d2e31337b686b38b7d9da0f874635906";
85609
- BUILD_TIME = "2026-10-20T00:42:15.256Z";
86818
+ BUILD_SHA = "760ec5bebb4b0432d2d732e2f43fd190f8e7c268";
86819
+ BUILD_TIME = "2026-10-20T13:47:22.068Z";
85610
86820
  SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
85611
86821
  VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
85612
86822
  INSTANCE_ID = randomUUID7().slice(0, 8);
@@ -85639,25 +86849,25 @@ var init_server3 = __esm({
85639
86849
  ...toolDefs10,
85640
86850
  ...toolDefs11,
85641
86851
  ...toolDefs13,
85642
- ...toolDefs15,
85643
- ...toolDefs14,
85644
86852
  ...toolDefs16,
86853
+ ...toolDefs14,
86854
+ ...toolDefs15,
85645
86855
  ...toolDefs17,
86856
+ ...toolDefs18,
85646
86857
  ...toolDefs5,
85647
86858
  ...toolDefs3,
85648
- ...toolDefs18,
86859
+ ...toolDefs19,
85649
86860
  ...toolDefs6,
85650
86861
  ...toolDefs7,
85651
- ...toolDefs19,
85652
86862
  ...toolDefs20,
85653
86863
  ...toolDefs21,
85654
- ...toolDefs24,
86864
+ ...toolDefs22,
85655
86865
  ...toolDefs25,
85656
86866
  ...toolDefs26,
85657
86867
  ...toolDefs27,
85658
86868
  ...toolDefs28,
85659
- ...toolDefs12,
85660
86869
  ...toolDefs29,
86870
+ ...toolDefs12,
85661
86871
  ...toolDefs30,
85662
86872
  ...toolDefs31,
85663
86873
  ...toolDefs32,
@@ -85670,24 +86880,25 @@ var init_server3 = __esm({
85670
86880
  ...toolDefs39,
85671
86881
  ...toolDefs40,
85672
86882
  ...toolDefs41,
85673
- ...toolDefs,
85674
86883
  ...toolDefs42,
86884
+ ...toolDefs,
85675
86885
  ...toolDefs43,
85676
86886
  ...toolDefs44,
85677
86887
  ...toolDefs45,
85678
86888
  ...toolDefs46,
85679
- ...toolDefs2,
85680
86889
  ...toolDefs47,
86890
+ ...toolDefs2,
85681
86891
  ...toolDefs48,
85682
86892
  ...toolDefs49,
85683
86893
  ...toolDefs50,
85684
86894
  ...toolDefs51,
85685
86895
  ...toolDefs52,
85686
- ...toolDefs23,
85687
- ...toolDefs22,
85688
86896
  ...toolDefs53,
86897
+ ...toolDefs24,
86898
+ ...toolDefs23,
85689
86899
  ...toolDefs54,
85690
- ...toolDefs55
86900
+ ...toolDefs55,
86901
+ ...toolDefs56
85691
86902
  ];
85692
86903
  LIST_TOOL_ORDER = [
85693
86904
  // ── Session lifecycle ──────────────────────────────────────
@@ -85714,6 +86925,8 @@ var init_server3 = __esm({
85714
86925
  "bom_add_line",
85715
86926
  "bom_export",
85716
86927
  "search_mechanical_parts",
86928
+ "check_part_stock",
86929
+ "bom_check_stock",
85717
86930
  // ── Live review window ─────────────────────────────────────
85718
86931
  "share_session",
85719
86932
  "unshare_session",
@@ -85829,6 +87042,8 @@ var init_server3 = __esm({
85829
87042
  "get_changelog",
85830
87043
  // ── ECAD (PCB) ─────────────────────────────────────────────
85831
87044
  "create_schematic",
87045
+ "update_schematic",
87046
+ "inspect_schematic",
85832
87047
  "place_components",
85833
87048
  "route_nets",
85834
87049
  "add_coil",