ceddcozum 0.1.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -6
- package/dist/index.js +419 -170
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
# ceddcozum
|
|
1
|
+
# ceddcozum / childmetrics
|
|
2
2
|
|
|
3
3
|
Pediatric clinical calculators from the terminal. 32 tools for growth, neonatal, bone mineral, diabetes, puberty, organ size, blood pressure, and more.
|
|
4
4
|
|
|
5
5
|
All calculations run locally — no API keys, no network requests, no patient data transmitted.
|
|
6
6
|
|
|
7
|
+
Also available as `childmetrics`:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx childmetrics --list
|
|
11
|
+
```
|
|
12
|
+
|
|
7
13
|
## Install
|
|
8
14
|
|
|
9
15
|
```bash
|
|
@@ -12,17 +18,22 @@ npx ceddcozum --list
|
|
|
12
18
|
|
|
13
19
|
# Or install globally
|
|
14
20
|
npm install -g ceddcozum
|
|
21
|
+
# or
|
|
22
|
+
npm install -g childmetrics
|
|
15
23
|
```
|
|
16
24
|
|
|
17
25
|
## Usage
|
|
18
26
|
|
|
19
27
|
```bash
|
|
20
|
-
#
|
|
21
|
-
ceddcozum auxology
|
|
28
|
+
# Simple key=value syntax
|
|
29
|
+
ceddcozum auxology sex=male age=5.5 height=110 weight=19
|
|
22
30
|
|
|
23
|
-
# JSON
|
|
31
|
+
# JSON args (for LLM agents)
|
|
24
32
|
ceddcozum auxology --args '{"sex":"male","age":5.5,"height":110}' --format json
|
|
25
33
|
|
|
34
|
+
# Growth hormone with brand name
|
|
35
|
+
ceddcozum growth-hormone weight=25 'usagePattern=her gün 2 mg' concentrationIU=30
|
|
36
|
+
|
|
26
37
|
# View tool schema (OpenAI function format)
|
|
27
38
|
ceddcozum auxology --schema
|
|
28
39
|
|
|
@@ -35,7 +46,8 @@ ceddcozum --schemas
|
|
|
35
46
|
| Command | Description |
|
|
36
47
|
|---------|-------------|
|
|
37
48
|
| `ceddcozum --list` | List all 32 available tools |
|
|
38
|
-
| `ceddcozum <tool>
|
|
49
|
+
| `ceddcozum <tool> key=value ...` | Run a calculator |
|
|
50
|
+
| `ceddcozum <tool> --args '{...}'` | Run with JSON args |
|
|
39
51
|
| `ceddcozum <tool> --schema` | Show tool schema (OpenAI format) |
|
|
40
52
|
| `ceddcozum --schemas` | Dump all schemas |
|
|
41
53
|
| `--format human` | Human-readable output (default) |
|
|
@@ -67,4 +79,8 @@ interface ToolResult {
|
|
|
67
79
|
|
|
68
80
|
## License
|
|
69
81
|
|
|
70
|
-
|
|
82
|
+
Apache License 2.0 — see [LICENSE](./LICENSE).
|
|
83
|
+
|
|
84
|
+
Copyright 2026 ÇEDD (Çocuk Endokrinolojisi ve Diyabet Derneği) / TSPED (Turkish Society for Pediatric Endocrinology and Diabetes).
|
|
85
|
+
|
|
86
|
+
Attribution is required when using or redistributing this software. See [NOTICE](./NOTICE) for details.
|
package/dist/index.js
CHANGED
|
@@ -12800,8 +12800,8 @@ var neonatalDatasetRegistry = {
|
|
|
12800
12800
|
var syndromeDatasetRegistry = {
|
|
12801
12801
|
[downDataset.id]: downDataset,
|
|
12802
12802
|
...turnerDatasets,
|
|
12803
|
-
[noonanWittDataset.id]: noonanWittDataset,
|
|
12804
12803
|
[noonanMalaquiasDataset.id]: noonanMalaquiasDataset,
|
|
12804
|
+
[noonanWittDataset.id]: noonanWittDataset,
|
|
12805
12805
|
[noonanRankeDataset.id]: noonanRankeDataset,
|
|
12806
12806
|
[praderWilliDataset.id]: praderWilliDataset,
|
|
12807
12807
|
[praderWilliButlerDataset.id]: praderWilliButlerDataset,
|
|
@@ -13783,7 +13783,8 @@ var SUPPORTED_MEASUREMENTS = {
|
|
|
13783
13783
|
["williams" /* williams */]: ["height"],
|
|
13784
13784
|
["xlh" /* xlh */]: ["height"],
|
|
13785
13785
|
["osteogenesisImperfecta" /* osteogenesisImperfecta */]: ["height", "weight"],
|
|
13786
|
-
["mucopolysaccharidosis" /* mucopolysaccharidosis */]: ["height", "weight"]
|
|
13786
|
+
["mucopolysaccharidosis" /* mucopolysaccharidosis */]: ["height", "weight"],
|
|
13787
|
+
["klinefelter" /* klinefelter */]: ["height"]
|
|
13787
13788
|
};
|
|
13788
13789
|
function getDatasetDefinition(datasetId) {
|
|
13789
13790
|
return syndromeDatasetRegistry[datasetId];
|
|
@@ -15499,22 +15500,6 @@ var igfbp3GuvenSds = [
|
|
|
15499
15500
|
{ ageRange: "17-18", sex: "f", values: [], mean: 5.19, sd: 1.3 }
|
|
15500
15501
|
];
|
|
15501
15502
|
|
|
15502
|
-
// ../src/data/igf/tanner.ts
|
|
15503
|
-
var femaleTannerData = [
|
|
15504
|
-
{ stage: "I", igf1: { low: 49, median: 159, high: 342 }, igfbp3: { low: 1.2, median: 3.6, high: 6.4, isIGFBP3: true } },
|
|
15505
|
-
{ stage: "II", igf1: { low: 115, median: 269, high: 428 }, igfbp3: { low: 2.8, median: 4.5, high: 6.9, isIGFBP3: true } },
|
|
15506
|
-
{ stage: "III", igf1: { low: 145, median: 412, high: 760 }, igfbp3: { low: 3.9, median: 5.3, high: 9.4, isIGFBP3: true } },
|
|
15507
|
-
{ stage: "IV", igf1: { low: 244, median: 504, high: 787 }, igfbp3: { low: 3.3, median: 5.9, high: 8.1, isIGFBP3: true } },
|
|
15508
|
-
{ stage: "V", igf1: { low: 143, median: 408, high: 859 }, igfbp3: { low: 2.7, median: 5.6, high: 9.1, isIGFBP3: true } }
|
|
15509
|
-
];
|
|
15510
|
-
var maleTannerData = [
|
|
15511
|
-
{ stage: "I", igf1: { low: 63, median: 152, high: 279 }, igfbp3: { low: 1.4, median: 3.6, high: 5.2, isIGFBP3: true } },
|
|
15512
|
-
{ stage: "II", igf1: { low: 75, median: 190, high: 420 }, igfbp3: { low: 2.3, median: 3.9, high: 6.3, isIGFBP3: true } },
|
|
15513
|
-
{ stage: "III", igf1: { low: 94, median: 406, high: 765 }, igfbp3: { low: 3.1, median: 5.4, high: 8.9, isIGFBP3: true } },
|
|
15514
|
-
{ stage: "IV", igf1: { low: 192, median: 577, high: 861 }, igfbp3: { low: 3.7, median: 6.5, high: 8.7, isIGFBP3: true } },
|
|
15515
|
-
{ stage: "V", igf1: { low: 171, median: 422, high: 814 }, igfbp3: { low: 2.6, median: 5.2, high: 8.6, isIGFBP3: true } }
|
|
15516
|
-
];
|
|
15517
|
-
|
|
15518
15503
|
// ../src/data/igf/igf1EcliaLms.ts
|
|
15519
15504
|
var maleEcliaLms = [
|
|
15520
15505
|
{ age: 0.25, L: 0.3, M: 39.4, S: 0.5076 },
|
|
@@ -15569,8 +15554,6 @@ var femaleEcliaLms = [
|
|
|
15569
15554
|
var igfSdsData = igf1ElmlingerSds;
|
|
15570
15555
|
var igfbp3SdsDataElmlinger = igfbp3ElmlingerSds;
|
|
15571
15556
|
var igfbp3SdsDataRiaBc = igfbp3RiaBcSds;
|
|
15572
|
-
var femaleTannerData2 = femaleTannerData;
|
|
15573
|
-
var maleTannerData2 = maleTannerData;
|
|
15574
15557
|
var femaleLmsParams = femaleEcliaLms;
|
|
15575
15558
|
var maleLmsParams = maleEcliaLms;
|
|
15576
15559
|
function calculateIGFSds(input) {
|
|
@@ -15580,7 +15563,6 @@ function calculateIGFSds(input) {
|
|
|
15580
15563
|
if (!input.igf1Value && !input.igfbp3Value) {
|
|
15581
15564
|
return null;
|
|
15582
15565
|
}
|
|
15583
|
-
const tannerStage = input.tannerStage || "I";
|
|
15584
15566
|
const result = {
|
|
15585
15567
|
dataset: {
|
|
15586
15568
|
igf1: input.igf1Database,
|
|
@@ -15594,22 +15576,14 @@ function calculateIGFSds(input) {
|
|
|
15594
15576
|
if (input.igf1Value !== void 0 && input.igf1Value > 0) {
|
|
15595
15577
|
const igf1Result = calculateIGF1SDS(input.age, input.sex, input.igf1Value, input.igf1Database);
|
|
15596
15578
|
if (igf1Result) {
|
|
15597
|
-
result.igf1 =
|
|
15598
|
-
...igf1Result,
|
|
15599
|
-
// Only Elmlinger dataset has Tanner stage data
|
|
15600
|
-
tannerPercentile: input.igf1Database === "elmlinger" ? getTannerPercentile(input.sex, tannerStage, input.igf1Value, "igf1") : void 0
|
|
15601
|
-
};
|
|
15579
|
+
result.igf1 = igf1Result;
|
|
15602
15580
|
}
|
|
15603
15581
|
}
|
|
15604
15582
|
if (input.igfbp3Value !== void 0 && input.igfbp3Value > 0) {
|
|
15605
15583
|
const convertedValue = input.igfbp3Unit === "ngPerML" ? input.igfbp3Value / 1e3 : input.igfbp3Value;
|
|
15606
15584
|
const igfbp3Result = calculateIGFBP3SDS(input.age, input.sex, convertedValue, input.igfbp3Database);
|
|
15607
15585
|
if (igfbp3Result) {
|
|
15608
|
-
result.igfbp3 =
|
|
15609
|
-
...igfbp3Result,
|
|
15610
|
-
// Only Elmlinger dataset has Tanner stage data
|
|
15611
|
-
tannerPercentile: input.igfbp3Database === "elmlinger" ? getTannerPercentile(input.sex, tannerStage, convertedValue, "igfbp3", input.igfbp3Database) : void 0
|
|
15612
|
-
};
|
|
15586
|
+
result.igfbp3 = igfbp3Result;
|
|
15613
15587
|
}
|
|
15614
15588
|
}
|
|
15615
15589
|
return result;
|
|
@@ -15674,7 +15648,9 @@ function calculateElmlingerSDS(age, sex, value, data) {
|
|
|
15674
15648
|
return {
|
|
15675
15649
|
sds,
|
|
15676
15650
|
percentile,
|
|
15677
|
-
interpretation: getInterpretation(sds)
|
|
15651
|
+
interpretation: getInterpretation(sds),
|
|
15652
|
+
median: values[2] ?? null
|
|
15653
|
+
// index 2 = Mean (50th percentile)
|
|
15678
15654
|
};
|
|
15679
15655
|
}
|
|
15680
15656
|
function calculateECLIASDS(age, sex, value) {
|
|
@@ -15683,7 +15659,8 @@ function calculateECLIASDS(age, sex, value) {
|
|
|
15683
15659
|
return {
|
|
15684
15660
|
sds: null,
|
|
15685
15661
|
percentile: null,
|
|
15686
|
-
interpretation: null
|
|
15662
|
+
interpretation: null,
|
|
15663
|
+
median: null
|
|
15687
15664
|
};
|
|
15688
15665
|
}
|
|
15689
15666
|
const sds = calculateLMSSDS(value, params.L, params.M, params.S);
|
|
@@ -15691,7 +15668,9 @@ function calculateECLIASDS(age, sex, value) {
|
|
|
15691
15668
|
return {
|
|
15692
15669
|
sds,
|
|
15693
15670
|
percentile,
|
|
15694
|
-
interpretation: getInterpretation(sds)
|
|
15671
|
+
interpretation: getInterpretation(sds),
|
|
15672
|
+
median: params.M
|
|
15673
|
+
// M parameter = 50th percentile in LMS
|
|
15695
15674
|
};
|
|
15696
15675
|
}
|
|
15697
15676
|
function calculateRiaBcSDS(age, sex, value) {
|
|
@@ -15707,7 +15686,9 @@ function calculateRiaBcSDS(age, sex, value) {
|
|
|
15707
15686
|
return {
|
|
15708
15687
|
sds,
|
|
15709
15688
|
percentile,
|
|
15710
|
-
interpretation: getInterpretation(sds)
|
|
15689
|
+
interpretation: getInterpretation(sds),
|
|
15690
|
+
median: group.mean
|
|
15691
|
+
// mean ≈ 50th percentile for Mean-SD method
|
|
15711
15692
|
};
|
|
15712
15693
|
}
|
|
15713
15694
|
function calculateGuvenSDS(age, sex, value, data) {
|
|
@@ -15723,7 +15704,9 @@ function calculateGuvenSDS(age, sex, value, data) {
|
|
|
15723
15704
|
return {
|
|
15724
15705
|
sds,
|
|
15725
15706
|
percentile,
|
|
15726
|
-
interpretation: getInterpretation(sds)
|
|
15707
|
+
interpretation: getInterpretation(sds),
|
|
15708
|
+
median: group.mean
|
|
15709
|
+
// mean ≈ 50th percentile for Mean-SD method
|
|
15727
15710
|
};
|
|
15728
15711
|
}
|
|
15729
15712
|
function getLMSForAge(age, sex) {
|
|
@@ -15760,41 +15743,6 @@ function getLMSForAge(age, sex) {
|
|
|
15760
15743
|
};
|
|
15761
15744
|
}
|
|
15762
15745
|
var calculateLMSSDS = (measurement, L, M, S) => calculateLMSBasedSDS(measurement, L, M, S, false);
|
|
15763
|
-
function getTannerPercentile(sex, stage, value, type, database) {
|
|
15764
|
-
const tannerData = sex === "female" ? femaleTannerData2 : maleTannerData2;
|
|
15765
|
-
const stageData = tannerData.find((data) => data.stage === stage);
|
|
15766
|
-
if (!stageData) {
|
|
15767
|
-
return "N/A";
|
|
15768
|
-
}
|
|
15769
|
-
const range = type === "igf1" ? stageData.igf1 : stageData.igfbp3;
|
|
15770
|
-
if (type === "igfbp3" && (database === "riaBc" || database === "guven")) {
|
|
15771
|
-
return "N/A";
|
|
15772
|
-
}
|
|
15773
|
-
if (type === "igfbp3") {
|
|
15774
|
-
const convertedValue = value * 1e3;
|
|
15775
|
-
const convertedRange = {
|
|
15776
|
-
low: range.low * 1e3,
|
|
15777
|
-
median: range.median * 1e3,
|
|
15778
|
-
high: range.high * 1e3
|
|
15779
|
-
};
|
|
15780
|
-
return calculateTannerPercentile(convertedRange, convertedValue);
|
|
15781
|
-
}
|
|
15782
|
-
return calculateTannerPercentile(range, value);
|
|
15783
|
-
}
|
|
15784
|
-
function calculateTannerPercentile(range, value) {
|
|
15785
|
-
if (value < range.low) return "<2.5p";
|
|
15786
|
-
if (value > range.high) return ">97.5p";
|
|
15787
|
-
if (value === range.low) return "2.5p";
|
|
15788
|
-
if (value === range.high) return "97.5p";
|
|
15789
|
-
if (value === range.median) return "50p";
|
|
15790
|
-
if (value > range.low && value < range.median) {
|
|
15791
|
-
return "2.5-50p";
|
|
15792
|
-
}
|
|
15793
|
-
if (value > range.median && value < range.high) {
|
|
15794
|
-
return "50-97.5p";
|
|
15795
|
-
}
|
|
15796
|
-
return "Unknown";
|
|
15797
|
-
}
|
|
15798
15746
|
function isAgeInRange(age, range) {
|
|
15799
15747
|
const components = range.split(/[–-]/).map((s) => s.trim());
|
|
15800
15748
|
const startAge = parseFloat(components[0]);
|
|
@@ -16943,7 +16891,9 @@ function calculatePhosphateSds(input) {
|
|
|
16943
16891
|
sds: null,
|
|
16944
16892
|
percentile: null,
|
|
16945
16893
|
phosphateMmol,
|
|
16946
|
-
phosphateMgDl
|
|
16894
|
+
phosphateMgDl,
|
|
16895
|
+
p2_5Mmol: null,
|
|
16896
|
+
p97_5Mmol: null
|
|
16947
16897
|
};
|
|
16948
16898
|
}
|
|
16949
16899
|
const sds = calculateLMSBasedSDS(
|
|
@@ -16955,11 +16905,17 @@ function calculatePhosphateSds(input) {
|
|
|
16955
16905
|
// no SD23 correction needed for phosphate
|
|
16956
16906
|
);
|
|
16957
16907
|
const percentile = sdsToPercentile(sds);
|
|
16908
|
+
const Z_2_5 = -1.96;
|
|
16909
|
+
const Z_97_5 = 1.96;
|
|
16910
|
+
const p2_5Mmol = lmsToValue(lmsParams.L, lmsParams.M, lmsParams.S, Z_2_5);
|
|
16911
|
+
const p97_5Mmol = lmsToValue(lmsParams.L, lmsParams.M, lmsParams.S, Z_97_5);
|
|
16958
16912
|
return {
|
|
16959
16913
|
sds,
|
|
16960
16914
|
percentile,
|
|
16961
16915
|
phosphateMmol,
|
|
16962
|
-
phosphateMgDl
|
|
16916
|
+
phosphateMgDl,
|
|
16917
|
+
p2_5Mmol,
|
|
16918
|
+
p97_5Mmol
|
|
16963
16919
|
};
|
|
16964
16920
|
}
|
|
16965
16921
|
|
|
@@ -17224,6 +17180,8 @@ function getInterpretation2(ratio) {
|
|
|
17224
17180
|
};
|
|
17225
17181
|
}
|
|
17226
17182
|
}
|
|
17183
|
+
var INSULIN_UNITS = ["uIU/mL", "mIU/L", "pmol/L"];
|
|
17184
|
+
var CPEPTIDE_UNITS = ["ng/mL", "nmol/L", "pmol/L"];
|
|
17227
17185
|
|
|
17228
17186
|
// ../src/lib/calculators/hcg-test.ts
|
|
17229
17187
|
var CONVERSION_FACTORS = {
|
|
@@ -24248,6 +24206,170 @@ function calculateOsmolality(input) {
|
|
|
24248
24206
|
}
|
|
24249
24207
|
}
|
|
24250
24208
|
|
|
24209
|
+
// ../src/lib/processors/DateAgeConverter.ts
|
|
24210
|
+
function calculateAge2(input) {
|
|
24211
|
+
const errors = [];
|
|
24212
|
+
let birthDate = null;
|
|
24213
|
+
if (input.birthDate instanceof Date) {
|
|
24214
|
+
birthDate = input.birthDate;
|
|
24215
|
+
} else if (typeof input.birthDate === "string") {
|
|
24216
|
+
birthDate = parseDate(input.birthDate);
|
|
24217
|
+
}
|
|
24218
|
+
if (!birthDate || isNaN(birthDate.getTime())) {
|
|
24219
|
+
errors.push("Invalid birth date");
|
|
24220
|
+
}
|
|
24221
|
+
const measurementDateInput = input.measurementDate || input.targetDate || /* @__PURE__ */ new Date();
|
|
24222
|
+
let measurementDate = null;
|
|
24223
|
+
if (measurementDateInput instanceof Date) {
|
|
24224
|
+
measurementDate = measurementDateInput;
|
|
24225
|
+
} else if (typeof measurementDateInput === "string") {
|
|
24226
|
+
measurementDate = parseDate(measurementDateInput);
|
|
24227
|
+
}
|
|
24228
|
+
if (!measurementDate || isNaN(measurementDate.getTime())) {
|
|
24229
|
+
errors.push("Invalid measurement date");
|
|
24230
|
+
}
|
|
24231
|
+
if (birthDate && measurementDate) {
|
|
24232
|
+
if (measurementDate < birthDate) {
|
|
24233
|
+
errors.push("Measurement date cannot be before birth date");
|
|
24234
|
+
}
|
|
24235
|
+
if (birthDate.getFullYear() < 1900) {
|
|
24236
|
+
errors.push("Birth date is too far in the past");
|
|
24237
|
+
}
|
|
24238
|
+
if (measurementDate.getFullYear() > (/* @__PURE__ */ new Date()).getFullYear() + 1) {
|
|
24239
|
+
errors.push("Measurement date is too far in the future");
|
|
24240
|
+
}
|
|
24241
|
+
}
|
|
24242
|
+
if (errors.length === 0 && birthDate && measurementDate) {
|
|
24243
|
+
const age = calculateAgeBreakdownFromDates(birthDate, measurementDate);
|
|
24244
|
+
return {
|
|
24245
|
+
isValid: true,
|
|
24246
|
+
age,
|
|
24247
|
+
errors: [],
|
|
24248
|
+
parsedBirthDate: birthDate,
|
|
24249
|
+
parsedMeasurementDate: measurementDate
|
|
24250
|
+
};
|
|
24251
|
+
}
|
|
24252
|
+
return {
|
|
24253
|
+
isValid: false,
|
|
24254
|
+
age: null,
|
|
24255
|
+
errors,
|
|
24256
|
+
parsedBirthDate: birthDate,
|
|
24257
|
+
parsedMeasurementDate: measurementDate
|
|
24258
|
+
};
|
|
24259
|
+
}
|
|
24260
|
+
function calculateAgeBreakdownFromDates(birthDate, measurementDate) {
|
|
24261
|
+
const totalDays = Math.floor((measurementDate.getTime() - birthDate.getTime()) / (1e3 * 60 * 60 * 24));
|
|
24262
|
+
if (totalDays < 0) {
|
|
24263
|
+
const decimalYears2 = totalDays / 365.25;
|
|
24264
|
+
return {
|
|
24265
|
+
years: 0,
|
|
24266
|
+
months: 0,
|
|
24267
|
+
days: totalDays,
|
|
24268
|
+
totalDays,
|
|
24269
|
+
decimalYears: decimalYears2,
|
|
24270
|
+
decimalMonths: decimalYears2 * 12
|
|
24271
|
+
};
|
|
24272
|
+
}
|
|
24273
|
+
const { years, months, days } = calculateCalendarAge(birthDate, measurementDate);
|
|
24274
|
+
const decimalYears = convertYMDToDecimalYears(years, months, days);
|
|
24275
|
+
const decimalMonths = decimalYears * 12;
|
|
24276
|
+
return {
|
|
24277
|
+
years,
|
|
24278
|
+
months,
|
|
24279
|
+
days,
|
|
24280
|
+
totalDays,
|
|
24281
|
+
decimalYears,
|
|
24282
|
+
decimalMonths
|
|
24283
|
+
};
|
|
24284
|
+
}
|
|
24285
|
+
function calculateCalendarAge(startDate, endDate) {
|
|
24286
|
+
let years = endDate.getFullYear() - startDate.getFullYear();
|
|
24287
|
+
let months = endDate.getMonth() - startDate.getMonth();
|
|
24288
|
+
if (endDate.getDate() < startDate.getDate()) {
|
|
24289
|
+
months--;
|
|
24290
|
+
}
|
|
24291
|
+
if (months < 0) {
|
|
24292
|
+
years--;
|
|
24293
|
+
months += 12;
|
|
24294
|
+
}
|
|
24295
|
+
const refDate = new Date(startDate.getFullYear() + years, startDate.getMonth() + months, 1);
|
|
24296
|
+
const daysInRefMonth = new Date(refDate.getFullYear(), refDate.getMonth() + 1, 0).getDate();
|
|
24297
|
+
const clampedDay = Math.min(startDate.getDate(), daysInRefMonth);
|
|
24298
|
+
refDate.setDate(clampedDay);
|
|
24299
|
+
const days = Math.round((endDate.getTime() - refDate.getTime()) / (1e3 * 60 * 60 * 24));
|
|
24300
|
+
return { years, months, days };
|
|
24301
|
+
}
|
|
24302
|
+
function convertYMDToDecimalYears(years, months, days) {
|
|
24303
|
+
const monthsInYears = months / 12;
|
|
24304
|
+
const daysInYears = days / 365.25;
|
|
24305
|
+
return years + monthsInYears + daysInYears;
|
|
24306
|
+
}
|
|
24307
|
+
function parseDate(dateString) {
|
|
24308
|
+
if (!dateString || dateString.trim() === "") {
|
|
24309
|
+
return null;
|
|
24310
|
+
}
|
|
24311
|
+
const cleaned = dateString.trim();
|
|
24312
|
+
let date = new Date(cleaned);
|
|
24313
|
+
if (!isNaN(date.getTime())) {
|
|
24314
|
+
return date;
|
|
24315
|
+
}
|
|
24316
|
+
const formats = [
|
|
24317
|
+
// DD.MM.YYYY (Turkish)
|
|
24318
|
+
/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/,
|
|
24319
|
+
// DD/MM/YYYY (European)
|
|
24320
|
+
/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/,
|
|
24321
|
+
// DD-MM-YYYY
|
|
24322
|
+
/^(\d{1,2})-(\d{1,2})-(\d{4})$/,
|
|
24323
|
+
// MM/DD/YYYY (American)
|
|
24324
|
+
/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/,
|
|
24325
|
+
// MM-DD-YYYY
|
|
24326
|
+
/^(\d{1,2})-(\d{1,2})-(\d{4})$/
|
|
24327
|
+
];
|
|
24328
|
+
const ddmmMatch = cleaned.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/);
|
|
24329
|
+
if (ddmmMatch) {
|
|
24330
|
+
const [, day, month, year] = ddmmMatch;
|
|
24331
|
+
date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
|
24332
|
+
if (!isNaN(date.getTime())) return date;
|
|
24333
|
+
}
|
|
24334
|
+
const ddmmSlashMatch = cleaned.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
|
|
24335
|
+
if (ddmmSlashMatch) {
|
|
24336
|
+
const [, day, month, year] = ddmmSlashMatch;
|
|
24337
|
+
date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
|
24338
|
+
if (!isNaN(date.getTime())) return date;
|
|
24339
|
+
}
|
|
24340
|
+
return null;
|
|
24341
|
+
}
|
|
24342
|
+
|
|
24343
|
+
// ../src/lib/calculators/cnpAnalogue.ts
|
|
24344
|
+
var VOSORITIDE_DOSE_TABLE = [
|
|
24345
|
+
{ minWeight: 3, maxWeight: 3, vialMg: 0.4, doseMg: 0.096, volumeMl: 0.12, reconcentration: 0.8 },
|
|
24346
|
+
{ minWeight: 4, maxWeight: 4, vialMg: 0.4, doseMg: 0.12, volumeMl: 0.15, reconcentration: 0.8 },
|
|
24347
|
+
{ minWeight: 5, maxWeight: 5, vialMg: 0.4, doseMg: 0.16, volumeMl: 0.2, reconcentration: 0.8 },
|
|
24348
|
+
{ minWeight: 6, maxWeight: 7, vialMg: 0.4, doseMg: 0.2, volumeMl: 0.25, reconcentration: 0.8 },
|
|
24349
|
+
{ minWeight: 8, maxWeight: 11, vialMg: 0.4, doseMg: 0.24, volumeMl: 0.3, reconcentration: 0.8 },
|
|
24350
|
+
{ minWeight: 12, maxWeight: 16, vialMg: 0.56, doseMg: 0.28, volumeMl: 0.35, reconcentration: 0.8 },
|
|
24351
|
+
{ minWeight: 17, maxWeight: 21, vialMg: 0.56, doseMg: 0.32, volumeMl: 0.4, reconcentration: 0.8 },
|
|
24352
|
+
{ minWeight: 22, maxWeight: 32, vialMg: 0.56, doseMg: 0.4, volumeMl: 0.5, reconcentration: 0.8 },
|
|
24353
|
+
{ minWeight: 33, maxWeight: 43, vialMg: 1.2, doseMg: 0.5, volumeMl: 0.25, reconcentration: 2 },
|
|
24354
|
+
{ minWeight: 44, maxWeight: 59, vialMg: 1.2, doseMg: 0.6, volumeMl: 0.3, reconcentration: 2 },
|
|
24355
|
+
{ minWeight: 60, maxWeight: 89, vialMg: 1.2, doseMg: 0.7, volumeMl: 0.35, reconcentration: 2 },
|
|
24356
|
+
{ minWeight: 90, maxWeight: Infinity, vialMg: 1.2, doseMg: 0.8, volumeMl: 0.4, reconcentration: 2 }
|
|
24357
|
+
];
|
|
24358
|
+
var NAVEPEGRITIDE_DOSE_MCG_KG_WEEK = 100;
|
|
24359
|
+
function lookupVosoritideDose(weightKg) {
|
|
24360
|
+
const rounded = Math.round(weightKg);
|
|
24361
|
+
if (rounded < 3) return null;
|
|
24362
|
+
return VOSORITIDE_DOSE_TABLE.find((e) => rounded >= e.minWeight && rounded <= e.maxWeight) ?? null;
|
|
24363
|
+
}
|
|
24364
|
+
function mcgPerKgDayVosoritide(entry, weightKgRounded) {
|
|
24365
|
+
return entry.doseMg * 1e3 / weightKgRounded;
|
|
24366
|
+
}
|
|
24367
|
+
function computeNavepegritideWeeklyDose(weightKg) {
|
|
24368
|
+
if (!weightKg || !isFinite(weightKg) || weightKg <= 0) return null;
|
|
24369
|
+
const doseMcg = NAVEPEGRITIDE_DOSE_MCG_KG_WEEK * weightKg;
|
|
24370
|
+
return { doseMcg, doseMg: doseMcg / 1e3 };
|
|
24371
|
+
}
|
|
24372
|
+
|
|
24251
24373
|
// ../src/lib/chat/toolExecutor.ts
|
|
24252
24374
|
function severity(sds) {
|
|
24253
24375
|
if (sds === null) return "normal";
|
|
@@ -24370,6 +24492,11 @@ function buildInputEcho(name, args) {
|
|
|
24370
24492
|
if (gw != null) parts.push(`${gw}w${gd ? `+${gd}d` : ""}`);
|
|
24371
24493
|
return { label: "", value: parts.join(" \xB7 "), tag: "echo" };
|
|
24372
24494
|
}
|
|
24495
|
+
case "cnp-analogue": {
|
|
24496
|
+
if (args.mode) parts.push(String(args.mode));
|
|
24497
|
+
if (args.weightKg != null) parts.push(`${args.weightKg} kg`);
|
|
24498
|
+
return { label: "", value: parts.join(" \xB7 "), tag: "echo" };
|
|
24499
|
+
}
|
|
24373
24500
|
}
|
|
24374
24501
|
const age = args.age != null ? Number(args.age) : args.ageYears != null ? Number(args.ageYears) : NaN;
|
|
24375
24502
|
if (!isNaN(age)) {
|
|
@@ -24416,18 +24543,6 @@ function formatDateTR(d) {
|
|
|
24416
24543
|
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
24417
24544
|
return `${dd}/${mm}/${d.getFullYear()}`;
|
|
24418
24545
|
}
|
|
24419
|
-
function ageBreakdown(birth, ref) {
|
|
24420
|
-
let years = ref.getFullYear() - birth.getFullYear();
|
|
24421
|
-
let months = ref.getMonth() - birth.getMonth();
|
|
24422
|
-
if (ref.getDate() < birth.getDate()) months--;
|
|
24423
|
-
if (months < 0) {
|
|
24424
|
-
years--;
|
|
24425
|
-
months += 12;
|
|
24426
|
-
}
|
|
24427
|
-
const diffMs = ref.getTime() - birth.getTime();
|
|
24428
|
-
const totalDecimal = diffMs / (365.25 * 24 * 60 * 60 * 1e3);
|
|
24429
|
-
return { years, months, totalDecimal };
|
|
24430
|
-
}
|
|
24431
24546
|
function executeDecimalAge(args) {
|
|
24432
24547
|
const birthDatesStr = String(args.birthDates || "");
|
|
24433
24548
|
const refDateStr = args.referenceDate ? String(args.referenceDate) : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -24456,8 +24571,13 @@ function executeDecimalAge(args) {
|
|
|
24456
24571
|
results.push({ label: formatDateTR(bd), value: "Hen\xFCz do\u011Fmad\u0131", severity: "severe" });
|
|
24457
24572
|
continue;
|
|
24458
24573
|
}
|
|
24459
|
-
const
|
|
24460
|
-
|
|
24574
|
+
const ageResult = calculateAge2({ birthDate: bd, measurementDate: refDate });
|
|
24575
|
+
if (!ageResult.isValid || !ageResult.age) {
|
|
24576
|
+
results.push({ label: `#${i + 1}`, value: `Hesaplanamad\u0131: ${birthDates[i]}`, severity: "severe" });
|
|
24577
|
+
continue;
|
|
24578
|
+
}
|
|
24579
|
+
const { years, months, decimalYears } = ageResult.age;
|
|
24580
|
+
const decimalStr = decimalYears.toFixed(2).replace(".", ",");
|
|
24461
24581
|
if (isBatch) {
|
|
24462
24582
|
results.push({
|
|
24463
24583
|
label: formatDateTR(bd),
|
|
@@ -24467,6 +24587,14 @@ function executeDecimalAge(args) {
|
|
|
24467
24587
|
results.push({ label: "Do\u011Fum tarihi", value: formatDateTR(bd) });
|
|
24468
24588
|
results.push({ label: "Ya\u015F", value: `${years} y\u0131l ${months} ay` });
|
|
24469
24589
|
results.push({ label: "Desimal ya\u015F", value: `${decimalStr} y\u0131l` });
|
|
24590
|
+
const roundedAge = parseFloat(decimalYears.toFixed(2));
|
|
24591
|
+
results.push({
|
|
24592
|
+
label: "",
|
|
24593
|
+
value: "Calculate growth SDS with this age",
|
|
24594
|
+
tag: "alt",
|
|
24595
|
+
altToolName: "auxology",
|
|
24596
|
+
altArgs: { age: roundedAge, ...sex ? { sex } : {} }
|
|
24597
|
+
});
|
|
24470
24598
|
}
|
|
24471
24599
|
}
|
|
24472
24600
|
return results;
|
|
@@ -24502,6 +24630,9 @@ function executeTool(name, args) {
|
|
|
24502
24630
|
case "body-ratios":
|
|
24503
24631
|
results = executeBodyRatios(args);
|
|
24504
24632
|
break;
|
|
24633
|
+
case "cnp-analogue":
|
|
24634
|
+
results = executeCnpAnalogue(args);
|
|
24635
|
+
break;
|
|
24505
24636
|
case "growth-hormone":
|
|
24506
24637
|
results = executeGrowthHormone(args);
|
|
24507
24638
|
break;
|
|
@@ -24591,7 +24722,7 @@ function executeTool(name, args) {
|
|
|
24591
24722
|
return [{ label: "Info", value: `Calculator "${name}" is not available in the chat. Please use it from the Tools panel.` }];
|
|
24592
24723
|
}
|
|
24593
24724
|
if (results.length > 0 && results[0].label === "Error") return results;
|
|
24594
|
-
const ECHO_TOOLS = /* @__PURE__ */ new Set(["growth-hormone"]);
|
|
24725
|
+
const ECHO_TOOLS = /* @__PURE__ */ new Set(["growth-hormone", "cnp-analogue"]);
|
|
24595
24726
|
if (ECHO_TOOLS.has(name)) {
|
|
24596
24727
|
const echo = buildInputEcho(name, args);
|
|
24597
24728
|
if (echo.value && echo.value !== "\u2014") {
|
|
@@ -25022,9 +25153,6 @@ function executeIGFSds(args) {
|
|
|
25022
25153
|
percentile: result.igf1.percentile,
|
|
25023
25154
|
severity: result.igf1.sds != null ? severity(result.igf1.sds) : "normal"
|
|
25024
25155
|
});
|
|
25025
|
-
if (result.igf1.tannerPercentile) {
|
|
25026
|
-
results.push({ label: "IGF-1 Tanner", value: result.igf1.tannerPercentile });
|
|
25027
|
-
}
|
|
25028
25156
|
}
|
|
25029
25157
|
if (result.igfbp3) {
|
|
25030
25158
|
results.push({
|
|
@@ -25116,6 +25244,44 @@ function executeBodyRatios(args) {
|
|
|
25116
25244
|
}
|
|
25117
25245
|
return results.length > 1 ? results : [patientRow({ age, sex }), { label: "Note", value: "Provide sittingHeight or armSpan" }];
|
|
25118
25246
|
}
|
|
25247
|
+
function executeCnpAnalogue(args) {
|
|
25248
|
+
const mode = args.mode === "weekly" || args.mode === "daily" ? args.mode : null;
|
|
25249
|
+
const weightKg = Number(args.weightKg);
|
|
25250
|
+
if (!mode || !isFinite(weightKg) || weightKg <= 0) {
|
|
25251
|
+
return [{ label: "Error", value: 'Provide mode ("daily" or "weekly") and weightKg > 0.', severity: "severe" }];
|
|
25252
|
+
}
|
|
25253
|
+
if (mode === "weekly") {
|
|
25254
|
+
const wk = computeNavepegritideWeeklyDose(weightKg);
|
|
25255
|
+
if (!wk) {
|
|
25256
|
+
return [{ label: "Error", value: "Invalid weight for weekly dose.", severity: "severe" }];
|
|
25257
|
+
}
|
|
25258
|
+
return [
|
|
25259
|
+
{ label: "Agent", value: "Navepegritide (weekly)" },
|
|
25260
|
+
{ label: "Weekly dose", value: wk.doseMg.toFixed(3), unit: "mg" },
|
|
25261
|
+
{ label: "Dose intensity", value: String(NAVEPEGRITIDE_DOSE_MCG_KG_WEEK), unit: "mcg/kg/week" }
|
|
25262
|
+
];
|
|
25263
|
+
}
|
|
25264
|
+
const rounded = Math.round(weightKg);
|
|
25265
|
+
const row = lookupVosoritideDose(weightKg);
|
|
25266
|
+
if (!row) {
|
|
25267
|
+
return [
|
|
25268
|
+
{
|
|
25269
|
+
label: "Error",
|
|
25270
|
+
value: "Weight out of range for vosoritide table (minimum 3 kg after rounding to nearest kg).",
|
|
25271
|
+
severity: "severe"
|
|
25272
|
+
}
|
|
25273
|
+
];
|
|
25274
|
+
}
|
|
25275
|
+
const mcgKg = mcgPerKgDayVosoritide(row, rounded);
|
|
25276
|
+
return [
|
|
25277
|
+
{ label: "Agent", value: "Vosoritide (daily)" },
|
|
25278
|
+
{ label: "Dose", value: row.doseMg.toFixed(3), unit: "mg" },
|
|
25279
|
+
{ label: "Approx. intensity", value: mcgKg.toFixed(1), unit: "mcg/kg/day" },
|
|
25280
|
+
{ label: "Vial", value: String(row.vialMg), unit: "mg" },
|
|
25281
|
+
{ label: "Concentration", value: row.reconcentration.toFixed(1), unit: "mg/mL" },
|
|
25282
|
+
{ label: "Injection volume", value: row.volumeMl.toFixed(2), unit: "mL" }
|
|
25283
|
+
];
|
|
25284
|
+
}
|
|
25119
25285
|
function parseUsagePattern(raw) {
|
|
25120
25286
|
const compactNormalized = raw.replace(/:(\d+),(\d+)/g, ":$1.$2");
|
|
25121
25287
|
const compactParts = compactNormalized.split(/[,;]/).map((p) => p.trim());
|
|
@@ -25152,6 +25318,14 @@ function parseUsagePattern(raw) {
|
|
|
25152
25318
|
}
|
|
25153
25319
|
}
|
|
25154
25320
|
if (revEntries.length > 0) return revEntries;
|
|
25321
|
+
const dailyRegex = /(?:her\s*g[üu]n(?:de)?|herg[üu]n|g[üu]n(?:de|l[üu]k)|daily|every\s*day)\s*:?\s*([\d]+[.,]?\d*)\s*(?:mg)?/i;
|
|
25322
|
+
const dailyMatch = raw.match(dailyRegex);
|
|
25323
|
+
if (dailyMatch) {
|
|
25324
|
+
const doseMg = parseFloat(dailyMatch[1].replace(",", "."));
|
|
25325
|
+
if (!Number.isNaN(doseMg) && doseMg >= 0) {
|
|
25326
|
+
return [{ days: 7, doseMg }];
|
|
25327
|
+
}
|
|
25328
|
+
}
|
|
25155
25329
|
return [];
|
|
25156
25330
|
}
|
|
25157
25331
|
function executeGrowthHormone(args) {
|
|
@@ -25277,12 +25451,25 @@ function executeCorrectedAge(args) {
|
|
|
25277
25451
|
return rows;
|
|
25278
25452
|
}
|
|
25279
25453
|
function executeBmdSds(args) {
|
|
25454
|
+
const siteRaw = String(args.site);
|
|
25455
|
+
const validSites = ["spine", "neck", "tblh"];
|
|
25456
|
+
if (!validSites.includes(siteRaw)) {
|
|
25457
|
+
return [
|
|
25458
|
+
{
|
|
25459
|
+
label: "Error",
|
|
25460
|
+
value: `BMD SDS supports sites: ${validSites.join(", ")}. Retry with one of those.`,
|
|
25461
|
+
severity: "severe"
|
|
25462
|
+
}
|
|
25463
|
+
];
|
|
25464
|
+
}
|
|
25465
|
+
const isTblh = siteRaw === "tblh";
|
|
25280
25466
|
const result = calculateBmdSds({
|
|
25281
25467
|
value: Number(args.value),
|
|
25282
25468
|
age: Number(args.age),
|
|
25283
25469
|
sex: String(args.sex),
|
|
25284
|
-
site:
|
|
25285
|
-
measure: "
|
|
25470
|
+
site: isTblh ? "spine" : siteRaw,
|
|
25471
|
+
measure: "aBMD",
|
|
25472
|
+
...isTblh && { dataset: "tblh" }
|
|
25286
25473
|
});
|
|
25287
25474
|
const ageVal = Number(args.age);
|
|
25288
25475
|
const sexVal = String(args.sex);
|
|
@@ -26031,6 +26218,44 @@ function executeCorrectedSodium(args) {
|
|
|
26031
26218
|
];
|
|
26032
26219
|
}
|
|
26033
26220
|
|
|
26221
|
+
// ../src/lib/chat/chatToolIdMapping.ts
|
|
26222
|
+
var CHAT_FUNCTION_NAME_ALIASES = {
|
|
26223
|
+
hba1c: "hba1c-estimation",
|
|
26224
|
+
"igf-lagh-adjustment": "igf-sds-lagh-adjustment"
|
|
26225
|
+
};
|
|
26226
|
+
var CHAT_NAME_CATEGORY_OVERRIDE = {
|
|
26227
|
+
"decimal-age": "misc"
|
|
26228
|
+
};
|
|
26229
|
+
function chatFunctionToMetadataId(chatFunctionName) {
|
|
26230
|
+
return CHAT_FUNCTION_NAME_ALIASES[chatFunctionName] ?? chatFunctionName;
|
|
26231
|
+
}
|
|
26232
|
+
function chatFunctionCategoryOverride(chatFunctionName) {
|
|
26233
|
+
return CHAT_NAME_CATEGORY_OVERRIDE[chatFunctionName];
|
|
26234
|
+
}
|
|
26235
|
+
|
|
26236
|
+
// ../src/lib/hiddenToolIds.ts
|
|
26237
|
+
var HIDDEN_TOOL_IDS = [
|
|
26238
|
+
"steroid-tapering",
|
|
26239
|
+
"periop-adrenal",
|
|
26240
|
+
"preop-diabetes",
|
|
26241
|
+
"dka-fluid",
|
|
26242
|
+
"gri",
|
|
26243
|
+
"basal-bolus",
|
|
26244
|
+
"meal-bolus",
|
|
26245
|
+
"custom-iv-fluid",
|
|
26246
|
+
"cp-height",
|
|
26247
|
+
"oral-phosphorus",
|
|
26248
|
+
"corrected-calcium",
|
|
26249
|
+
"gfr",
|
|
26250
|
+
"leptin-sds"
|
|
26251
|
+
];
|
|
26252
|
+
function isToolVisible(toolId, isUnlocked) {
|
|
26253
|
+
if (!HIDDEN_TOOL_IDS.includes(toolId)) {
|
|
26254
|
+
return true;
|
|
26255
|
+
}
|
|
26256
|
+
return isUnlocked;
|
|
26257
|
+
}
|
|
26258
|
+
|
|
26034
26259
|
// ../src/lib/chat/toolSchemas.ts
|
|
26035
26260
|
var toolSchemas = [
|
|
26036
26261
|
// ╔══════════════════════════════════════════╗
|
|
@@ -26040,7 +26265,7 @@ var toolSchemas = [
|
|
|
26040
26265
|
type: "function",
|
|
26041
26266
|
function: {
|
|
26042
26267
|
name: "decimal-age",
|
|
26043
|
-
description: "Calculate decimal age(s) from birth date(s). Accepts one or multiple comma-separated birth dates. Returns decimal age in years for each.
|
|
26268
|
+
description: "Calculate decimal age(s) from birth date(s). Accepts one or multiple comma-separated birth dates. Returns decimal age in years for each.",
|
|
26044
26269
|
parameters: {
|
|
26045
26270
|
type: "object",
|
|
26046
26271
|
properties: {
|
|
@@ -26068,8 +26293,8 @@ var toolSchemas = [
|
|
|
26068
26293
|
height: { type: "number", description: "Height in cm [optional]" },
|
|
26069
26294
|
weight: { type: "number", description: "Weight in kg [optional]" },
|
|
26070
26295
|
headCircumference: { type: "number", description: "Head circumference in cm [optional]" },
|
|
26071
|
-
dataset: { type: "string", description:
|
|
26072
|
-
syndrome: { type: "string", description: 'Condition dataset ID.
|
|
26296
|
+
dataset: { type: "string", description: "Standard dataset [default: neyzi]", enum: ["neyzi", "who", "cdc-extended-2022"] },
|
|
26297
|
+
syndrome: { type: "string", description: 'Condition dataset ID. Use "none" for standard growth.', enum: ["none", ...auxologySyndromeDatasetIds] }
|
|
26073
26298
|
},
|
|
26074
26299
|
required: ["sex", "age"]
|
|
26075
26300
|
}
|
|
@@ -26084,13 +26309,13 @@ var toolSchemas = [
|
|
|
26084
26309
|
type: "object",
|
|
26085
26310
|
properties: {
|
|
26086
26311
|
sex: { type: "string", description: "Sex", enum: ["male", "female"] },
|
|
26087
|
-
currentAge: { type: "number", description: "Age at last measurement in years
|
|
26312
|
+
currentAge: { type: "number", description: "Age at last measurement in years" },
|
|
26088
26313
|
previousAge: { type: "number", description: "Previous age in years [optional if intervalMonths given]" },
|
|
26089
26314
|
currentHeight: { type: "number", description: "Current height in cm [optional if heightChange given]" },
|
|
26090
26315
|
previousHeight: { type: "number", description: "Previous height in cm [optional if heightChange given]" },
|
|
26091
26316
|
heightChange: { type: "number", description: "Height gained in cm [optional, alternative to two heights]" },
|
|
26092
26317
|
intervalMonths: { type: "number", description: "Interval between measurements in months [optional, alternative to two ages]" },
|
|
26093
|
-
dataset: { type: "string", description: "
|
|
26318
|
+
dataset: { type: "string", description: "Reference dataset [default: auto by age]", enum: ["neyzi", "kelly", "baumgartner", "tanner", "who", "achondroplasia"] }
|
|
26094
26319
|
},
|
|
26095
26320
|
required: ["sex", "currentAge"]
|
|
26096
26321
|
}
|
|
@@ -26100,15 +26325,15 @@ var toolSchemas = [
|
|
|
26100
26325
|
type: "function",
|
|
26101
26326
|
function: {
|
|
26102
26327
|
name: "predicted-height",
|
|
26103
|
-
description:
|
|
26328
|
+
description: "Predicted adult height. Three modes: (1) Target height from parental heights. (2) Bayley-Pinneau (default): height + bone age. (3) RWT (Roche-Wainer-Thissen): height + weight + bone age + parents.",
|
|
26104
26329
|
parameters: {
|
|
26105
26330
|
type: "object",
|
|
26106
26331
|
properties: {
|
|
26107
26332
|
sex: { type: "string", description: "Sex", enum: ["male", "female"] },
|
|
26108
|
-
method: { type: "string", description: "PAH method
|
|
26109
|
-
height: { type: "number", description: "Current height in cm [
|
|
26110
|
-
weight: { type: "number", description: "Weight in kg [required for RWT
|
|
26111
|
-
boneAge: { type: "number", description: "Bone age in years [
|
|
26333
|
+
method: { type: "string", description: "PAH method [default: bayley-pinneau]", enum: ["bayley-pinneau", "rwt"] },
|
|
26334
|
+
height: { type: "number", description: "Current height in cm [optional]" },
|
|
26335
|
+
weight: { type: "number", description: "Weight in kg [optional, required for RWT]" },
|
|
26336
|
+
boneAge: { type: "number", description: "Bone age in years [optional]" },
|
|
26112
26337
|
chronologicalAge: { type: "number", description: "Chronological age in years" },
|
|
26113
26338
|
motherHeight: { type: "number", description: "Mother height in cm" },
|
|
26114
26339
|
fatherHeight: { type: "number", description: "Father height in cm" }
|
|
@@ -26130,8 +26355,8 @@ var toolSchemas = [
|
|
|
26130
26355
|
igf1Value: { type: "number", description: "IGF-1 concentration in ng/mL [optional]" },
|
|
26131
26356
|
igfbp3Value: { type: "number", description: "IGFBP-3 concentration [optional]" },
|
|
26132
26357
|
igfbp3Unit: { type: "string", description: "IGFBP-3 unit [default: mcg/mL]", enum: ["mcg/mL", "ng/mL"] },
|
|
26133
|
-
igf1Database: { type: "string", description: "IGF-1 reference kit. elmlinger = CLIA
|
|
26134
|
-
igfbp3Database: { type: "string", description: "IGFBP-3 reference kit. elmlinger = CLIA
|
|
26358
|
+
igf1Database: { type: "string", description: "IGF-1 reference kit. elmlinger = CLIA/IMMULITE (default). eclia = ECLIA/Roche. guven = G\xFCven.", enum: ["elmlinger", "eclia", "guven"] },
|
|
26359
|
+
igfbp3Database: { type: "string", description: "IGFBP-3 reference kit. elmlinger = CLIA/IMMULITE (default). ria-bc = RIA/Beckman Coulter. guven = G\xFCven.", enum: ["elmlinger", "ria-bc", "guven"] }
|
|
26135
26360
|
},
|
|
26136
26361
|
required: ["age", "sex"]
|
|
26137
26362
|
}
|
|
@@ -26148,24 +26373,39 @@ var toolSchemas = [
|
|
|
26148
26373
|
sex: { type: "string", description: "Sex", enum: ["male", "female"] },
|
|
26149
26374
|
age: { type: "number", description: "Age in years" },
|
|
26150
26375
|
height: { type: "number", description: "Standing height in cm" },
|
|
26151
|
-
sittingHeight: { type: "number", description: "Sitting height in cm [optional]
|
|
26152
|
-
armSpan: { type: "number", description: "Arm span in cm [optional]
|
|
26376
|
+
sittingHeight: { type: "number", description: "Sitting height in cm [optional]" },
|
|
26377
|
+
armSpan: { type: "number", description: "Arm span in cm [optional]" }
|
|
26153
26378
|
},
|
|
26154
26379
|
required: ["sex", "age", "height"]
|
|
26155
26380
|
}
|
|
26156
26381
|
}
|
|
26157
26382
|
},
|
|
26383
|
+
{
|
|
26384
|
+
type: "function",
|
|
26385
|
+
function: {
|
|
26386
|
+
name: "cnp-analogue",
|
|
26387
|
+
description: "CNP analogue dosing for achondroplasia. daily = vosoritide (label weight-band table, ~15 mcg/kg/day). weekly = navepegritide 100 mcg/kg/week SC.",
|
|
26388
|
+
parameters: {
|
|
26389
|
+
type: "object",
|
|
26390
|
+
properties: {
|
|
26391
|
+
mode: { type: "string", description: "daily (vosoritide) or weekly (navepegritide)", enum: ["daily", "weekly"] },
|
|
26392
|
+
weightKg: { type: "number", description: "Body weight in kg (daily table uses weight rounded to nearest kg)" }
|
|
26393
|
+
},
|
|
26394
|
+
required: ["mode", "weightKg"]
|
|
26395
|
+
}
|
|
26396
|
+
}
|
|
26397
|
+
},
|
|
26158
26398
|
{
|
|
26159
26399
|
type: "function",
|
|
26160
26400
|
function: {
|
|
26161
26401
|
name: "growth-hormone",
|
|
26162
|
-
description: 'Growth hormone dosing analysis.
|
|
26402
|
+
description: 'Growth hormone dosing analysis. Weekly schedule as "days:doseMg,days:doseMg" (days 1-7, supports off-day schedules).',
|
|
26163
26403
|
parameters: {
|
|
26164
26404
|
type: "object",
|
|
26165
26405
|
properties: {
|
|
26166
|
-
weight: { type: "number", description: "Weight in kg
|
|
26167
|
-
usagePattern: { type: "string", description: 'Compact format "days:doseMg,days:doseMg".
|
|
26168
|
-
concentrationIU: { type: "number", description:
|
|
26406
|
+
weight: { type: "number", description: "Weight in kg" },
|
|
26407
|
+
usagePattern: { type: "string", description: 'Compact format "days:doseMg,days:doseMg". Days can sum to 1-7 (supports off-day schedules).' },
|
|
26408
|
+
concentrationIU: { type: "number", description: "Preparation concentration in IU (mg\xD73) [optional, for box calculation]" }
|
|
26169
26409
|
},
|
|
26170
26410
|
required: ["weight", "usagePattern"]
|
|
26171
26411
|
}
|
|
@@ -26175,7 +26415,7 @@ var toolSchemas = [
|
|
|
26175
26415
|
type: "function",
|
|
26176
26416
|
function: {
|
|
26177
26417
|
name: "igf-lagh-adjustment",
|
|
26178
|
-
description: "Adjust IGF-1 value for long-acting growth hormone (somatrogon). Corrects measured IGF-1 based on hours after dose.
|
|
26418
|
+
description: "Adjust IGF-1 value for long-acting growth hormone (somatrogon). Corrects measured IGF-1 based on hours after dose.",
|
|
26179
26419
|
parameters: {
|
|
26180
26420
|
type: "object",
|
|
26181
26421
|
properties: {
|
|
@@ -26193,7 +26433,7 @@ var toolSchemas = [
|
|
|
26193
26433
|
type: "function",
|
|
26194
26434
|
function: {
|
|
26195
26435
|
name: "neonatal-parameters",
|
|
26196
|
-
description: "Calculate SDS/percentile for neonatal birth parameters (weight, length, head circumference) by gestational week.
|
|
26436
|
+
description: "Calculate SDS/percentile for neonatal birth parameters (weight, length, head circumference) by gestational week. Uses Fenton (2025) or Kurto\u011Flu (2012) reference data.",
|
|
26197
26437
|
parameters: {
|
|
26198
26438
|
type: "object",
|
|
26199
26439
|
properties: {
|
|
@@ -26240,7 +26480,7 @@ var toolSchemas = [
|
|
|
26240
26480
|
sex: { type: "string", description: "Sex", enum: ["male", "female"] },
|
|
26241
26481
|
age: { type: "number", description: "Age in years" },
|
|
26242
26482
|
value: { type: "number", description: "BMD value in g/cm\xB2" },
|
|
26243
|
-
site: { type: "string", description: "
|
|
26483
|
+
site: { type: "string", description: "DXA measurement site: spine (L1-L4), neck (femoral neck), or tblh (total body less head)", enum: ["spine", "neck", "tblh"] }
|
|
26244
26484
|
},
|
|
26245
26485
|
required: ["sex", "age", "value", "site"]
|
|
26246
26486
|
}
|
|
@@ -26378,9 +26618,9 @@ var toolSchemas = [
|
|
|
26378
26618
|
type: "object",
|
|
26379
26619
|
properties: {
|
|
26380
26620
|
insulin: { type: "number", description: "Insulin value" },
|
|
26381
|
-
insulinUnit: { type: "string", description: "Insulin unit", enum: [
|
|
26621
|
+
insulinUnit: { type: "string", description: "Insulin unit", enum: [...INSULIN_UNITS] },
|
|
26382
26622
|
cPeptide: { type: "number", description: "C-Peptide value" },
|
|
26383
|
-
cPeptideUnit: { type: "string", description: "C-Peptide unit", enum: [
|
|
26623
|
+
cPeptideUnit: { type: "string", description: "C-Peptide unit", enum: [...CPEPTIDE_UNITS] }
|
|
26384
26624
|
},
|
|
26385
26625
|
required: ["insulin", "insulinUnit", "cPeptide", "cPeptideUnit"]
|
|
26386
26626
|
}
|
|
@@ -26393,7 +26633,7 @@ var toolSchemas = [
|
|
|
26393
26633
|
type: "function",
|
|
26394
26634
|
function: {
|
|
26395
26635
|
name: "steroid-conversion",
|
|
26396
|
-
description: "Convert equivalent doses between glucocorticoids
|
|
26636
|
+
description: "Convert equivalent doses between glucocorticoids (Hydrocortisone, Prednisolone, Methylprednisolone, Dexamethasone).",
|
|
26397
26637
|
parameters: {
|
|
26398
26638
|
type: "object",
|
|
26399
26639
|
properties: {
|
|
@@ -26579,7 +26819,7 @@ var toolSchemas = [
|
|
|
26579
26819
|
parameters: {
|
|
26580
26820
|
type: "object",
|
|
26581
26821
|
properties: {
|
|
26582
|
-
substance: { type: "string", description: "Substance to convert.
|
|
26822
|
+
substance: { type: "string", description: "Substance to convert (e.g. testosterone, cortisol, estradiol, glucose, calcium, insulin, igf1, prolactin, etc.)" },
|
|
26583
26823
|
value: { type: "number", description: "Numeric value to convert" },
|
|
26584
26824
|
fromUnit: { type: "string", description: "Source unit (e.g. ng/dL, nmol/L, mg/dL, mmol/L, \xB5g/dL, pg/mL, pmol/L, ng/mL, \xB5U/mL, mIU/L)" }
|
|
26585
26825
|
},
|
|
@@ -26591,7 +26831,7 @@ var toolSchemas = [
|
|
|
26591
26831
|
type: "function",
|
|
26592
26832
|
function: {
|
|
26593
26833
|
name: "osmolality",
|
|
26594
|
-
description: "Calculate serum or urine osmolality. Serum: 2\xD7Na + Glucose/18 + BUN/2.8. Urine: 2\xD7(Na+K) + Glucose/18 + UUN/2.8.
|
|
26834
|
+
description: "Calculate serum or urine osmolality. Serum: 2\xD7Na + Glucose/18 + BUN/2.8. Urine: 2\xD7(Na+K) + Glucose/18 + UUN/2.8.",
|
|
26595
26835
|
parameters: {
|
|
26596
26836
|
type: "object",
|
|
26597
26837
|
properties: {
|
|
@@ -26623,6 +26863,12 @@ var toolSchemas = [
|
|
|
26623
26863
|
}
|
|
26624
26864
|
}
|
|
26625
26865
|
];
|
|
26866
|
+
function isChatCatalogSchema(s) {
|
|
26867
|
+
const name = s.function.name;
|
|
26868
|
+
const metaId = chatFunctionToMetadataId(name);
|
|
26869
|
+
return isToolVisible(metaId, false);
|
|
26870
|
+
}
|
|
26871
|
+
var chatToolSchemas = toolSchemas.filter(isChatCatalogSchema);
|
|
26626
26872
|
|
|
26627
26873
|
// ../src/data/toolMetadata.ts
|
|
26628
26874
|
var toolsMetadata = [
|
|
@@ -26667,16 +26913,6 @@ var toolsMetadata = [
|
|
|
26667
26913
|
subtitleFallback: "Growth hormone dose calculator",
|
|
26668
26914
|
searchKeywords: ["GH", "b\xFCy\xFCme hormonu", "somatropin", "Norditropin", "Genotropin", "Nutropin", "doz", "dose", "IU", "mg"]
|
|
26669
26915
|
},
|
|
26670
|
-
{
|
|
26671
|
-
id: "cnp-analogue",
|
|
26672
|
-
category: "growth",
|
|
26673
|
-
type: "calcs",
|
|
26674
|
-
titleKey: "tools.cnp-analogue.title",
|
|
26675
|
-
subtitleKey: "tools.cnp-analogue.subtitle",
|
|
26676
|
-
titleFallback: "CNP Analogue",
|
|
26677
|
-
subtitleFallback: "Vosoritide & navepegritide dosing",
|
|
26678
|
-
searchKeywords: ["vosoritide", "CNP", "achondroplasia", "akondroplazi", "navepegritide", "natriuretic peptide", "doz", "dose", "c\xFCcelik", "dwarfism", "skeletal dysplasia", "cnp analogue", "cnp analog"]
|
|
26679
|
-
},
|
|
26680
26916
|
{
|
|
26681
26917
|
id: "igf-sds",
|
|
26682
26918
|
category: "growth",
|
|
@@ -26684,8 +26920,8 @@ var toolsMetadata = [
|
|
|
26684
26920
|
titleKey: "tools.igf-sds.title",
|
|
26685
26921
|
subtitleKey: "tools.igf-sds.subtitle",
|
|
26686
26922
|
titleFallback: "IGF-1 & IGFBP-3 SDS",
|
|
26687
|
-
subtitleFallback: "Age
|
|
26688
|
-
searchKeywords: ["IGF-1", "IGFBP-3", "somatomedin", "
|
|
26923
|
+
subtitleFallback: "Age-based IGF-1 & IGFBP-3 SDS calculation",
|
|
26924
|
+
searchKeywords: ["IGF-1", "IGFBP-3", "somatomedin", "SDS"]
|
|
26689
26925
|
},
|
|
26690
26926
|
{
|
|
26691
26927
|
id: "igf-sds-lagh-adjustment",
|
|
@@ -26727,6 +26963,16 @@ var toolsMetadata = [
|
|
|
26727
26963
|
subtitleFallback: "Height estimation from segmental measurements",
|
|
26728
26964
|
searchKeywords: ["serebral palsi", "cerebral palsy", "CP", "segmental", "segment", "Stevenson", "boy tahmini"]
|
|
26729
26965
|
},
|
|
26966
|
+
{
|
|
26967
|
+
id: "cnp-analogue",
|
|
26968
|
+
category: "growth",
|
|
26969
|
+
type: "calcs",
|
|
26970
|
+
titleKey: "tools.cnp-analogue.title",
|
|
26971
|
+
subtitleKey: "tools.cnp-analogue.subtitle",
|
|
26972
|
+
titleFallback: "CNP Analogue",
|
|
26973
|
+
subtitleFallback: "Vosoritide & navepegritide dosing",
|
|
26974
|
+
searchKeywords: ["vosoritide", "CNP", "achondroplasia", "akondroplazi", "navepegritide", "natriuretic peptide", "doz", "dose", "c\xFCcelik", "dwarfism", "skeletal dysplasia", "cnp analogue", "cnp analog"]
|
|
26975
|
+
},
|
|
26730
26976
|
// BONE CATEGORY
|
|
26731
26977
|
{
|
|
26732
26978
|
id: "bmd-sds",
|
|
@@ -27000,7 +27246,26 @@ var toolsMetadata = [
|
|
|
27000
27246
|
subtitleKey: "tools.thyroid-volume.subtitle",
|
|
27001
27247
|
titleFallback: "Thyroid",
|
|
27002
27248
|
subtitleFallback: "Thyroid volume SDS",
|
|
27003
|
-
searchKeywords: [
|
|
27249
|
+
searchKeywords: [
|
|
27250
|
+
"tiroid",
|
|
27251
|
+
"thyroid",
|
|
27252
|
+
"guatr",
|
|
27253
|
+
"goiter",
|
|
27254
|
+
"hacim",
|
|
27255
|
+
"volume",
|
|
27256
|
+
"USG",
|
|
27257
|
+
"ultrason",
|
|
27258
|
+
"SDS",
|
|
27259
|
+
"neonatal thyroid",
|
|
27260
|
+
"yenido\u011Fan tiroid",
|
|
27261
|
+
"newborn thyroid",
|
|
27262
|
+
"gestational",
|
|
27263
|
+
"gestasyon",
|
|
27264
|
+
"TSH",
|
|
27265
|
+
"Kurto\u011Flu",
|
|
27266
|
+
"Mutlu",
|
|
27267
|
+
"Tuzcu"
|
|
27268
|
+
]
|
|
27004
27269
|
},
|
|
27005
27270
|
{
|
|
27006
27271
|
id: "pituitary-height",
|
|
@@ -27086,23 +27351,6 @@ var toolsMetadata = [
|
|
|
27086
27351
|
}
|
|
27087
27352
|
];
|
|
27088
27353
|
|
|
27089
|
-
// ../src/lib/hiddenToolIds.ts
|
|
27090
|
-
var HIDDEN_TOOL_IDS = [
|
|
27091
|
-
"steroid-tapering",
|
|
27092
|
-
"periop-adrenal",
|
|
27093
|
-
"preop-diabetes",
|
|
27094
|
-
"dka-fluid",
|
|
27095
|
-
"gri",
|
|
27096
|
-
"basal-bolus",
|
|
27097
|
-
"meal-bolus",
|
|
27098
|
-
"custom-iv-fluid",
|
|
27099
|
-
"cp-height",
|
|
27100
|
-
"oral-phosphorus",
|
|
27101
|
-
"corrected-calcium",
|
|
27102
|
-
"gfr",
|
|
27103
|
-
"leptin-sds"
|
|
27104
|
-
];
|
|
27105
|
-
|
|
27106
27354
|
// src/formatter.ts
|
|
27107
27355
|
var useColor = !process.env.NO_COLOR && !!process.stdout.isTTY;
|
|
27108
27356
|
var bold = (s) => useColor ? `\x1B[1m${s}\x1B[0m` : s;
|
|
@@ -27179,26 +27427,15 @@ var cyan2 = (s) => useColor2 ? `\x1B[36m${s}\x1B[0m` : s;
|
|
|
27179
27427
|
var green = (s) => useColor2 ? `\x1B[32m${s}\x1B[0m` : s;
|
|
27180
27428
|
var yellow2 = (s) => useColor2 ? `\x1B[33m${s}\x1B[0m` : s;
|
|
27181
27429
|
var magenta = (s) => useColor2 ? `\x1B[35m${s}\x1B[0m` : s;
|
|
27182
|
-
var VERSION = true ? "0.
|
|
27183
|
-
var visibleSchemas =
|
|
27184
|
-
(s) => !HIDDEN_TOOL_IDS.includes(s.function.name)
|
|
27185
|
-
);
|
|
27186
|
-
var SCHEMA_TO_META = {
|
|
27187
|
-
"hba1c": "hba1c-estimation",
|
|
27188
|
-
"igf-lagh-adjustment": "igf-sds-lagh-adjustment",
|
|
27189
|
-
"decimal-age": "decimal-age"
|
|
27190
|
-
// no metadata entry — handled by fallback category
|
|
27191
|
-
};
|
|
27192
|
-
var CATEGORY_OVERRIDE = {
|
|
27193
|
-
"igf-lagh-adjustment": "growth",
|
|
27194
|
-
"decimal-age": "misc"
|
|
27195
|
-
};
|
|
27430
|
+
var VERSION = true ? "0.2.0" : "0.0.0-dev";
|
|
27431
|
+
var visibleSchemas = chatToolSchemas;
|
|
27196
27432
|
function findMeta(schemaId) {
|
|
27197
|
-
const mappedId =
|
|
27433
|
+
const mappedId = chatFunctionToMetadataId(schemaId);
|
|
27198
27434
|
return toolsMetadata.find((m) => m.id === mappedId);
|
|
27199
27435
|
}
|
|
27200
27436
|
function getCategoryForSchema(schemaId) {
|
|
27201
|
-
|
|
27437
|
+
const override = chatFunctionCategoryOverride(schemaId);
|
|
27438
|
+
if (override) return override;
|
|
27202
27439
|
const meta = findMeta(schemaId);
|
|
27203
27440
|
return meta?.category ?? "misc";
|
|
27204
27441
|
}
|
|
@@ -27280,6 +27517,7 @@ ${bold2("USAGE")}
|
|
|
27280
27517
|
|
|
27281
27518
|
${bold2("OPTIONS")}
|
|
27282
27519
|
--format human|json ${dim2("Output format (default: human)")}
|
|
27520
|
+
--pipe ${dim2("Output only decimal age(s) for command chaining")}
|
|
27283
27521
|
--help ${dim2("Show this help")}
|
|
27284
27522
|
--version ${dim2("Show version")}
|
|
27285
27523
|
|
|
@@ -27447,7 +27685,7 @@ function parseKeyValueArgs(pairs, schema) {
|
|
|
27447
27685
|
}
|
|
27448
27686
|
return args;
|
|
27449
27687
|
}
|
|
27450
|
-
function runToolWithArgs(name, args, format) {
|
|
27688
|
+
function runToolWithArgs(name, args, format, pipe) {
|
|
27451
27689
|
const schema = getSchemaByName(name);
|
|
27452
27690
|
const errors = validateArgs(name, args);
|
|
27453
27691
|
if (errors.length > 0) {
|
|
@@ -27466,7 +27704,17 @@ ${yellow2("!")} Validation errors for ${bold2(name)}:`);
|
|
|
27466
27704
|
console.error(`${yellow2("!")} Error executing ${bold2(name)}:`, err instanceof Error ? err.message : err);
|
|
27467
27705
|
process.exit(1);
|
|
27468
27706
|
}
|
|
27469
|
-
if (
|
|
27707
|
+
if (pipe && name === "decimal-age") {
|
|
27708
|
+
for (const r of results) {
|
|
27709
|
+
if (r.label === "Desimal ya\u015F") {
|
|
27710
|
+
const match = r.value.match(/([\d,]+)\s*yıl/);
|
|
27711
|
+
if (match) console.log(match[1].replace(",", "."));
|
|
27712
|
+
} else if (r.tag !== "echo" && r.value.includes("y\u0131l") && r.label.match(/^\d{2}\/\d{2}\/\d{4}$/)) {
|
|
27713
|
+
const match = r.value.match(/\(([\d,]+)\s*yıl\)/);
|
|
27714
|
+
if (match) console.log(match[1].replace(",", "."));
|
|
27715
|
+
}
|
|
27716
|
+
}
|
|
27717
|
+
} else if (format === "json") {
|
|
27470
27718
|
const dataResults = results.filter((r) => r.tag !== "alt" && r.tag !== "sexAlt");
|
|
27471
27719
|
console.log(JSON.stringify(dataResults, null, 2));
|
|
27472
27720
|
} else {
|
|
@@ -27483,7 +27731,8 @@ function main() {
|
|
|
27483
27731
|
schemas: { type: "boolean" },
|
|
27484
27732
|
schema: { type: "boolean" },
|
|
27485
27733
|
args: { type: "string", short: "a" },
|
|
27486
|
-
format: { type: "string", short: "f", default: "human" }
|
|
27734
|
+
format: { type: "string", short: "f", default: "human" },
|
|
27735
|
+
pipe: { type: "boolean", short: "p" }
|
|
27487
27736
|
}
|
|
27488
27737
|
});
|
|
27489
27738
|
if (values.help && positionals.length === 0) {
|
|
@@ -27548,10 +27797,10 @@ Apache-2.0 \xA9 \xC7EDD / TSPED`);
|
|
|
27548
27797
|
console.error(`${yellow2("!")} Invalid JSON in --args: ${values.args}`);
|
|
27549
27798
|
process.exit(1);
|
|
27550
27799
|
}
|
|
27551
|
-
runToolWithArgs(toolName, args, format);
|
|
27800
|
+
runToolWithArgs(toolName, args, format, values.pipe);
|
|
27552
27801
|
} else if (kvPairs.length > 0) {
|
|
27553
27802
|
const args = parseKeyValueArgs(kvPairs, schema);
|
|
27554
|
-
runToolWithArgs(toolName, args, format);
|
|
27803
|
+
runToolWithArgs(toolName, args, format, values.pipe);
|
|
27555
27804
|
} else {
|
|
27556
27805
|
printToolInfo(toolName);
|
|
27557
27806
|
}
|