railwise-ai 1.2.32 → 1.2.34

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 (173) hide show
  1. package/agent-pack/README.md +16 -0
  2. package/agent-pack/assets/agent/chief_manager.md +69 -0
  3. package/agent-pack/assets/agent/commercial_specialist.md +47 -0
  4. package/agent-pack/assets/agent/data_analyst.md +54 -0
  5. package/agent-pack/assets/agent/docs.md +38 -0
  6. package/agent-pack/assets/agent/duplicate-pr.md +25 -0
  7. package/agent-pack/assets/agent/qa_inspector.md +58 -0
  8. package/agent-pack/assets/agent/qa_reviewer.md +57 -0
  9. package/agent-pack/assets/agent/solution_architect.md +53 -0
  10. package/agent-pack/assets/agent/technical_writer.md +61 -0
  11. package/agent-pack/assets/agent/translator.md +34 -0
  12. package/agent-pack/assets/agent/triage.md +139 -0
  13. package/agent-pack/assets/command/ai-deps.md +24 -0
  14. package/agent-pack/assets/command/bid-intel.md +29 -0
  15. package/agent-pack/assets/command/bid-prepare.md +24 -0
  16. package/agent-pack/assets/command/commit.md +37 -0
  17. package/agent-pack/assets/command/daily-report.md +26 -0
  18. package/agent-pack/assets/command/data-check.md +22 -0
  19. package/agent-pack/assets/command/emergency-response.md +66 -0
  20. package/agent-pack/assets/command/issues.md +23 -0
  21. package/agent-pack/assets/command/learn.md +42 -0
  22. package/agent-pack/assets/command/monthly-report.md +35 -0
  23. package/agent-pack/assets/command/payment-reminder.md +27 -0
  24. package/agent-pack/assets/command/plan-draft.md +28 -0
  25. package/agent-pack/assets/command/review-response.md +23 -0
  26. package/agent-pack/assets/command/rmslop.md +15 -0
  27. package/agent-pack/assets/command/safety-check.md +20 -0
  28. package/agent-pack/assets/command/spellcheck.md +5 -0
  29. package/agent-pack/assets/command/trend-analysis.md +38 -0
  30. package/agent-pack/assets/command/weekly-report.md +28 -0
  31. package/agent-pack/assets/lib/os_api.ts +100 -0
  32. package/agent-pack/assets/skill/adjustment-report/SKILL.md +88 -0
  33. package/agent-pack/assets/skill/approval-flow-intelligence/SKILL.md +123 -0
  34. package/agent-pack/assets/skill/bidding-knowledge/SKILL.md +98 -0
  35. package/agent-pack/assets/skill/bun-file-io/SKILL.md +42 -0
  36. package/agent-pack/assets/skill/business-finance/SKILL.md +91 -0
  37. package/agent-pack/assets/skill/business-operations-analytics/SKILL.md +120 -0
  38. package/agent-pack/assets/skill/cad-bim-review/SKILL.md +84 -0
  39. package/agent-pack/assets/skill/canvas-design/SKILL.md +141 -0
  40. package/agent-pack/assets/skill/construction-monitoring/SKILL.md +89 -0
  41. package/agent-pack/assets/skill/customer-portal-brief/SKILL.md +121 -0
  42. package/agent-pack/assets/skill/data-analysis/SKILL.md +79 -0
  43. package/agent-pack/assets/skill/di-bao-monitoring/SKILL.md +680 -0
  44. package/agent-pack/assets/skill/di-bao-monitoring/agents/openai.yaml +4 -0
  45. package/agent-pack/assets/skill/di-bao-monitoring/assets/crossing-static-level-input-template.csv +2 -0
  46. package/agent-pack/assets/skill/di-bao-monitoring/assets/crossing-total-station-input-template.csv +2 -0
  47. package/agent-pack/assets/skill/di-bao-monitoring/assets/crossing-total-station-report-template.md +59 -0
  48. package/agent-pack/assets/skill/di-bao-monitoring/assets/daily-report-template.md +90 -0
  49. package/agent-pack/assets/skill/di-bao-monitoring/assets/data-input-template.csv +2 -0
  50. package/agent-pack/assets/skill/di-bao-monitoring/assets/initial-value-report-template.md +92 -0
  51. package/agent-pack/assets/skill/di-bao-monitoring/assets/monthly-report-template.md +103 -0
  52. package/agent-pack/assets/skill/di-bao-monitoring/assets/plan-template.md +673 -0
  53. package/agent-pack/assets/skill/di-bao-monitoring/assets/point-alias-map-template.json +25 -0
  54. package/agent-pack/assets/skill/di-bao-monitoring/assets/project-brief-template.md +59 -0
  55. package/agent-pack/assets/skill/di-bao-monitoring/assets/project-summary-template.md +154 -0
  56. package/agent-pack/assets/skill/di-bao-monitoring/assets/warning-notice-template.md +58 -0
  57. package/agent-pack/assets/skill/di-bao-monitoring/assets/weekly-report-template.md +49 -0
  58. package/agent-pack/assets/skill/di-bao-monitoring/assets//345/256/214/346/225/264/346/212/245/350/241/250/346/250/241/346/235/277.xlsx +0 -0
  59. package/agent-pack/assets/skill/di-bao-monitoring/assets//345/277/253/346/212/245/346/250/241/346/235/277.xlsx +0 -0
  60. package/agent-pack/assets/skill/di-bao-monitoring/references/crossing-static-level-report.md +115 -0
  61. package/agent-pack/assets/skill/di-bao-monitoring/references/crossing-total-station-report.md +206 -0
  62. package/agent-pack/assets/skill/di-bao-monitoring/references/data-processing.md +170 -0
  63. package/agent-pack/assets/skill/di-bao-monitoring/references/elevated-line.md +319 -0
  64. package/agent-pack/assets/skill/di-bao-monitoring/references/initial-and-summary-workflow.md +222 -0
  65. package/agent-pack/assets/skill/di-bao-monitoring/references/key-difficulties.md +216 -0
  66. package/agent-pack/assets/skill/di-bao-monitoring/references/monitoring-catalog.md +80 -0
  67. package/agent-pack/assets/skill/di-bao-monitoring/references/phase0-intake.md +169 -0
  68. package/agent-pack/assets/skill/di-bao-monitoring/references/phase1-drafting.md +185 -0
  69. package/agent-pack/assets/skill/di-bao-monitoring/references/phase2-internal-review.md +166 -0
  70. package/agent-pack/assets/skill/di-bao-monitoring/references/report-workflow.md +181 -0
  71. package/agent-pack/assets/skill/di-bao-monitoring/references/review-checklist.md +140 -0
  72. package/agent-pack/assets/skill/di-bao-monitoring/references/review-response-template.md +87 -0
  73. package/agent-pack/assets/skill/di-bao-monitoring/references/scoping-and-pricing.md +214 -0
  74. package/agent-pack/assets/skill/di-bao-monitoring/references/source-analysis.md +137 -0
  75. package/agent-pack/assets/skill/di-bao-monitoring/references/technical-standards.md +188 -0
  76. package/agent-pack/assets/skill/di-bao-monitoring/references/toc-template-A.md +142 -0
  77. package/agent-pack/assets/skill/di-bao-monitoring/references/toc-template-B.md +100 -0
  78. package/agent-pack/assets/skill/di-bao-monitoring/references/warning-workflow.md +63 -0
  79. package/agent-pack/assets/skill/di-bao-monitoring/scripts/build_crossing_total_station_xlsx.py +2438 -0
  80. package/agent-pack/assets/skill/di-bao-monitoring/scripts/evaluate_alarms.py +215 -0
  81. package/agent-pack/assets/skill/di-bao-monitoring/scripts/fetch_adjusted_total_station.py +946 -0
  82. package/agent-pack/assets/skill/di-bao-monitoring/scripts/fetch_shhh_static_level.py +556 -0
  83. package/agent-pack/assets/skill/di-bao-monitoring/scripts/run_crossing_4h_report.sh +141 -0
  84. package/agent-pack/assets/skill/di-bao-monitoring/scripts/summarize_crossing_total_station.py +303 -0
  85. package/agent-pack/assets/skill/docx-generation/SKILL.md +141 -0
  86. package/agent-pack/assets/skill/excel-operations/SKILL.md +121 -0
  87. package/agent-pack/assets/skill/frontend-design/SKILL.md +151 -0
  88. package/agent-pack/assets/skill/humanizer/SKILL.md +90 -0
  89. package/agent-pack/assets/skill/monitoring-design/SKILL.md +100 -0
  90. package/agent-pack/assets/skill/operational-monitoring/SKILL.md +135 -0
  91. package/agent-pack/assets/skill/operational-monitoring/assets/checklists/archive-self-check.md +54 -0
  92. package/agent-pack/assets/skill/operational-monitoring/assets/schemas/convergence-result-table.csv +5 -0
  93. package/agent-pack/assets/skill/operational-monitoring/assets/schemas/horizontal-result-table.csv +3 -0
  94. package/agent-pack/assets/skill/operational-monitoring/assets/schemas/settlement-result-table.csv +7 -0
  95. package/agent-pack/assets/skill/operational-monitoring/assets/scripts/init-archive-tree.sh +65 -0
  96. package/agent-pack/assets/skill/operational-monitoring/assets/templates/control-network-report.md +102 -0
  97. package/agent-pack/assets/skill/operational-monitoring/assets/templates/daily-log.md +47 -0
  98. package/agent-pack/assets/skill/operational-monitoring/assets/templates/i-angle-check.md +38 -0
  99. package/agent-pack/assets/skill/operational-monitoring/assets/templates/monitoring-scheme.md +234 -0
  100. package/agent-pack/assets/skill/operational-monitoring/assets/templates/period-report.md +95 -0
  101. package/agent-pack/assets/skill/operational-monitoring/assets/templates/point-acceptance-record.md +51 -0
  102. package/agent-pack/assets/skill/operational-monitoring/assets/templates/point-installation-record.md +38 -0
  103. package/agent-pack/assets/skill/operational-monitoring/assets/templates/summary-report.md +89 -0
  104. package/agent-pack/assets/skill/operational-monitoring/assets/templates/warning-bulletin.md +61 -0
  105. package/agent-pack/assets/skill/operational-monitoring/assets/templates/weekly-monthly-report.md +46 -0
  106. package/agent-pack/assets/skill/operational-monitoring/references/archive-and-delivery.md +146 -0
  107. package/agent-pack/assets/skill/operational-monitoring/references/baseline-network.md +131 -0
  108. package/agent-pack/assets/skill/operational-monitoring/references/convergence-monitoring.md +134 -0
  109. package/agent-pack/assets/skill/operational-monitoring/references/data-processing.md +178 -0
  110. package/agent-pack/assets/skill/operational-monitoring/references/horizontal-displacement.md +128 -0
  111. package/agent-pack/assets/skill/operational-monitoring/references/monitoring-points.md +132 -0
  112. package/agent-pack/assets/skill/operational-monitoring/references/monitoring-scheme.md +178 -0
  113. package/agent-pack/assets/skill/operational-monitoring/references/period-report.md +108 -0
  114. package/agent-pack/assets/skill/operational-monitoring/references/regulations-and-frequency.md +127 -0
  115. package/agent-pack/assets/skill/operational-monitoring/references/settlement-monitoring.md +116 -0
  116. package/agent-pack/assets/skill/operational-monitoring/references/summary-report.md +128 -0
  117. package/agent-pack/assets/skill/operational-monitoring/references/warning-and-disposal.md +118 -0
  118. package/agent-pack/assets/skill/ops-monitoring/SKILL.md +93 -0
  119. package/agent-pack/assets/skill/railwise-knowledge-curation/SKILL.md +92 -0
  120. package/agent-pack/assets/skill/report-dibao/SKILL.md +60 -0
  121. package/agent-pack/assets/skill/report-writing/SKILL.md +103 -0
  122. package/agent-pack/assets/skill/resource-dispatch-intelligence/SKILL.md +125 -0
  123. package/agent-pack/assets/skill/standard-reference/SKILL.md +101 -0
  124. package/agent-pack/assets/skill/weekly-work-intelligence/SKILL.md +123 -0
  125. package/agent-pack/assets/template/adjustment-trend.json +53 -0
  126. package/agent-pack/assets/template/bid-technical-proposal.json +42 -0
  127. package/agent-pack/assets/template/compliance-review.json +35 -0
  128. package/agent-pack/assets/template/daily-monitor-report.json +34 -0
  129. package/agent-pack/assets/template/field-data-qa.json +47 -0
  130. package/agent-pack/assets/template/monitoring-plan.json +28 -0
  131. package/agent-pack/assets/template/monthly-monitor-report.json +47 -0
  132. package/agent-pack/assets/template/project-ppt.json +47 -0
  133. package/agent-pack/assets/template/review-response.json +28 -0
  134. package/agent-pack/assets/template/schema.json +72 -0
  135. package/agent-pack/assets/template/weekly-monitor-report.json +33 -0
  136. package/agent-pack/assets/theme/mytheme.json +223 -0
  137. package/agent-pack/assets/tool/angle_convert.ts +151 -0
  138. package/agent-pack/assets/tool/axial_force.ts +186 -0
  139. package/agent-pack/assets/tool/chart_generator.ts +185 -0
  140. package/agent-pack/assets/tool/control_network.ts +413 -0
  141. package/agent-pack/assets/tool/coord_transform.ts +333 -0
  142. package/agent-pack/assets/tool/cpiii_adjustment.ts +446 -0
  143. package/agent-pack/assets/tool/cross_section.ts +356 -0
  144. package/agent-pack/assets/tool/deformation_rate.ts +234 -0
  145. package/agent-pack/assets/tool/distance_calculator.ts +239 -0
  146. package/agent-pack/assets/tool/excel_export.ts +494 -0
  147. package/agent-pack/assets/tool/format_parser.ts +181 -0
  148. package/agent-pack/assets/tool/github-pr-search.ts +57 -0
  149. package/agent-pack/assets/tool/github-pr-search.txt +10 -0
  150. package/agent-pack/assets/tool/github-triage.ts +113 -0
  151. package/agent-pack/assets/tool/github-triage.txt +6 -0
  152. package/agent-pack/assets/tool/inclinometer.ts +248 -0
  153. package/agent-pack/assets/tool/monitoring_csv.ts +120 -0
  154. package/agent-pack/assets/tool/os_contract_query.ts +63 -0
  155. package/agent-pack/assets/tool/os_dibao_project_files.ts +75 -0
  156. package/agent-pack/assets/tool/os_file_download_ref.ts +48 -0
  157. package/agent-pack/assets/tool/os_file_preview.ts +46 -0
  158. package/agent-pack/assets/tool/os_gbrain_think.ts +31 -0
  159. package/agent-pack/assets/tool/os_ppt_generate.ts +37 -0
  160. package/agent-pack/assets/tool/os_project_context.ts +24 -0
  161. package/agent-pack/assets/tool/os_report_publish.ts +28 -0
  162. package/agent-pack/assets/tool/os_wiki_ingest.ts +38 -0
  163. package/agent-pack/assets/tool/os_wiki_query.ts +26 -0
  164. package/agent-pack/assets/tool/pile_stakeout.ts +299 -0
  165. package/agent-pack/assets/tool/report_export.ts +279 -0
  166. package/agent-pack/assets/tool/shield_guidance.ts +311 -0
  167. package/agent-pack/assets/tool/standard_query.ts +391 -0
  168. package/agent-pack/assets/tool/survey_calculator.ts +415 -0
  169. package/agent-pack/assets/tool/water_level.ts +209 -0
  170. package/agent-pack/bin/install.js +183 -0
  171. package/agent-pack/package.json +695 -0
  172. package/package.json +14 -8
  173. package/postinstall.mjs +53 -22
@@ -0,0 +1,494 @@
1
+ /// <reference path="../env.d.ts" />
2
+ import { tool } from "nb-railwise/tool"
3
+ import { deflateRawSync } from "node:zlib"
4
+
5
+ // XLSX = ZIP of XML files (Office Open XML SpreadsheetML)
6
+ // Reuse the minimal ZIP builder from report_export.ts
7
+
8
+ function crc32(buf: Uint8Array) {
9
+ let c = 0xffffffff
10
+ for (let i = 0; i < buf.length; i++) {
11
+ c ^= buf[i]!
12
+ for (let j = 0; j < 8; j++) c = (c >>> 1) ^ (c & 1 ? 0xedb88320 : 0)
13
+ }
14
+ return (c ^ 0xffffffff) >>> 0
15
+ }
16
+
17
+ function zip(files: Array<{ name: string; data: Uint8Array }>) {
18
+ const entries: Array<{
19
+ name: Uint8Array
20
+ compressed: Uint8Array
21
+ crc: number
22
+ size: number
23
+ csize: number
24
+ offset: number
25
+ }> = []
26
+ const parts: Uint8Array[] = []
27
+ let offset = 0
28
+
29
+ for (const f of files) {
30
+ const nameBytes = new TextEncoder().encode(f.name)
31
+ const crc = crc32(f.data)
32
+ const compressed = new Uint8Array(deflateRawSync(f.data))
33
+ const header = new Uint8Array(30 + nameBytes.length)
34
+ const v = new DataView(header.buffer)
35
+ v.setUint32(0, 0x04034b50, true)
36
+ v.setUint16(4, 20, true)
37
+ v.setUint16(8, 8, true)
38
+ v.setUint32(14, crc, true)
39
+ v.setUint32(18, compressed.length, true)
40
+ v.setUint32(22, f.data.length, true)
41
+ v.setUint16(26, nameBytes.length, true)
42
+ header.set(nameBytes, 30)
43
+
44
+ entries.push({ name: nameBytes, compressed, crc, size: f.data.length, csize: compressed.length, offset })
45
+ parts.push(header, compressed)
46
+ offset += header.length + compressed.length
47
+ }
48
+
49
+ const cdStart = offset
50
+ for (const e of entries) {
51
+ const cd = new Uint8Array(46 + e.name.length)
52
+ const v = new DataView(cd.buffer)
53
+ v.setUint32(0, 0x02014b50, true)
54
+ v.setUint16(4, 20, true)
55
+ v.setUint16(6, 20, true)
56
+ v.setUint16(10, 8, true)
57
+ v.setUint32(16, e.crc, true)
58
+ v.setUint32(20, e.csize, true)
59
+ v.setUint32(24, e.size, true)
60
+ v.setUint16(28, e.name.length, true)
61
+ v.setUint32(42, e.offset, true)
62
+ cd.set(e.name, 46)
63
+ parts.push(cd)
64
+ offset += cd.length
65
+ }
66
+
67
+ const eocd = new Uint8Array(22)
68
+ const ev = new DataView(eocd.buffer)
69
+ ev.setUint32(0, 0x06054b50, true)
70
+ ev.setUint16(8, entries.length, true)
71
+ ev.setUint16(10, entries.length, true)
72
+ ev.setUint32(12, offset - cdStart, true)
73
+ ev.setUint32(16, cdStart, true)
74
+ parts.push(eocd)
75
+
76
+ let total = 0
77
+ for (const p of parts) total += p.length
78
+ const result = new Uint8Array(total)
79
+ let pos = 0
80
+ for (const p of parts) {
81
+ result.set(p, pos)
82
+ pos += p.length
83
+ }
84
+ return result
85
+ }
86
+
87
+ function esc(s: string) {
88
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;")
89
+ }
90
+
91
+ // Excel column letter: 0->A, 1->B, ..., 25->Z, 26->AA
92
+ function colLetter(idx: number): string {
93
+ let s = ""
94
+ let n = idx
95
+ while (n >= 0) {
96
+ s = String.fromCharCode(65 + (n % 26)) + s
97
+ n = Math.floor(n / 26) - 1
98
+ }
99
+ return s
100
+ }
101
+
102
+ type CellValue = string | number | boolean | null | undefined
103
+ type SheetData = {
104
+ name: string
105
+ headers: string[]
106
+ rows: CellValue[][]
107
+ columnWidths?: number[]
108
+ freezeRow?: number
109
+ }
110
+
111
+ function buildSharedStrings(sheets: SheetData[]): { xml: string; lookup: Map<string, number> } {
112
+ const lookup = new Map<string, number>()
113
+ let idx = 0
114
+
115
+ for (const sheet of sheets) {
116
+ for (const h of sheet.headers) {
117
+ if (!lookup.has(h)) lookup.set(h, idx++)
118
+ }
119
+ for (const row of sheet.rows) {
120
+ for (const cell of row) {
121
+ if (typeof cell === "string" && !lookup.has(cell)) lookup.set(cell, idx++)
122
+ }
123
+ }
124
+ }
125
+
126
+ const items = Array.from(lookup.entries())
127
+ .sort((a, b) => a[1] - b[1])
128
+ .map(([s]) => `<si><t>${esc(s)}</t></si>`)
129
+ .join("")
130
+
131
+ return {
132
+ xml: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
133
+ <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${lookup.size}" uniqueCount="${lookup.size}">${items}</sst>`,
134
+ lookup,
135
+ }
136
+ }
137
+
138
+ function buildSheet(sheet: SheetData, strings: Map<string, number>): string {
139
+ const cols = sheet.headers.length
140
+ const lastCol = colLetter(cols - 1)
141
+ const lastRow = sheet.rows.length + 1
142
+
143
+ // Column widths
144
+ let colsXml = ""
145
+ if (sheet.columnWidths && sheet.columnWidths.length > 0) {
146
+ const colDefs = sheet.columnWidths
147
+ .map((w, i) => `<col min="${i + 1}" max="${i + 1}" width="${w}" customWidth="1"/>`)
148
+ .join("")
149
+ colsXml = `<cols>${colDefs}</cols>`
150
+ }
151
+
152
+ // Header row
153
+ const headerCells = sheet.headers
154
+ .map((h, c) => {
155
+ const ref = `${colLetter(c)}1`
156
+ const si = strings.get(h) ?? 0
157
+ return `<c r="${ref}" t="s" s="1"><v>${si}</v></c>`
158
+ })
159
+ .join("")
160
+ const headerRow = `<row r="1">${headerCells}</row>`
161
+
162
+ // Data rows
163
+ const dataRows = sheet.rows
164
+ .map((row, ri) => {
165
+ const r = ri + 2
166
+ const cells = row
167
+ .map((cell, ci) => {
168
+ const ref = `${colLetter(ci)}${r}`
169
+ if (cell === null || cell === undefined) return `<c r="${ref}"/>`
170
+ if (typeof cell === "number") return `<c r="${ref}" s="2"><v>${cell}</v></c>`
171
+ if (typeof cell === "boolean") return `<c r="${ref}"><v>${cell ? 1 : 0}</v></c>`
172
+ const si = strings.get(cell) ?? 0
173
+ return `<c r="${ref}" t="s"><v>${si}</v></c>`
174
+ })
175
+ .join("")
176
+ return `<row r="${r}">${cells}</row>`
177
+ })
178
+ .join("\n")
179
+
180
+ // Freeze pane (freeze header row by default)
181
+ const freezeRow = sheet.freezeRow ?? 1
182
+ const pane =
183
+ freezeRow > 0
184
+ ? `<pane ySplit="${freezeRow}" topLeftCell="A${freezeRow + 1}" activePane="bottomLeft" state="frozen"/>`
185
+ : ""
186
+
187
+ // AutoFilter on header row
188
+ const autoFilter = `<autoFilter ref="A1:${lastCol}${lastRow}"/>`
189
+
190
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
191
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
192
+ xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
193
+ ${colsXml}
194
+ <sheetViews><sheetView tabSelected="1" workbookViewId="0">${pane}</sheetView></sheetViews>
195
+ <sheetData>
196
+ ${headerRow}
197
+ ${dataRows}
198
+ </sheetData>
199
+ ${autoFilter}
200
+ </worksheet>`
201
+ }
202
+
203
+ function buildStyles(): string {
204
+ // s="0" = default, s="1" = header (bold, light blue bg), s="2" = number (2 decimals)
205
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
206
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
207
+ <numFmts count="1">
208
+ <numFmt numFmtId="164" formatCode="0.000"/>
209
+ </numFmts>
210
+ <fonts count="2">
211
+ <font><sz val="11"/><name val="等线"/></font>
212
+ <font><b/><sz val="11"/><name val="等线"/></font>
213
+ </fonts>
214
+ <fills count="3">
215
+ <fill><patternFill patternType="none"/></fill>
216
+ <fill><patternFill patternType="gray125"/></fill>
217
+ <fill><patternFill patternType="solid"><fgColor rgb="FFD9E2F3"/></patternFill></fill>
218
+ </fills>
219
+ <borders count="2">
220
+ <border/>
221
+ <border>
222
+ <left style="thin"><color auto="1"/></left>
223
+ <right style="thin"><color auto="1"/></right>
224
+ <top style="thin"><color auto="1"/></top>
225
+ <bottom style="thin"><color auto="1"/></bottom>
226
+ </border>
227
+ </borders>
228
+ <cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>
229
+ <cellXfs count="3">
230
+ <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
231
+ <xf numFmtId="0" fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1" applyAlignment="1">
232
+ <alignment horizontal="center" vertical="center" wrapText="1"/>
233
+ </xf>
234
+ <xf numFmtId="164" fontId="0" fillId="0" borderId="1" applyNumberFormat="1" applyBorder="1"/>
235
+ </cellXfs>
236
+ </styleSheet>`
237
+ }
238
+
239
+ function buildXlsx(sheets: SheetData[]): Uint8Array {
240
+ const { xml: sst, lookup } = buildSharedStrings(sheets)
241
+
242
+ const sheetXmls = sheets.map((s) => buildSheet(s, lookup))
243
+
244
+ const sheetRefs = sheets
245
+ .map((s, i) => `<sheet name="${esc(s.name)}" sheetId="${i + 1}" r:id="rId${i + 1}"/>`)
246
+ .join("")
247
+
248
+ const workbook = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
249
+ <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
250
+ xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
251
+ <sheets>${sheetRefs}</sheets>
252
+ </workbook>`
253
+
254
+ const sheetRelEntries = sheets
255
+ .map(
256
+ (_, i) =>
257
+ `<Relationship Id="rId${i + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${i + 1}.xml"/>`,
258
+ )
259
+ .join("")
260
+
261
+ const wbRels = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
262
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
263
+ ${sheetRelEntries}
264
+ <Relationship Id="rId${sheets.length + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
265
+ <Relationship Id="rId${sheets.length + 2}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
266
+ </Relationships>`
267
+
268
+ const rels = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
269
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
270
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
271
+ </Relationships>`
272
+
273
+ const sheetOverrides = sheets
274
+ .map(
275
+ (_, i) =>
276
+ `<Override PartName="/xl/worksheets/sheet${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`,
277
+ )
278
+ .join("\n ")
279
+
280
+ const contentTypes = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
281
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
282
+ <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
283
+ <Default Extension="xml" ContentType="application/xml"/>
284
+ <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
285
+ ${sheetOverrides}
286
+ <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
287
+ <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
288
+ </Types>`
289
+
290
+ const enc = (s: string) => new TextEncoder().encode(s)
291
+
292
+ const files: Array<{ name: string; data: Uint8Array }> = [
293
+ { name: "[Content_Types].xml", data: enc(contentTypes) },
294
+ { name: "_rels/.rels", data: enc(rels) },
295
+ { name: "xl/workbook.xml", data: enc(workbook) },
296
+ { name: "xl/_rels/workbook.xml.rels", data: enc(wbRels) },
297
+ { name: "xl/styles.xml", data: enc(buildStyles()) },
298
+ { name: "xl/sharedStrings.xml", data: enc(sst) },
299
+ ]
300
+
301
+ for (let i = 0; i < sheetXmls.length; i++) {
302
+ files.push({ name: `xl/worksheets/sheet${i + 1}.xml`, data: enc(sheetXmls[i]!) })
303
+ }
304
+
305
+ return zip(files)
306
+ }
307
+
308
+ export const excel_export = tool({
309
+ description:
310
+ "将监测数据导出为 .xlsx(Excel)文件。支持多 Sheet、表头冻结、自动筛选、数值格式化。用于导出基坑监测日报/周报数据表、变形汇总表、轴力统计表等。data_analyst 或 technical_writer 需要输出 Excel 报表时必须调用此工具。",
311
+ args: {
312
+ sheets: tool.schema
313
+ .array(
314
+ tool.schema.object({
315
+ name: tool.schema.string().describe("Sheet 名称,如 '沉降监测' '深层位移' '轴力统计'"),
316
+ headers: tool.schema.array(tool.schema.string()).min(1).describe("列标题"),
317
+ rows: tool.schema
318
+ .array(
319
+ tool.schema.array(
320
+ tool.schema.union([
321
+ tool.schema.string(),
322
+ tool.schema.number(),
323
+ tool.schema.boolean(),
324
+ tool.schema.null(),
325
+ ]),
326
+ ),
327
+ )
328
+ .describe("数据行,每行元素数量与 headers 一致"),
329
+ columnWidths: tool.schema
330
+ .array(tool.schema.number().positive())
331
+ .optional()
332
+ .describe("各列宽度(字符数),不传则自动计算"),
333
+ freezeRow: tool.schema.number().int().default(1).describe("冻结前N行,默认1(冻结表头)"),
334
+ }),
335
+ )
336
+ .min(1)
337
+ .describe("工作表列表,支持多个 Sheet"),
338
+ title: tool.schema.string().default("监测数据").describe("文件名(不含扩展名)"),
339
+ outputPath: tool.schema.string().optional().describe("输出路径,默认 ./[title].xlsx"),
340
+ },
341
+ async execute(args) {
342
+ const sheetsData: SheetData[] = args.sheets.map((s) => {
343
+ const widths =
344
+ s.columnWidths ??
345
+ s.headers.map((h, i) => {
346
+ const headerLen = [...h].length + 4
347
+ const maxDataLen = s.rows.reduce((max, row) => {
348
+ const cell = row[i]
349
+ const len = cell === null || cell === undefined ? 0 : String(cell).length
350
+ return Math.max(max, len)
351
+ }, 0)
352
+ return Math.min(Math.max(headerLen, maxDataLen + 2), 50)
353
+ })
354
+
355
+ return {
356
+ name: s.name,
357
+ headers: s.headers,
358
+ rows: s.rows,
359
+ columnWidths: widths,
360
+ freezeRow: s.freezeRow,
361
+ }
362
+ })
363
+
364
+ const xlsxBytes = buildXlsx(sheetsData)
365
+ const dest = args.outputPath ?? `./${args.title.replace(/[/\\:*?"<>|]/g, "_")}.xlsx`
366
+ await Bun.write(dest, xlsxBytes)
367
+
368
+ const totalRows = args.sheets.reduce((s, sh) => s + sh.rows.length, 0)
369
+ const totalCols = args.sheets.reduce((s, sh) => s + sh.headers.length, 0)
370
+
371
+ return JSON.stringify({
372
+ output_path: dest,
373
+ file_size_kb: Number((xlsxBytes.length / 1024).toFixed(1)),
374
+ format: "xlsx (Office Open XML SpreadsheetML)",
375
+ sheets: args.sheets.map((s) => ({
376
+ name: s.name,
377
+ columns: s.headers.length,
378
+ rows: s.rows.length,
379
+ })),
380
+ total_rows: totalRows,
381
+ total_columns: totalCols,
382
+ message: `✅ Excel 报表已导出:${dest}(${(xlsxBytes.length / 1024).toFixed(1)} KB),${args.sheets.length}个工作表,共 ${totalRows} 行数据`,
383
+ })
384
+ },
385
+ })
386
+
387
+ export const monitoring_table_export = tool({
388
+ description:
389
+ "快速导出标准格式的监测数据汇总表。直接输入测点数据,自动生成带预警标识的规范化 Excel 报表。适用于沉降、位移、轴力、水位等各类监测项目的数据报表导出。",
390
+ args: {
391
+ projectName: tool.schema.string().describe("项目名称"),
392
+ monitoringType: tool.schema
393
+ .enum(["settlement", "displacement", "axial_force", "water_level", "inclinometer", "convergence"])
394
+ .describe("监测类型"),
395
+ date: tool.schema.string().describe("报表日期 YYYY-MM-DD"),
396
+ points: tool.schema
397
+ .array(
398
+ tool.schema.object({
399
+ id: tool.schema.string().describe("测点编号"),
400
+ section: tool.schema.string().optional().describe("所属断面"),
401
+ initialValue: tool.schema.number().optional().describe("初始值"),
402
+ previousValue: tool.schema.number().optional().describe("上期值"),
403
+ currentValue: tool.schema.number().describe("本期值"),
404
+ cumulativeChange: tool.schema.number().describe("累计变化量"),
405
+ periodChange: tool.schema.number().optional().describe("本期变化量"),
406
+ rate: tool.schema.number().optional().describe("变化速率(/d)"),
407
+ }),
408
+ )
409
+ .min(1)
410
+ .describe("各测点数据"),
411
+ alertThreshold: tool.schema.number().positive().optional().describe("报警控制值"),
412
+ unit: tool.schema.string().default("mm").describe("单位"),
413
+ outputPath: tool.schema.string().optional().describe("输出路径"),
414
+ },
415
+ async execute(args) {
416
+ const typeLabels: Record<string, string> = {
417
+ settlement: "沉降监测",
418
+ displacement: "水平位移",
419
+ axial_force: "轴力监测",
420
+ water_level: "水位监测",
421
+ inclinometer: "深层水平位移",
422
+ convergence: "收敛监测",
423
+ }
424
+ const typeLabel = typeLabels[args.monitoringType] ?? args.monitoringType
425
+
426
+ const headers = [
427
+ "测点编号",
428
+ ...(args.points.some((p) => p.section) ? ["所属断面"] : []),
429
+ ...(args.points.some((p) => p.initialValue !== undefined) ? [`初始值(${args.unit})`] : []),
430
+ ...(args.points.some((p) => p.previousValue !== undefined) ? [`上期值(${args.unit})`] : []),
431
+ `本期值(${args.unit})`,
432
+ `累计变化量(${args.unit})`,
433
+ ...(args.points.some((p) => p.periodChange !== undefined) ? [`本期变化量(${args.unit})`] : []),
434
+ ...(args.points.some((p) => p.rate !== undefined) ? [`变化速率(${args.unit}/d)`] : []),
435
+ ...(args.alertThreshold ? [`控制值(${args.unit})`, "占控制值(%)", "预警状态"] : []),
436
+ ]
437
+
438
+ const rows: CellValue[][] = args.points.map((p) => {
439
+ const ratio = args.alertThreshold ? Math.abs(p.cumulativeChange) / args.alertThreshold : 0
440
+ let alertStatus = ""
441
+ if (args.alertThreshold) {
442
+ if (ratio >= 1.0) alertStatus = "超限"
443
+ else if (ratio >= 0.85) alertStatus = "橙色预警"
444
+ else if (ratio >= 0.7) alertStatus = "黄色预警"
445
+ else alertStatus = "正常"
446
+ }
447
+
448
+ return [
449
+ p.id,
450
+ ...(args.points.some((pt) => pt.section) ? [p.section ?? ""] : []),
451
+ ...(args.points.some((pt) => pt.initialValue !== undefined) ? [p.initialValue ?? null] : []),
452
+ ...(args.points.some((pt) => pt.previousValue !== undefined) ? [p.previousValue ?? null] : []),
453
+ p.currentValue,
454
+ p.cumulativeChange,
455
+ ...(args.points.some((pt) => pt.periodChange !== undefined) ? [p.periodChange ?? null] : []),
456
+ ...(args.points.some((pt) => pt.rate !== undefined) ? [p.rate ?? null] : []),
457
+ ...(args.alertThreshold ? [args.alertThreshold, Number((ratio * 100).toFixed(1)), alertStatus] : []),
458
+ ]
459
+ })
460
+
461
+ // Summary row
462
+ const cumulativeValues = args.points.map((p) => Math.abs(p.cumulativeChange))
463
+ const maxIdx = cumulativeValues.indexOf(Math.max(...cumulativeValues))
464
+ const avgCumulative = cumulativeValues.reduce((s, v) => s + v, 0) / cumulativeValues.length
465
+ const alertCount = args.alertThreshold
466
+ ? args.points.filter((p) => Math.abs(p.cumulativeChange) >= args.alertThreshold! * 0.7).length
467
+ : 0
468
+
469
+ const sheetData: SheetData = {
470
+ name: typeLabel,
471
+ headers,
472
+ rows,
473
+ freezeRow: 1,
474
+ }
475
+
476
+ const xlsxBytes = buildXlsx([sheetData])
477
+ const filename = `${args.projectName}_${typeLabel}_${args.date}`
478
+ const dest = args.outputPath ?? `./${filename.replace(/[/\\:*?"<>|]/g, "_")}.xlsx`
479
+ await Bun.write(dest, xlsxBytes)
480
+
481
+ return JSON.stringify({
482
+ output_path: dest,
483
+ file_size_kb: Number((xlsxBytes.length / 1024).toFixed(1)),
484
+ project: args.projectName,
485
+ type: typeLabel,
486
+ date: args.date,
487
+ point_count: args.points.length,
488
+ max_point: { id: args.points[maxIdx]!.id, value: args.points[maxIdx]!.cumulativeChange },
489
+ avg_cumulative: Number(avgCumulative.toFixed(3)),
490
+ alert_count: alertCount,
491
+ message: `✅ ${typeLabel}报表已导出:${dest},${args.points.length}个测点,最大变化 ${args.points[maxIdx]!.id}(${args.points[maxIdx]!.cumulativeChange}${args.unit})${alertCount > 0 ? `,${alertCount}个测点预警` : ""}`,
492
+ })
493
+ },
494
+ })
@@ -0,0 +1,181 @@
1
+ /// <reference path="../env.d.ts" />
2
+ import { tool } from "nb-railwise/tool"
3
+ import path from "path"
4
+
5
+ const FIELDS: Record<number, string> = {
6
+ 11: "point_id",
7
+ 21: "hz_angle_deg",
8
+ 22: "v_angle_deg",
9
+ 31: "slope_dist_m",
10
+ 32: "horiz_dist_m",
11
+ 33: "height_diff_m",
12
+ 81: "easting_m",
13
+ 82: "northing_m",
14
+ 83: "elevation_m",
15
+ 87: "reflector_height_m",
16
+ 88: "instrument_height_m",
17
+ }
18
+
19
+ const ANGLES = new Set([21, 22])
20
+ const METRIC = new Set([31, 32, 33, 81, 82, 83, 87, 88])
21
+
22
+ const PATTERNS: [RegExp, string][] = [
23
+ [/^(point|id|name|pt|nr|编号|测点|点号)$/i, "point_id"],
24
+ [/^(hz|h_angle|horizontal_angle|水平角|ha|hz_angle)$/i, "hz_angle_deg"],
25
+ [/^(v_angle|vertical|zenith|天顶角|竖直角|va)$/i, "v_angle_deg"],
26
+ [/^(slope|sd|slope_dist|斜距|slope_distance)$/i, "slope_dist_m"],
27
+ [/^(hd|horiz_dist|horizontal_dist|平距|水平距)$/i, "horiz_dist_m"],
28
+ [/^(dh|height_diff|高差)$/i, "height_diff_m"],
29
+ [/^(east|easting|e_coord|东坐标)$/i, "easting_m"],
30
+ [/^(north|northing|n_coord|北坐标)$/i, "northing_m"],
31
+ [/^(elev|elevation|height|h_coord|高程|高度)$/i, "elevation_m"],
32
+ [/^(rh|reflector|target_height|棱镜高|觇标高)$/i, "reflector_height_m"],
33
+ [/^(ih|instrument_height|仪器高)$/i, "instrument_height_m"],
34
+ ]
35
+
36
+ function dms(data: string, wide: boolean) {
37
+ const deg = parseInt(data.slice(0, 3), 10)
38
+ const min = parseInt(data.slice(3, 5), 10)
39
+ const sec = wide
40
+ ? parseInt(data.slice(5, 7), 10) + parseInt(data.slice(7), 10) / Math.pow(10, data.length - 7)
41
+ : parseInt(data.slice(5, 7), 10) + parseInt(data.slice(7), 10) / 10
42
+ return deg + min / 60 + sec / 3600
43
+ }
44
+
45
+ function word(raw: string, wide: boolean) {
46
+ const clean = raw.replace(/^[*+]+/, "")
47
+ const m = clean.match(/^(\d{2})([^+-]+)([+-])(.+)$/)
48
+ if (!m) return null
49
+
50
+ const wi = parseInt(m[1]!, 10)
51
+ const name = FIELDS[wi]
52
+ if (!name) return null
53
+
54
+ if (wi === 11) return { name, value: m[4]!.replace(/^[0\s]+/, "") || "0" }
55
+
56
+ const sign = m[3] === "-" ? -1 : 1
57
+ if (ANGLES.has(wi)) return { name, value: Number((sign * dms(m[4]!, wide)).toFixed(6)) }
58
+ if (METRIC.has(wi)) return { name, value: Number(((sign * parseInt(m[4]!, 10)) / (wide ? 10000 : 1000)).toFixed(4)) }
59
+ return null
60
+ }
61
+
62
+ function gsi(content: string, wide: boolean) {
63
+ const lines = content
64
+ .split("\n")
65
+ .map((l) => l.trim())
66
+ .filter((l) => l.length > 0)
67
+ if (lines.length === 0) return null
68
+
69
+ const starred = lines.some((l) => l.startsWith("*"))
70
+ const groups = starred
71
+ ? lines.reduce<string[][]>((acc, line) => {
72
+ if (line.startsWith("*")) {
73
+ acc.push([line])
74
+ return acc
75
+ }
76
+ const last = acc[acc.length - 1]
77
+ if (last) last.push(line)
78
+ return acc
79
+ }, [])
80
+ : lines.map((l) => [l])
81
+
82
+ const records = groups
83
+ .map((group) =>
84
+ group
85
+ .flatMap((line) => line.split(/\s+/))
86
+ .reduce<Record<string, string | number>>((obs, w) => {
87
+ const parsed = word(w, wide)
88
+ if (parsed) obs[parsed.name] = parsed.value
89
+ return obs
90
+ }, {}),
91
+ )
92
+ .filter((obs) => Object.keys(obs).length > 0)
93
+
94
+ if (records.length === 0) return null
95
+ return { format: wide ? "gsi-16" : "gsi-8", records }
96
+ }
97
+
98
+ function dat(content: string) {
99
+ const lines = content
100
+ .split("\n")
101
+ .map((l) => l.trim())
102
+ .filter((l) => l.length > 0 && !l.startsWith("#") && !l.startsWith("!"))
103
+ if (lines.length < 2) return null
104
+
105
+ const first = lines[0]!
106
+ const delim = first.includes("\t") ? /\t+/ : first.includes(",") ? /,\s*/ : first.includes(";") ? /;\s*/ : /\s+/
107
+ const cells = first.split(delim).map((c) => c.trim())
108
+ if (!cells.some((c) => /[a-zA-Z\u4e00-\u9fff]/.test(c))) return null
109
+
110
+ const mapping = cells.map((h) => PATTERNS.find(([re]) => re.test(h.trim()))?.[1] ?? null)
111
+ if (!mapping.some(Boolean)) return null
112
+
113
+ const records = lines
114
+ .slice(1)
115
+ .map((line) =>
116
+ line
117
+ .split(delim)
118
+ .map((c) => c.trim())
119
+ .reduce<Record<string, string | number>>((obs, val, i) => {
120
+ const col = mapping[i]
121
+ if (!col || !val) return obs
122
+ if (col === "point_id") {
123
+ obs[col] = val
124
+ return obs
125
+ }
126
+ const num = parseFloat(val)
127
+ if (!isNaN(num)) obs[col] = num
128
+ return obs
129
+ }, {}),
130
+ )
131
+ .filter((obs) => Object.keys(obs).length > 0)
132
+
133
+ if (records.length === 0) return null
134
+ return { format: "dat", records }
135
+ }
136
+
137
+ function detect(content: string) {
138
+ const lines = content
139
+ .split("\n")
140
+ .map((l) => l.trim())
141
+ .filter((l) => l.length > 0)
142
+ if (lines.length === 0) return null
143
+
144
+ const probe = lines[0]!.replace(/^\*/, "").trim().split(/\s+/)[0] ?? ""
145
+ const m = probe.match(/^(\d{2})([^+-]+)([+-])(.+)$/)
146
+ if (m) return gsi(content, m[4]!.length > 8)
147
+ return dat(content)
148
+ }
149
+
150
+ export default tool({
151
+ description:
152
+ "解析徕卡全站仪 GSI-8/GSI-16 格式及通用 DAT 文本格式的外业观测文件,将仪器原始数据转换为结构化 JSON,供平差计算和报告生成使用。",
153
+ args: {
154
+ filePath: tool.schema.string().describe("外业数据文件的绝对路径"),
155
+ format: tool.schema
156
+ .enum(["gsi-8", "gsi-16", "dat-auto"])
157
+ .describe("文件格式:gsi-8=Leica GSI-8, gsi-16=Leica GSI-16, dat-auto=自动检测(优先尝试 GSI,其次 DAT 表格)"),
158
+ },
159
+ async execute(args) {
160
+ const file = Bun.file(args.filePath)
161
+ const exists = await file.exists()
162
+ if (!exists) return JSON.stringify({ error: `文件不存在:${args.filePath}` })
163
+
164
+ const raw = await file.text()
165
+ if (raw.trim().length === 0) return JSON.stringify({ error: "文件内容为空" })
166
+
167
+ const result = args.format === "gsi-8" ? gsi(raw, false) : args.format === "gsi-16" ? gsi(raw, true) : detect(raw)
168
+
169
+ if (!result)
170
+ return JSON.stringify({
171
+ error: "无法识别文件格式,请确认为 GSI 或 DAT 表格格式,或手动指定 format 参数。",
172
+ })
173
+
174
+ return JSON.stringify({
175
+ format: result.format,
176
+ file: path.basename(args.filePath),
177
+ total_records: result.records.length,
178
+ records: result.records,
179
+ })
180
+ },
181
+ })