@geotechcli/core 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.
Files changed (194) hide show
  1. package/dist/agents/brain.d.ts +39 -0
  2. package/dist/agents/brain.d.ts.map +1 -0
  3. package/dist/agents/brain.js +339 -0
  4. package/dist/agents/brain.js.map +1 -0
  5. package/dist/agents/bridge-tools.d.ts +2 -0
  6. package/dist/agents/bridge-tools.d.ts.map +1 -0
  7. package/dist/agents/bridge-tools.js +170 -0
  8. package/dist/agents/bridge-tools.js.map +1 -0
  9. package/dist/agents/data-tools.d.ts +2 -0
  10. package/dist/agents/data-tools.d.ts.map +1 -0
  11. package/dist/agents/data-tools.js +309 -0
  12. package/dist/agents/data-tools.js.map +1 -0
  13. package/dist/agents/filesystem-tools.d.ts +2 -0
  14. package/dist/agents/filesystem-tools.d.ts.map +1 -0
  15. package/dist/agents/filesystem-tools.js +267 -0
  16. package/dist/agents/filesystem-tools.js.map +1 -0
  17. package/dist/agents/guardrails.d.ts +17 -0
  18. package/dist/agents/guardrails.d.ts.map +1 -0
  19. package/dist/agents/guardrails.js +260 -0
  20. package/dist/agents/guardrails.js.map +1 -0
  21. package/dist/agents/orchestrator.d.ts +9 -0
  22. package/dist/agents/orchestrator.d.ts.map +1 -0
  23. package/dist/agents/orchestrator.js +136 -0
  24. package/dist/agents/orchestrator.js.map +1 -0
  25. package/dist/agents/safety.d.ts +9 -0
  26. package/dist/agents/safety.d.ts.map +1 -0
  27. package/dist/agents/safety.js +40 -0
  28. package/dist/agents/safety.js.map +1 -0
  29. package/dist/agents/sandbox.d.ts +34 -0
  30. package/dist/agents/sandbox.d.ts.map +1 -0
  31. package/dist/agents/sandbox.js +235 -0
  32. package/dist/agents/sandbox.js.map +1 -0
  33. package/dist/agents/swarm.d.ts +25 -0
  34. package/dist/agents/swarm.d.ts.map +1 -0
  35. package/dist/agents/swarm.js +434 -0
  36. package/dist/agents/swarm.js.map +1 -0
  37. package/dist/agents/tools.d.ts +37 -0
  38. package/dist/agents/tools.d.ts.map +1 -0
  39. package/dist/agents/tools.js +451 -0
  40. package/dist/agents/tools.js.map +1 -0
  41. package/dist/bridge/index.d.ts +52 -0
  42. package/dist/bridge/index.d.ts.map +1 -0
  43. package/dist/bridge/index.js +195 -0
  44. package/dist/bridge/index.js.map +1 -0
  45. package/dist/config/index.d.ts +106 -0
  46. package/dist/config/index.d.ts.map +1 -0
  47. package/dist/config/index.js +217 -0
  48. package/dist/config/index.js.map +1 -0
  49. package/dist/db/index.d.ts +4 -0
  50. package/dist/db/index.d.ts.map +1 -0
  51. package/dist/db/index.js +4 -0
  52. package/dist/db/index.js.map +1 -0
  53. package/dist/db/redis.d.ts +14 -0
  54. package/dist/db/redis.d.ts.map +1 -0
  55. package/dist/db/redis.js +204 -0
  56. package/dist/db/redis.js.map +1 -0
  57. package/dist/db/supabase.d.ts +57 -0
  58. package/dist/db/supabase.d.ts.map +1 -0
  59. package/dist/db/supabase.js +156 -0
  60. package/dist/db/supabase.js.map +1 -0
  61. package/dist/db/users.d.ts +50 -0
  62. package/dist/db/users.d.ts.map +1 -0
  63. package/dist/db/users.js +132 -0
  64. package/dist/db/users.js.map +1 -0
  65. package/dist/export/index.d.ts +51 -0
  66. package/dist/export/index.d.ts.map +1 -0
  67. package/dist/export/index.js +126 -0
  68. package/dist/export/index.js.map +1 -0
  69. package/dist/geo/bearing-capacity.d.ts +60 -0
  70. package/dist/geo/bearing-capacity.d.ts.map +1 -0
  71. package/dist/geo/bearing-capacity.js +195 -0
  72. package/dist/geo/bearing-capacity.js.map +1 -0
  73. package/dist/geo/classification.d.ts +107 -0
  74. package/dist/geo/classification.d.ts.map +1 -0
  75. package/dist/geo/classification.js +261 -0
  76. package/dist/geo/classification.js.map +1 -0
  77. package/dist/geo/index.d.ts +9 -0
  78. package/dist/geo/index.d.ts.map +1 -0
  79. package/dist/geo/index.js +9 -0
  80. package/dist/geo/index.js.map +1 -0
  81. package/dist/geo/lateral-earth-pressure.d.ts +75 -0
  82. package/dist/geo/lateral-earth-pressure.d.ts.map +1 -0
  83. package/dist/geo/lateral-earth-pressure.js +219 -0
  84. package/dist/geo/lateral-earth-pressure.js.map +1 -0
  85. package/dist/geo/liquefaction.d.ts +65 -0
  86. package/dist/geo/liquefaction.d.ts.map +1 -0
  87. package/dist/geo/liquefaction.js +163 -0
  88. package/dist/geo/liquefaction.js.map +1 -0
  89. package/dist/geo/pile-capacity.d.ts +91 -0
  90. package/dist/geo/pile-capacity.d.ts.map +1 -0
  91. package/dist/geo/pile-capacity.js +233 -0
  92. package/dist/geo/pile-capacity.js.map +1 -0
  93. package/dist/geo/settlement.d.ts +119 -0
  94. package/dist/geo/settlement.d.ts.map +1 -0
  95. package/dist/geo/settlement.js +184 -0
  96. package/dist/geo/settlement.js.map +1 -0
  97. package/dist/geo/slope-stability.d.ts +82 -0
  98. package/dist/geo/slope-stability.d.ts.map +1 -0
  99. package/dist/geo/slope-stability.js +214 -0
  100. package/dist/geo/slope-stability.js.map +1 -0
  101. package/dist/geo/tunnel/index.d.ts +2 -0
  102. package/dist/geo/tunnel/index.d.ts.map +1 -0
  103. package/dist/geo/tunnel/index.js +2 -0
  104. package/dist/geo/tunnel/index.js.map +1 -0
  105. package/dist/geo/tunnel/tbm.d.ts +135 -0
  106. package/dist/geo/tunnel/tbm.d.ts.map +1 -0
  107. package/dist/geo/tunnel/tbm.js +268 -0
  108. package/dist/geo/tunnel/tbm.js.map +1 -0
  109. package/dist/index.d.ts +20 -0
  110. package/dist/index.d.ts.map +1 -0
  111. package/dist/index.js +33 -0
  112. package/dist/index.js.map +1 -0
  113. package/dist/ingest/ags.d.ts +42 -0
  114. package/dist/ingest/ags.d.ts.map +1 -0
  115. package/dist/ingest/ags.js +133 -0
  116. package/dist/ingest/ags.js.map +1 -0
  117. package/dist/ingest/cpt.d.ts +47 -0
  118. package/dist/ingest/cpt.d.ts.map +1 -0
  119. package/dist/ingest/cpt.js +112 -0
  120. package/dist/ingest/cpt.js.map +1 -0
  121. package/dist/ingest/index.d.ts +3 -0
  122. package/dist/ingest/index.d.ts.map +1 -0
  123. package/dist/ingest/index.js +3 -0
  124. package/dist/ingest/index.js.map +1 -0
  125. package/dist/llm/index.d.ts +5 -0
  126. package/dist/llm/index.d.ts.map +1 -0
  127. package/dist/llm/index.js +4 -0
  128. package/dist/llm/index.js.map +1 -0
  129. package/dist/llm/middleware/metering.d.ts +55 -0
  130. package/dist/llm/middleware/metering.d.ts.map +1 -0
  131. package/dist/llm/middleware/metering.js +191 -0
  132. package/dist/llm/middleware/metering.js.map +1 -0
  133. package/dist/llm/middleware/persistent-usage.d.ts +7 -0
  134. package/dist/llm/middleware/persistent-usage.d.ts.map +1 -0
  135. package/dist/llm/middleware/persistent-usage.js +108 -0
  136. package/dist/llm/middleware/persistent-usage.js.map +1 -0
  137. package/dist/llm/middleware/retry.d.ts +7 -0
  138. package/dist/llm/middleware/retry.d.ts.map +1 -0
  139. package/dist/llm/middleware/retry.js +29 -0
  140. package/dist/llm/middleware/retry.js.map +1 -0
  141. package/dist/llm/providers/anthropic.d.ts +10 -0
  142. package/dist/llm/providers/anthropic.d.ts.map +1 -0
  143. package/dist/llm/providers/anthropic.js +107 -0
  144. package/dist/llm/providers/anthropic.js.map +1 -0
  145. package/dist/llm/providers/hosted-beta.d.ts +10 -0
  146. package/dist/llm/providers/hosted-beta.d.ts.map +1 -0
  147. package/dist/llm/providers/hosted-beta.js +106 -0
  148. package/dist/llm/providers/hosted-beta.js.map +1 -0
  149. package/dist/llm/providers/huggingface.d.ts +37 -0
  150. package/dist/llm/providers/huggingface.d.ts.map +1 -0
  151. package/dist/llm/providers/huggingface.js +133 -0
  152. package/dist/llm/providers/huggingface.js.map +1 -0
  153. package/dist/llm/providers/openai-compatible.d.ts +27 -0
  154. package/dist/llm/providers/openai-compatible.d.ts.map +1 -0
  155. package/dist/llm/providers/openai-compatible.js +99 -0
  156. package/dist/llm/providers/openai-compatible.js.map +1 -0
  157. package/dist/llm/providers/zhipu.d.ts +10 -0
  158. package/dist/llm/providers/zhipu.d.ts.map +1 -0
  159. package/dist/llm/providers/zhipu.js +81 -0
  160. package/dist/llm/providers/zhipu.js.map +1 -0
  161. package/dist/llm/router.d.ts +35 -0
  162. package/dist/llm/router.d.ts.map +1 -0
  163. package/dist/llm/router.js +109 -0
  164. package/dist/llm/router.js.map +1 -0
  165. package/dist/llm/types.d.ts +63 -0
  166. package/dist/llm/types.d.ts.map +1 -0
  167. package/dist/llm/types.js +38 -0
  168. package/dist/llm/types.js.map +1 -0
  169. package/dist/meta/index.d.ts +12 -0
  170. package/dist/meta/index.d.ts.map +1 -0
  171. package/dist/meta/index.js +8 -0
  172. package/dist/meta/index.js.map +1 -0
  173. package/dist/meta/metadata.json +46 -0
  174. package/dist/report/index.d.ts +20 -0
  175. package/dist/report/index.d.ts.map +1 -0
  176. package/dist/report/index.js +58 -0
  177. package/dist/report/index.js.map +1 -0
  178. package/dist/standards/index.d.ts +23 -0
  179. package/dist/standards/index.d.ts.map +1 -0
  180. package/dist/standards/index.js +89 -0
  181. package/dist/standards/index.js.map +1 -0
  182. package/dist/storage/index.d.ts +114 -0
  183. package/dist/storage/index.d.ts.map +1 -0
  184. package/dist/storage/index.js +465 -0
  185. package/dist/storage/index.js.map +1 -0
  186. package/dist/vision/index.d.ts +80 -0
  187. package/dist/vision/index.d.ts.map +1 -0
  188. package/dist/vision/index.js +298 -0
  189. package/dist/vision/index.js.map +1 -0
  190. package/dist/vision/parse.d.ts +20 -0
  191. package/dist/vision/parse.d.ts.map +1 -0
  192. package/dist/vision/parse.js +75 -0
  193. package/dist/vision/parse.js.map +1 -0
  194. package/package.json +55 -0
@@ -0,0 +1,133 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ export function parseAGS(filePath) {
4
+ const content = readFileSync(resolve(filePath), 'utf-8');
5
+ return parseAGSContent(content);
6
+ }
7
+ export function parseAGSContent(content) {
8
+ const groups = new Map();
9
+ let currentGroup = null;
10
+ let section = 'none';
11
+ const lines = content.split(/\r?\n/);
12
+ for (const rawLine of lines) {
13
+ const line = rawLine.trim();
14
+ if (!line || line.startsWith('**'))
15
+ continue;
16
+ // Detect group start
17
+ if (line.startsWith('"GROUP"')) {
18
+ const match = line.match(/"GROUP"\s*,\s*"([^"]+)"/);
19
+ if (match) {
20
+ currentGroup = { name: match[1], headings: [], units: [], types: [], data: [] };
21
+ groups.set(match[1], currentGroup);
22
+ section = 'none';
23
+ }
24
+ continue;
25
+ }
26
+ if (!currentGroup)
27
+ continue;
28
+ // Parse row: comma-separated quoted values
29
+ const cells = parseAGSRow(line);
30
+ if (cells.length === 0)
31
+ continue;
32
+ const rowType = cells[0];
33
+ if (rowType === 'HEADING') {
34
+ currentGroup.headings = cells.slice(1);
35
+ section = 'heading';
36
+ }
37
+ else if (rowType === 'UNIT') {
38
+ currentGroup.units = cells.slice(1);
39
+ section = 'unit';
40
+ }
41
+ else if (rowType === 'TYPE') {
42
+ currentGroup.types = cells.slice(1);
43
+ section = 'type';
44
+ }
45
+ else if (rowType === 'DATA') {
46
+ const row = {};
47
+ const values = cells.slice(1);
48
+ currentGroup.headings.forEach((h, i) => {
49
+ const val = values[i] ?? '';
50
+ const type = currentGroup.types[i] ?? 'X';
51
+ if (val === '' || val === 'null') {
52
+ row[h] = null;
53
+ }
54
+ else if (type === '2DP' || type === '3DP' || type === '1DP' || type === '0DP' || type.startsWith('nDP') || type === 'SF') {
55
+ const num = parseFloat(val);
56
+ row[h] = isNaN(num) ? val : num;
57
+ }
58
+ else if (type === 'ID' || type === 'PA' || type === 'X' || type === 'XN' || type === 'DT') {
59
+ row[h] = val;
60
+ }
61
+ else {
62
+ const num = parseFloat(val);
63
+ row[h] = isNaN(num) ? val : num;
64
+ }
65
+ });
66
+ currentGroup.data.push(row);
67
+ }
68
+ }
69
+ // Extract structured data from known AGS groups
70
+ const projectInfo = {};
71
+ const projGroup = groups.get('PROJ');
72
+ if (projGroup && projGroup.data.length > 0) {
73
+ const row = projGroup.data[0];
74
+ for (const [k, v] of Object.entries(row)) {
75
+ if (v !== null)
76
+ projectInfo[k] = String(v);
77
+ }
78
+ }
79
+ // Boreholes from HOLE group
80
+ const boreholes = (groups.get('HOLE')?.data ?? []).map((row) => ({
81
+ id: String(row['HOLE_ID'] ?? row['LOCA_ID'] ?? ''),
82
+ easting: row['HOLE_NATE'] != null ? Number(row['HOLE_NATE']) : row['LOCA_NATE'] != null ? Number(row['LOCA_NATE']) : null,
83
+ northing: row['HOLE_NATN'] != null ? Number(row['HOLE_NATN']) : row['LOCA_NATN'] != null ? Number(row['LOCA_NATN']) : null,
84
+ groundLevel: row['HOLE_GL'] != null ? Number(row['HOLE_GL']) : row['LOCA_GL'] != null ? Number(row['LOCA_GL']) : null,
85
+ finalDepth: row['HOLE_FDEP'] != null ? Number(row['HOLE_FDEP']) : row['LOCA_FDEP'] != null ? Number(row['LOCA_FDEP']) : null,
86
+ }));
87
+ // Samples from SAMP group
88
+ const samples = (groups.get('SAMP')?.data ?? []).map((row) => ({
89
+ boreholeId: String(row['LOCA_ID'] ?? row['HOLE_ID'] ?? ''),
90
+ depthTop: Number(row['SAMP_TOP'] ?? 0),
91
+ depthBase: Number(row['SAMP_BASE'] ?? row['SAMP_TOP'] ?? 0),
92
+ type: String(row['SAMP_TYPE'] ?? ''),
93
+ description: String(row['SAMP_DESC'] ?? row['SAMP_REM'] ?? ''),
94
+ }));
95
+ // SPT from ISPT group
96
+ const sptResults = (groups.get('ISPT')?.data ?? []).map((row) => ({
97
+ boreholeId: String(row['LOCA_ID'] ?? row['HOLE_ID'] ?? ''),
98
+ depth: Number(row['ISPT_TOP'] ?? 0),
99
+ nValue: Number(row['ISPT_NVAL'] ?? row['ISPT_REP'] ?? 0),
100
+ n60: row['ISPT_N60'] != null ? Number(row['ISPT_N60']) : null,
101
+ }));
102
+ // Geology from GEOL group
103
+ const geology = (groups.get('GEOL')?.data ?? []).map((row) => ({
104
+ boreholeId: String(row['LOCA_ID'] ?? row['HOLE_ID'] ?? ''),
105
+ depthTop: Number(row['GEOL_TOP'] ?? 0),
106
+ depthBase: Number(row['GEOL_BASE'] ?? 0),
107
+ legend: String(row['GEOL_LEG'] ?? ''),
108
+ description: String(row['GEOL_DESC'] ?? ''),
109
+ uscs: String(row['GEOL_USCS'] ?? row['GEOL_STAT'] ?? ''),
110
+ }));
111
+ return { groups, projectInfo, boreholes, samples, sptResults, geology };
112
+ }
113
+ function parseAGSRow(line) {
114
+ const cells = [];
115
+ let current = '';
116
+ let inQuotes = false;
117
+ for (let i = 0; i < line.length; i++) {
118
+ const ch = line[i];
119
+ if (ch === '"') {
120
+ inQuotes = !inQuotes;
121
+ }
122
+ else if (ch === ',' && !inQuotes) {
123
+ cells.push(current.trim());
124
+ current = '';
125
+ }
126
+ else {
127
+ current += ch;
128
+ }
129
+ }
130
+ cells.push(current.trim());
131
+ return cells;
132
+ }
133
+ //# sourceMappingURL=ags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ags.js","sourceRoot":"","sources":["../../src/ingest/ags.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ export interface CPTReading {
2
+ depth: number;
3
+ qc: number;
4
+ fs: number;
5
+ u2: number;
6
+ qt: number;
7
+ Rf: number;
8
+ Bq: number;
9
+ Ic: number;
10
+ SBTn: number;
11
+ SBTnDescription: string;
12
+ sigmav: number;
13
+ sigmavPrime: number;
14
+ Qtn: number;
15
+ Fr: number;
16
+ }
17
+ export interface CPTProfile {
18
+ id: string;
19
+ readings: CPTReading[];
20
+ waterTableDepth: number;
21
+ areaRatio: number;
22
+ summary: {
23
+ maxDepth: number;
24
+ avgQc: number;
25
+ dominantSoilType: string;
26
+ layerBoundaries: Array<{
27
+ depth: number;
28
+ from: string;
29
+ to: string;
30
+ }>;
31
+ };
32
+ }
33
+ export interface CPTParseOptions {
34
+ id?: string;
35
+ waterTableDepth?: number;
36
+ areaRatio?: number;
37
+ unitWeight?: number;
38
+ columns?: {
39
+ depth?: string;
40
+ qc?: string;
41
+ fs?: string;
42
+ u2?: string;
43
+ };
44
+ }
45
+ export declare function parseCPT(filePath: string, options?: CPTParseOptions): CPTProfile;
46
+ export declare function parseCPTContent(content: string, options?: CPTParseOptions): CPTProfile;
47
+ //# sourceMappingURL=cpt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cpt.d.ts","sourceRoot":"","sources":["../../src/ingest/cpt.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,gBAAgB,EAAE,MAAM,CAAC;QACzB,eAAe,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACrE,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAoBD,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,UAAU,CAGhF;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,UAAU,CAqGtF"}
@@ -0,0 +1,112 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ // ---------------------------------------------------------------------------
4
+ // Robertson SBTn classification (1990, updated 2016)
5
+ // ---------------------------------------------------------------------------
6
+ function classifyRobertsonSBTn(Ic) {
7
+ if (Ic > 3.6)
8
+ return { zone: 1, description: 'Sensitive fine grained' };
9
+ if (Ic > 2.95)
10
+ return { zone: 2, description: 'Organic soils — clay' };
11
+ if (Ic > 2.6)
12
+ return { zone: 3, description: 'Clay — silty clay' };
13
+ if (Ic > 2.05)
14
+ return { zone: 4, description: 'Silt mixtures — clayey silt' };
15
+ if (Ic > 1.31)
16
+ return { zone: 5, description: 'Sand mixtures — silty sand' };
17
+ if (Ic > 1.0)
18
+ return { zone: 6, description: 'Clean sand to silty sand' };
19
+ return { zone: 7, description: 'Gravelly sand to dense sand' };
20
+ }
21
+ // ---------------------------------------------------------------------------
22
+ // Parse CPT data from CSV
23
+ // ---------------------------------------------------------------------------
24
+ export function parseCPT(filePath, options) {
25
+ const content = readFileSync(resolve(filePath), 'utf-8');
26
+ return parseCPTContent(content, options);
27
+ }
28
+ export function parseCPTContent(content, options) {
29
+ const lines = content.trim().split(/\r?\n/);
30
+ if (lines.length < 2)
31
+ throw new Error('CPT file must have at least header + one data row');
32
+ const header = lines[0].split(',').map((h) => h.trim().toLowerCase().replace(/['"]/g, ''));
33
+ // Auto-detect columns
34
+ const depthCol = options?.columns?.depth ?? header.find((h) => /depth|z/i.test(h)) ?? header[0];
35
+ const qcCol = options?.columns?.qc ?? header.find((h) => /qc|q_c|tip/i.test(h)) ?? header[1];
36
+ const fsCol = options?.columns?.fs ?? header.find((h) => /fs|f_s|sleeve/i.test(h)) ?? header[2];
37
+ const u2Col = options?.columns?.u2 ?? header.find((h) => /u2|u_2|pore/i.test(h)) ?? header[3];
38
+ const depthIdx = header.indexOf(depthCol);
39
+ const qcIdx = header.indexOf(qcCol);
40
+ const fsIdx = header.indexOf(fsCol);
41
+ const u2Idx = header.indexOf(u2Col);
42
+ const gwt = options?.waterTableDepth ?? 1.0;
43
+ const a = options?.areaRatio ?? 0.8;
44
+ const gamma = options?.unitWeight ?? 18;
45
+ const gammaW = 9.81;
46
+ const readings = [];
47
+ for (let i = 1; i < lines.length; i++) {
48
+ const cols = lines[i].split(',').map((c) => c.trim().replace(/['"]/g, ''));
49
+ const depth = parseFloat(cols[depthIdx]);
50
+ const qc = parseFloat(cols[qcIdx]);
51
+ const fs = parseFloat(cols[fsIdx]) || 0;
52
+ const u2Raw = u2Idx >= 0 ? parseFloat(cols[u2Idx]) : 0;
53
+ if (isNaN(depth) || isNaN(qc))
54
+ continue;
55
+ const u2 = isNaN(u2Raw) ? 0 : u2Raw;
56
+ // Corrected tip resistance: qt = qc + u2(1 - a)
57
+ const qt = qc + (u2 / 1000) * (1 - a); // u2 in kPa, qt in MPa
58
+ // Stresses
59
+ const sigmav = gamma * depth;
60
+ const u0 = depth > gwt ? gammaW * (depth - gwt) : 0;
61
+ const sigmavPrime = Math.max(sigmav - u0, 1);
62
+ // Friction ratio
63
+ const Rf = qc > 0 ? (fs / (qc * 1000)) * 100 : 0;
64
+ // Pore pressure ratio
65
+ const Bq = qt > sigmav / 1000 ? (u2 / 1000) / (qt - sigmav / 1000) : 0;
66
+ // Normalized parameters
67
+ const pa = 0.1; // Atmospheric pressure in MPa
68
+ const Qtn = ((qt - sigmav / 1000) / pa) * Math.pow(pa / (sigmavPrime / 1000), 0.5);
69
+ const Fr = sigmav / 1000 < qt ? (fs / 1000) / (qt - sigmav / 1000) * 100 : 0;
70
+ // Soil Behavior Type Index (Robertson 2009)
71
+ const Ic = Math.sqrt(Math.pow(3.47 - Math.log10(Math.max(Qtn, 1)), 2) + Math.pow(Math.log10(Math.max(Fr, 0.1)) + 1.22, 2));
72
+ const sbt = classifyRobertsonSBTn(Ic);
73
+ readings.push({
74
+ depth, qc, fs, u2, qt: Math.round(qt * 1000) / 1000,
75
+ Rf: Math.round(Rf * 100) / 100, Bq: Math.round(Bq * 1000) / 1000,
76
+ Ic: Math.round(Ic * 100) / 100, SBTn: sbt.zone, SBTnDescription: sbt.description,
77
+ sigmav: Math.round(sigmav * 10) / 10, sigmavPrime: Math.round(sigmavPrime * 10) / 10,
78
+ Qtn: Math.round(Qtn * 10) / 10, Fr: Math.round(Fr * 100) / 100,
79
+ });
80
+ }
81
+ // Detect layer boundaries (where SBTn changes)
82
+ const layerBoundaries = [];
83
+ for (let i = 1; i < readings.length; i++) {
84
+ if (readings[i].SBTn !== readings[i - 1].SBTn) {
85
+ layerBoundaries.push({
86
+ depth: readings[i].depth,
87
+ from: readings[i - 1].SBTnDescription,
88
+ to: readings[i].SBTnDescription,
89
+ });
90
+ }
91
+ }
92
+ // Dominant soil type
93
+ const zoneCounts = new Map();
94
+ for (const r of readings) {
95
+ zoneCounts.set(r.SBTnDescription, (zoneCounts.get(r.SBTnDescription) ?? 0) + 1);
96
+ }
97
+ const dominantSoilType = [...zoneCounts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? 'Unknown';
98
+ const avgQc = readings.length > 0 ? readings.reduce((s, r) => s + r.qc, 0) / readings.length : 0;
99
+ return {
100
+ id: options?.id ?? 'CPT-01',
101
+ readings,
102
+ waterTableDepth: gwt,
103
+ areaRatio: a,
104
+ summary: {
105
+ maxDepth: readings.length > 0 ? readings[readings.length - 1].depth : 0,
106
+ avgQc: Math.round(avgQc * 100) / 100,
107
+ dominantSoilType,
108
+ layerBoundaries,
109
+ },
110
+ };
111
+ }
112
+ //# sourceMappingURL=cpt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cpt.js","sourceRoot":"","sources":["../../src/ingest/cpt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { parseAGS, parseAGSContent, type AGSFile, type AGSGroup } from './ags.js';
2
+ export { parseCPT, parseCPTContent, type CPTProfile, type CPTReading, type CPTParseOptions } from './cpt.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ingest/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAClF,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { parseAGS, parseAGSContent } from './ags.js';
2
+ export { parseCPT, parseCPTContent } from './cpt.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ingest/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,eAAe,EAA+B,MAAM,UAAU,CAAC;AAClF,OAAO,EAAE,QAAQ,EAAE,eAAe,EAA0D,MAAM,UAAU,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { generateText, generateChat, generateVision, registry } from './router.js';
2
+ export { withRetry } from './middleware/retry.js';
3
+ export type { LLMProvider, LLMConfig, ChatMessage, ContentPart, CompletionRequest, CompletionResponse, TokenUsage, ProviderAdapter, UserTier, TierLimits, } from './types.js';
4
+ export { TIER_LIMITS } from './types.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/llm/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,YAAY,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,QAAQ,EACR,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { generateText, generateChat, generateVision, registry } from './router.js';
2
+ export { withRetry } from './middleware/retry.js';
3
+ export { TIER_LIMITS } from './types.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/llm/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAalD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,55 @@
1
+ import type { UserTier } from '../types.js';
2
+ export interface UsageRecord {
3
+ identifier: string;
4
+ tier: UserTier;
5
+ llmCalls: number;
6
+ visionCalls: number;
7
+ agentCalls: number;
8
+ periodStart: number;
9
+ lastCallTimestamp: number;
10
+ }
11
+ export interface UsageStore {
12
+ get(identifier: string): Promise<UsageRecord | null>;
13
+ set(identifier: string, record: UsageRecord): Promise<void>;
14
+ increment(identifier: string, field: 'llmCalls' | 'visionCalls' | 'agentCalls'): Promise<UsageRecord>;
15
+ }
16
+ export declare class InMemoryUsageStore implements UsageStore {
17
+ private records;
18
+ get(identifier: string): Promise<UsageRecord | null>;
19
+ set(identifier: string, record: UsageRecord): Promise<void>;
20
+ increment(identifier: string, field: 'llmCalls' | 'visionCalls' | 'agentCalls'): Promise<UsageRecord>;
21
+ }
22
+ export interface AbuseCheckResult {
23
+ allowed: boolean;
24
+ reason?: string;
25
+ remainingCalls?: number;
26
+ shouldPromptUpgrade: boolean;
27
+ upgradeMessage?: string;
28
+ }
29
+ /**
30
+ * Generate a fingerprint for unregistered users.
31
+ * Combines IP address with user-agent for basic identification.
32
+ * Not foolproof but raises the bar significantly.
33
+ */
34
+ export declare function generateFingerprint(ip: string, userAgent?: string, additionalSignals?: string[]): string;
35
+ /**
36
+ * Check if a request should be allowed based on usage and abuse patterns.
37
+ *
38
+ * Logic:
39
+ * 1. Registered users (have API key) → check tier limits
40
+ * 2. Unregistered users → max 5 calls total, then hard block with upgrade prompt
41
+ * 3. Rate limiting → max 10 calls/minute regardless of tier
42
+ */
43
+ export declare function checkUsageAndAbuse(store: UsageStore, identifier: string, callType: 'llmCalls' | 'visionCalls' | 'agentCalls', tier: UserTier, isRegistered: boolean): Promise<AbuseCheckResult>;
44
+ export interface SuspiciousActivityCheck {
45
+ isSuspicious: boolean;
46
+ signals: string[];
47
+ }
48
+ /**
49
+ * Check for suspicious patterns that suggest abuse:
50
+ * - Rapid IP rotation (same fingerprint, different IPs)
51
+ * - Known VPN/proxy indicators
52
+ * - Abnormal request patterns
53
+ */
54
+ export declare function detectSuspiciousActivity(ip: string, headers: Record<string, string | undefined>, recentIPs?: string[]): SuspiciousActivityCheck;
55
+ //# sourceMappingURL=metering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metering.d.ts","sourceRoot":"","sources":["../../../src/llm/middleware/metering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,aAAa,CAAC;AAQxD,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACrD,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,aAAa,GAAG,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CACvG;AAMD,qBAAa,kBAAmB,YAAW,UAAU;IACnD,OAAO,CAAC,OAAO,CAAkC;IAE3C,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAiBpD,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D,SAAS,CACb,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,UAAU,GAAG,aAAa,GAAG,YAAY,GAC/C,OAAO,CAAC,WAAW,CAAC;CAmBxB;AAYD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,MAAM,EACV,SAAS,CAAC,EAAE,MAAM,EAClB,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAC3B,MAAM,CAQR;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,UAAU,GAAG,aAAa,GAAG,YAAY,EACnD,IAAI,EAAE,QAAQ,EACd,YAAY,EAAE,OAAO,GACpB,OAAO,CAAC,gBAAgB,CAAC,CA0F3B;AAMD,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAQD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC3C,SAAS,CAAC,EAAE,MAAM,EAAE,GACnB,uBAAuB,CA4BzB"}
@@ -0,0 +1,191 @@
1
+ import { TIER_LIMITS } from '../types.js';
2
+ import { createHash } from 'node:crypto';
3
+ // ---------------------------------------------------------------------------
4
+ // In-memory store (for CLI and tests)
5
+ // ---------------------------------------------------------------------------
6
+ export class InMemoryUsageStore {
7
+ records = new Map();
8
+ async get(identifier) {
9
+ const record = this.records.get(identifier);
10
+ if (!record)
11
+ return null;
12
+ // Reset if period expired (monthly)
13
+ const now = Date.now();
14
+ const monthMs = 30 * 24 * 60 * 60 * 1000;
15
+ if (now - record.periodStart > monthMs) {
16
+ record.llmCalls = 0;
17
+ record.visionCalls = 0;
18
+ record.agentCalls = 0;
19
+ record.periodStart = now;
20
+ }
21
+ return record;
22
+ }
23
+ async set(identifier, record) {
24
+ this.records.set(identifier, record);
25
+ }
26
+ async increment(identifier, field) {
27
+ let record = await this.get(identifier);
28
+ if (!record) {
29
+ record = {
30
+ identifier,
31
+ tier: 'free',
32
+ llmCalls: 0,
33
+ visionCalls: 0,
34
+ agentCalls: 0,
35
+ periodStart: Date.now(),
36
+ lastCallTimestamp: Date.now(),
37
+ };
38
+ }
39
+ record[field] += 1;
40
+ record.lastCallTimestamp = Date.now();
41
+ this.records.set(identifier, record);
42
+ return record;
43
+ }
44
+ }
45
+ // ---------------------------------------------------------------------------
46
+ // Abuse detection — fingerprinting and rate limiting
47
+ // ---------------------------------------------------------------------------
48
+ // Unregistered users: max 5 API calls total, then must register
49
+ const UNREGISTERED_MAX_CALLS = 5;
50
+ // Rate limiting: max calls per minute per identifier
51
+ const RATE_LIMIT_PER_MINUTE = 10;
52
+ /**
53
+ * Generate a fingerprint for unregistered users.
54
+ * Combines IP address with user-agent for basic identification.
55
+ * Not foolproof but raises the bar significantly.
56
+ */
57
+ export function generateFingerprint(ip, userAgent, additionalSignals) {
58
+ const raw = [
59
+ ip,
60
+ userAgent ?? '',
61
+ ...(additionalSignals ?? []),
62
+ ].join('|');
63
+ return createHash('sha256').update(raw).digest('hex').slice(0, 32);
64
+ }
65
+ /**
66
+ * Check if a request should be allowed based on usage and abuse patterns.
67
+ *
68
+ * Logic:
69
+ * 1. Registered users (have API key) → check tier limits
70
+ * 2. Unregistered users → max 5 calls total, then hard block with upgrade prompt
71
+ * 3. Rate limiting → max 10 calls/minute regardless of tier
72
+ */
73
+ export async function checkUsageAndAbuse(store, identifier, callType, tier, isRegistered) {
74
+ const record = await store.get(identifier);
75
+ const limits = TIER_LIMITS[tier];
76
+ // --- Unregistered user check ---
77
+ if (!isRegistered) {
78
+ const totalCalls = record
79
+ ? record.llmCalls + record.visionCalls + record.agentCalls
80
+ : 0;
81
+ if (totalCalls >= UNREGISTERED_MAX_CALLS) {
82
+ return {
83
+ allowed: false,
84
+ reason: 'Free trial limit reached (5 AI calls without registration).',
85
+ remainingCalls: 0,
86
+ shouldPromptUpgrade: true,
87
+ upgradeMessage: `
88
+ ╔══════════════════════════════════════════════════════════╗
89
+ ║ You've used your 5 free AI calls. ║
90
+ ║ ║
91
+ ║ To continue using AI features: ║
92
+ ║ ║
93
+ ║ → Lite Pro ($15/mo) Unlimited GLM models ║
94
+ ║ → Pro ($49/mo) Bring Your Own LLM ║
95
+ ║ → Annual ($399/yr) Everything + SLA ║
96
+ ║ ║
97
+ ║ Register at: https://beta.geotechcli.com/pricing ║
98
+ ║ No login is required on strong-beta ║
99
+ ║ ║
100
+ ║ Deterministic calculations remain FREE forever. ║
101
+ ╚══════════════════════════════════════════════════════════╝`,
102
+ };
103
+ }
104
+ const remaining = UNREGISTERED_MAX_CALLS - totalCalls - 1;
105
+ return {
106
+ allowed: true,
107
+ remainingCalls: remaining,
108
+ shouldPromptUpgrade: remaining <= 2,
109
+ upgradeMessage: remaining <= 2
110
+ ? `⚠ ${remaining} free AI call${remaining === 1 ? '' : 's'} remaining. See beta.geotechcli.com/pricing for current strong-beta status.`
111
+ : undefined,
112
+ };
113
+ }
114
+ // --- Registered user: check tier limits ---
115
+ const currentCount = record ? record[callType] : 0;
116
+ const limitForType = callType === 'llmCalls'
117
+ ? limits.llmCallsPerMonth
118
+ : callType === 'visionCalls'
119
+ ? limits.visionCallsPerMonth
120
+ : limits.agentCallsPerMonth;
121
+ if (currentCount >= limitForType) {
122
+ const tierName = tier === 'free' ? 'Free' : tier === 'lite_pro' ? 'Lite Pro' : tier === 'pro' ? 'Pro' : 'Annual';
123
+ return {
124
+ allowed: false,
125
+ reason: `Monthly ${callType.replace('Calls', '')} limit reached for ${tierName} tier (${limitForType}/month).`,
126
+ remainingCalls: 0,
127
+ shouldPromptUpgrade: tier === 'free' || tier === 'lite_pro',
128
+ upgradeMessage: tier === 'free'
129
+ ? `Upgrade to Lite Pro ($15/mo) for 1000 analyses/month, or Pro ($49/mo) for unlimited. See beta.geotechcli.com/pricing`
130
+ : tier === 'lite_pro'
131
+ ? `Upgrade to Pro ($49/mo) for unlimited analyses + BYOL. Visit geotechcli.com/pricing`
132
+ : undefined,
133
+ };
134
+ }
135
+ // --- Rate limiting (burst protection) ---
136
+ if (record) {
137
+ const timeSinceLastCall = Date.now() - record.lastCallTimestamp;
138
+ const totalRecentCalls = record.llmCalls + record.visionCalls + record.agentCalls;
139
+ // Simple burst detection: if more than 10 calls in last 60 seconds
140
+ if (timeSinceLastCall < 6000 && totalRecentCalls > RATE_LIMIT_PER_MINUTE) {
141
+ return {
142
+ allowed: false,
143
+ reason: 'Rate limit exceeded. Please wait a moment before making another request.',
144
+ shouldPromptUpgrade: false,
145
+ };
146
+ }
147
+ }
148
+ const remaining = limitForType === Infinity ? Infinity : limitForType - currentCount - 1;
149
+ return {
150
+ allowed: true,
151
+ remainingCalls: remaining,
152
+ shouldPromptUpgrade: false,
153
+ };
154
+ }
155
+ const knownVPNHeaders = [
156
+ 'cf-connecting-ip',
157
+ 'x-forwarded-for',
158
+ 'true-client-ip',
159
+ ];
160
+ /**
161
+ * Check for suspicious patterns that suggest abuse:
162
+ * - Rapid IP rotation (same fingerprint, different IPs)
163
+ * - Known VPN/proxy indicators
164
+ * - Abnormal request patterns
165
+ */
166
+ export function detectSuspiciousActivity(ip, headers, recentIPs) {
167
+ const signals = [];
168
+ // Check for multiple forwarded IPs (proxy chain)
169
+ const forwardedFor = headers['x-forwarded-for'];
170
+ if (forwardedFor && forwardedFor.split(',').length > 3) {
171
+ signals.push('Deep proxy chain detected (>3 hops)');
172
+ }
173
+ // Check for IP mismatch between headers
174
+ const cfIP = headers['cf-connecting-ip'];
175
+ const trueIP = headers['true-client-ip'];
176
+ if (cfIP && trueIP && cfIP !== trueIP) {
177
+ signals.push('IP mismatch between Cloudflare and True-Client-IP headers');
178
+ }
179
+ // Check for rapid IP changes (if history provided)
180
+ if (recentIPs && recentIPs.length >= 3) {
181
+ const uniqueIPs = new Set(recentIPs.slice(-5));
182
+ if (uniqueIPs.size >= 4) {
183
+ signals.push('Rapid IP rotation detected (4+ unique IPs in recent requests)');
184
+ }
185
+ }
186
+ return {
187
+ isSuspicious: signals.length >= 2,
188
+ signals,
189
+ };
190
+ }
191
+ //# sourceMappingURL=metering.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metering.js","sourceRoot":"","sources":["../../../src/llm/middleware/metering.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { UsageRecord, UsageStore } from './metering.js';
2
+ export declare class FileUsageStore implements UsageStore {
3
+ get(identifier: string): Promise<UsageRecord | null>;
4
+ set(identifier: string, record: UsageRecord): Promise<void>;
5
+ increment(identifier: string, field: 'llmCalls' | 'visionCalls' | 'agentCalls'): Promise<UsageRecord>;
6
+ }
7
+ //# sourceMappingURL=persistent-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistent-usage.d.ts","sourceRoot":"","sources":["../../../src/llm/middleware/persistent-usage.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAsD7D,qBAAa,cAAe,YAAW,UAAU;IACzC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAkBpD,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3D,SAAS,CACb,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,UAAU,GAAG,aAAa,GAAG,YAAY,GAC/C,OAAO,CAAC,WAAW,CAAC;CA8BxB"}