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,57 @@
1
+ /// <reference path="../env.d.ts" />
2
+ import { tool } from "nb-railwise/tool"
3
+ import DESCRIPTION from "./github-pr-search.txt"
4
+
5
+ async function githubFetch(endpoint: string, options: RequestInit = {}) {
6
+ const response = await fetch(`https://api.github.com${endpoint}`, {
7
+ ...options,
8
+ headers: {
9
+ Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
10
+ Accept: "application/vnd.github+json",
11
+ "Content-Type": "application/json",
12
+ ...options.headers,
13
+ },
14
+ })
15
+ if (!response.ok) {
16
+ throw new Error(`GitHub API error: ${response.status} ${response.statusText}`)
17
+ }
18
+ return response.json()
19
+ }
20
+
21
+ interface PR {
22
+ title: string
23
+ html_url: string
24
+ }
25
+
26
+ export default tool({
27
+ description: DESCRIPTION,
28
+ args: {
29
+ query: tool.schema.string().describe("Search query for PR titles and descriptions"),
30
+ limit: tool.schema.number().describe("Maximum number of results to return").default(10),
31
+ offset: tool.schema.number().describe("Number of results to skip for pagination").default(0),
32
+ },
33
+ async execute(args) {
34
+ const owner = "anomalyco"
35
+ const repo = "railwise"
36
+
37
+ const page = Math.floor(args.offset / args.limit) + 1
38
+ const searchQuery = encodeURIComponent(`${args.query} repo:${owner}/${repo} type:pr state:open`)
39
+ const result = await githubFetch(
40
+ `/search/issues?q=${searchQuery}&per_page=${args.limit}&page=${page}&sort=updated&order=desc`,
41
+ )
42
+
43
+ if (result.total_count === 0) {
44
+ return `No PRs found matching "${args.query}"`
45
+ }
46
+
47
+ const prs = result.items as PR[]
48
+
49
+ if (prs.length === 0) {
50
+ return `No other PRs found matching "${args.query}"`
51
+ }
52
+
53
+ const formatted = prs.map((pr) => `${pr.title}\n${pr.html_url}`).join("\n\n")
54
+
55
+ return `Found ${result.total_count} PRs (showing ${prs.length}):\n\n${formatted}`
56
+ },
57
+ })
@@ -0,0 +1,10 @@
1
+ Use this tool to search GitHub pull requests by title and description.
2
+
3
+ This tool searches PRs in the sst/railwise repository and returns LLM-friendly results including:
4
+ - PR number and title
5
+ - Author
6
+ - State (open/closed/merged)
7
+ - Labels
8
+ - Description snippet
9
+
10
+ Use the query parameter to search for keywords that might appear in PR titles or descriptions.
@@ -0,0 +1,113 @@
1
+ /// <reference path="../env.d.ts" />
2
+ import { tool } from "nb-railwise/tool"
3
+ import DESCRIPTION from "./github-triage.txt"
4
+
5
+ const TEAM = {
6
+ desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"],
7
+ zen: ["fwang", "MrMushrooooom"],
8
+ tui: ["thdxr", "kommander", "rekram1-node"],
9
+ core: ["thdxr", "rekram1-node", "jlongster"],
10
+ docs: ["R44VC0RP"],
11
+ windows: ["Hona"],
12
+ } as const
13
+
14
+ const ASSIGNEES = [...new Set(Object.values(TEAM).flat())]
15
+
16
+ function pick<T>(items: readonly T[]) {
17
+ return items[Math.floor(Math.random() * items.length)]!
18
+ }
19
+
20
+ function getIssueNumber(): number {
21
+ const issue = parseInt(process.env.ISSUE_NUMBER ?? "", 10)
22
+ if (!issue) throw new Error("ISSUE_NUMBER env var not set")
23
+ return issue
24
+ }
25
+
26
+ async function githubFetch(endpoint: string, options: RequestInit = {}) {
27
+ const response = await fetch(`https://api.github.com${endpoint}`, {
28
+ ...options,
29
+ headers: {
30
+ Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
31
+ Accept: "application/vnd.github+json",
32
+ "Content-Type": "application/json",
33
+ ...options.headers,
34
+ },
35
+ })
36
+ if (!response.ok) {
37
+ throw new Error(`GitHub API error: ${response.status} ${response.statusText}`)
38
+ }
39
+ return response.json()
40
+ }
41
+
42
+ export default tool({
43
+ description: DESCRIPTION,
44
+ args: {
45
+ assignee: tool.schema
46
+ .enum(ASSIGNEES as [string, ...string[]])
47
+ .describe("The username of the assignee")
48
+ .default("rekram1-node"),
49
+ labels: tool.schema
50
+ .array(tool.schema.enum(["nix", "opentui", "perf", "web", "desktop", "zen", "docs", "windows", "core"]))
51
+ .describe("The labels(s) to add to the issue")
52
+ .default([]),
53
+ },
54
+ async execute(args) {
55
+ const issue = getIssueNumber()
56
+ const owner = "anomalyco"
57
+ const repo = "railwise"
58
+
59
+ const results: string[] = []
60
+ let labels = [...new Set(args.labels.map((x) => (x === "desktop" ? "web" : x)))]
61
+ const web = labels.includes("web")
62
+ const text = `${process.env.ISSUE_TITLE ?? ""}\n${process.env.ISSUE_BODY ?? ""}`.toLowerCase()
63
+ const zen = /\bzen\b/.test(text) || text.includes("railwise black")
64
+ const nix = /\bnix(os)?\b/.test(text)
65
+
66
+ if (labels.includes("nix") && !nix) {
67
+ labels = labels.filter((x) => x !== "nix")
68
+ results.push("Dropped label: nix (issue does not mention nix)")
69
+ }
70
+
71
+ const assignee = nix ? "rekram1-node" : web ? pick(TEAM.desktop) : args.assignee
72
+
73
+ if (labels.includes("zen") && !zen) {
74
+ throw new Error("Only add the zen label when issue title/body contains 'zen'")
75
+ }
76
+
77
+ if (web && !nix && !(TEAM.desktop as readonly string[]).includes(assignee)) {
78
+ throw new Error("Web issues must be assigned to adamdotdevin, iamdavidhill, Brendonovich, or nexxeln")
79
+ }
80
+
81
+ if ((TEAM.zen as readonly string[]).includes(assignee) && !labels.includes("zen")) {
82
+ throw new Error("Only zen issues should be assigned to fwang or MrMushrooooom")
83
+ }
84
+
85
+ if (assignee === "Hona" && !labels.includes("windows")) {
86
+ throw new Error("Only windows issues should be assigned to Hona")
87
+ }
88
+
89
+ if (assignee === "R44VC0RP" && !labels.includes("docs")) {
90
+ throw new Error("Only docs issues should be assigned to R44VC0RP")
91
+ }
92
+
93
+ if (assignee === "kommander" && !labels.includes("opentui")) {
94
+ throw new Error("Only opentui issues should be assigned to kommander")
95
+ }
96
+
97
+ await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, {
98
+ method: "POST",
99
+ body: JSON.stringify({ assignees: [assignee] }),
100
+ })
101
+ results.push(`Assigned @${assignee} to issue #${issue}`)
102
+
103
+ if (labels.length > 0) {
104
+ await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/labels`, {
105
+ method: "POST",
106
+ body: JSON.stringify({ labels }),
107
+ })
108
+ results.push(`Added labels: ${labels.join(", ")}`)
109
+ }
110
+
111
+ return results.join("\n")
112
+ },
113
+ })
@@ -0,0 +1,6 @@
1
+ Use this tool to assign and/or label a GitHub issue.
2
+
3
+ Choose labels and assignee using the current triage policy and ownership rules.
4
+ Pick the most fitting labels for the issue and assign one owner.
5
+
6
+ If unsure, choose the team/section with the most overlap with the issue and assign a member from that team at random.
@@ -0,0 +1,248 @@
1
+ /// <reference path="../env.d.ts" />
2
+ import { tool } from "nb-railwise/tool"
3
+
4
+ // ============================================================
5
+ // Deep horizontal displacement (inclinometer) tools
6
+ // 基坑深层水平位移(测斜仪)监测数据处理与分析
7
+ // ============================================================
8
+
9
+ // ============================================================
10
+ // Tool: Inclinometer profile calculation
11
+ // ============================================================
12
+
13
+ export const inclinometer_profile = tool({
14
+ description:
15
+ "测斜仪深层水平位移剖面计算。根据各深度处的测斜仪读数(A+/A-/B+/B-),计算各深度处的累计水平位移,生成位移-深度剖面。基坑监测中深层水平位移分析的核心工具。data_analyst 处理测斜数据时必须调用此工具。",
16
+ args: {
17
+ pointId: tool.schema.string().describe("测斜孔编号,如 CX-01"),
18
+ gaugeLength: tool.schema.number().positive().default(0.5).describe("测斜仪导轮间距(m),常见为0.5m或1.0m"),
19
+ direction: tool.schema
20
+ .enum(["A", "B", "AB"])
21
+ .default("A")
22
+ .describe("计算方向:A=垂直基坑方向, B=平行基坑方向, AB=双向"),
23
+ baseDepth: tool.schema.number().positive().describe("管底深度(m),即假定不动点深度"),
24
+ initialReadings: tool.schema
25
+ .array(
26
+ tool.schema.object({
27
+ depth: tool.schema.number().describe("测量深度(m),从管口往下"),
28
+ aPlus: tool.schema.number().describe("A+方向读数"),
29
+ aMinus: tool.schema.number().describe("A-方向读数"),
30
+ bPlus: tool.schema.number().optional().describe("B+方向读数"),
31
+ bMinus: tool.schema.number().optional().describe("B-方向读数"),
32
+ }),
33
+ )
34
+ .min(2)
35
+ .describe("初始(基准)读数,从浅到深排列"),
36
+ currentReadings: tool.schema
37
+ .array(
38
+ tool.schema.object({
39
+ depth: tool.schema.number().describe("测量深度(m)"),
40
+ aPlus: tool.schema.number().describe("A+方向读数"),
41
+ aMinus: tool.schema.number().describe("A-方向读数"),
42
+ bPlus: tool.schema.number().optional().describe("B+方向读数"),
43
+ bMinus: tool.schema.number().optional().describe("B-方向读数"),
44
+ }),
45
+ )
46
+ .min(2)
47
+ .describe("本期读数,深度序列与初始读数一致"),
48
+ alertThreshold: tool.schema.number().positive().optional().describe("水平位移报警值(mm)"),
49
+ },
50
+ async execute(args) {
51
+ const K = 2 * 25000 // 灵敏度系数,标准测斜仪 2×25000
52
+ const L = args.gaugeLength
53
+
54
+ // Build depth-indexed maps
55
+ const initMap = new Map(args.initialReadings.map((r) => [r.depth, r]))
56
+ const currMap = new Map(args.currentReadings.map((r) => [r.depth, r]))
57
+
58
+ // Get sorted depths (deep to shallow for bottom-up accumulation)
59
+ const depths = [...new Set([...initMap.keys(), ...currMap.keys()])].sort((a, b) => b - a)
60
+
61
+ // Calculate incremental displacement at each depth
62
+ const increments: Array<{
63
+ depth: number
64
+ deltaA: number
65
+ deltaB: number | null
66
+ }> = []
67
+
68
+ for (const d of depths) {
69
+ const init = initMap.get(d)
70
+ const curr = currMap.get(d)
71
+ if (!init || !curr) continue
72
+
73
+ // A direction: combined reading = (A+ - A-) to eliminate zero offset
74
+ const initCombA = init.aPlus - init.aMinus
75
+ const currCombA = curr.aPlus - curr.aMinus
76
+ const deltaA = ((currCombA - initCombA) / K) * L * 1000 // mm
77
+
78
+ let deltaB: number | null = null
79
+ if (
80
+ init.bPlus !== undefined &&
81
+ init.bMinus !== undefined &&
82
+ curr.bPlus !== undefined &&
83
+ curr.bMinus !== undefined
84
+ ) {
85
+ const initCombB = init.bPlus - init.bMinus
86
+ const currCombB = curr.bPlus - curr.bMinus
87
+ deltaB = ((currCombB - initCombB) / K) * L * 1000 // mm
88
+ }
89
+
90
+ increments.push({ depth: d, deltaA, deltaB })
91
+ }
92
+
93
+ // Bottom-up accumulation (from base depth upward)
94
+ // The deepest point (base) is assumed to have zero displacement
95
+ let cumA = 0
96
+ let cumB = 0
97
+ const profile: Array<{
98
+ depth: number
99
+ incremental_a_mm: number
100
+ incremental_b_mm: number | null
101
+ cumulative_a_mm: number
102
+ cumulative_b_mm: number | null
103
+ resultant_mm: number | null
104
+ status: string
105
+ }> = []
106
+
107
+ for (const inc of increments) {
108
+ cumA += inc.deltaA
109
+ if (inc.deltaB !== null) cumB += inc.deltaB
110
+
111
+ const resultant = inc.deltaB !== null ? Math.sqrt(cumA * cumA + cumB * cumB) : null
112
+
113
+ let status = "🟢 正常"
114
+ if (args.alertThreshold) {
115
+ const checkVal = resultant ?? Math.abs(cumA)
116
+ const ratio = checkVal / args.alertThreshold
117
+ if (ratio >= 1.0) status = "🔴 超限"
118
+ else if (ratio >= 0.85) status = "🟠 接近阈值"
119
+ else if (ratio >= 0.7) status = "🟡 关注"
120
+ }
121
+
122
+ profile.push({
123
+ depth: inc.depth,
124
+ incremental_a_mm: Number(inc.deltaA.toFixed(3)),
125
+ incremental_b_mm: inc.deltaB !== null ? Number(inc.deltaB.toFixed(3)) : null,
126
+ cumulative_a_mm: Number(cumA.toFixed(3)),
127
+ cumulative_b_mm: inc.deltaB !== null ? Number(cumB.toFixed(3)) : null,
128
+ resultant_mm: resultant !== null ? Number(resultant.toFixed(3)) : null,
129
+ status,
130
+ })
131
+ }
132
+
133
+ // Reverse so output is shallow-to-deep (top to bottom)
134
+ profile.reverse()
135
+
136
+ // Find max displacement
137
+ const maxPoint = profile.reduce(
138
+ (max, p) => {
139
+ const val = p.resultant_mm ?? Math.abs(p.cumulative_a_mm)
140
+ return val > max.value ? { depth: p.depth, value: val } : max
141
+ },
142
+ { depth: 0, value: 0 },
143
+ )
144
+
145
+ return JSON.stringify({
146
+ point_id: args.pointId,
147
+ direction: args.direction,
148
+ gauge_length_m: L,
149
+ base_depth_m: args.baseDepth,
150
+ measurement_count: profile.length,
151
+ max_displacement: {
152
+ depth_m: maxPoint.depth,
153
+ value_mm: Number(maxPoint.value.toFixed(3)),
154
+ },
155
+ profile,
156
+ alert_threshold_mm: args.alertThreshold ?? null,
157
+ message: `✅ ${args.pointId} 测斜分析完成:${profile.length}个测点,最大位移 ${maxPoint.value.toFixed(3)}mm(深度 ${maxPoint.depth}m)${args.alertThreshold ? `,控制值 ${args.alertThreshold}mm` : ""}`,
158
+ })
159
+ },
160
+ })
161
+
162
+ // ============================================================
163
+ // Tool: Inclinometer multi-period trend analysis
164
+ // ============================================================
165
+
166
+ export const inclinometer_trend = tool({
167
+ description:
168
+ "测斜仪多期数据趋势分析。输入同一测斜孔多期的最大水平位移值,分析位移发展趋势、速率变化,预测未来位移。用于基坑监测周报中的深层位移趋势判断。",
169
+ args: {
170
+ pointId: tool.schema.string().describe("测斜孔编号"),
171
+ maxDepth: tool.schema.number().positive().describe("最大位移所在深度(m)"),
172
+ records: tool.schema
173
+ .array(
174
+ tool.schema.object({
175
+ date: tool.schema.string().describe("观测日期 YYYY-MM-DD"),
176
+ maxDisplacement: tool.schema.number().describe("该期最大累计水平位移(mm)"),
177
+ }),
178
+ )
179
+ .min(3)
180
+ .describe("多期最大位移数据,按时间排序"),
181
+ alertThreshold: tool.schema.number().positive().optional().describe("报警值(mm)"),
182
+ predictionDays: tool.schema.number().int().positive().default(7).describe("预测天数"),
183
+ },
184
+ async execute(args) {
185
+ const n = args.records.length
186
+ const t0 = new Date(args.records[0]!.date).getTime()
187
+ const days = args.records.map((r) => (new Date(r.date).getTime() - t0) / 86400000)
188
+ const vals = args.records.map((r) => r.maxDisplacement)
189
+
190
+ // Period rates
191
+ const rates: Array<{ period: string; rate: number; increment: number }> = []
192
+ for (let i = 1; i < n; i++) {
193
+ const dt = days[i]! - days[i - 1]!
194
+ const dv = vals[i]! - vals[i - 1]!
195
+ rates.push({
196
+ period: `${args.records[i - 1]!.date} → ${args.records[i]!.date}`,
197
+ rate: dt > 0 ? Number((dv / dt).toFixed(4)) : 0,
198
+ increment: Number(dv.toFixed(3)),
199
+ })
200
+ }
201
+
202
+ // Linear regression
203
+ const meanX = days.reduce((s, v) => s + v, 0) / n
204
+ const meanY = vals.reduce((s, v) => s + v, 0) / n
205
+ const ssxy = days.reduce((s, x, i) => s + (x - meanX) * (vals[i]! - meanY), 0)
206
+ const ssxx = days.reduce((s, x) => s + (x - meanX) * (x - meanX), 0)
207
+ const b = ssxx > 0 ? ssxy / ssxx : 0
208
+ const a = meanY - b * meanX
209
+
210
+ const lastDay = days[n - 1]!
211
+ const predictions = Array.from({ length: args.predictionDays }, (_, i) => {
212
+ const d = lastDay + i + 1
213
+ return {
214
+ date: new Date(t0 + d * 86400000).toISOString().slice(0, 10),
215
+ predicted_mm: Number((a + b * d).toFixed(3)),
216
+ }
217
+ })
218
+
219
+ // Stability
220
+ const last3 = rates.slice(-3).map((r) => Math.abs(r.rate))
221
+ const avgLast3 = last3.length > 0 ? last3.reduce((s, v) => s + v, 0) / last3.length : 0
222
+ const avgRate = Math.abs(b)
223
+
224
+ let stability: string
225
+ if (avgLast3 < 0.01) stability = "✅ 已收敛"
226
+ else if (avgLast3 < 0.05) stability = "🟢 趋于收敛"
227
+ else if (avgLast3 > avgRate * 1.5) stability = "🔴 加速发展,需加密监测"
228
+ else stability = "🟡 等速发展,继续监测"
229
+
230
+ let daysToAlert: number | string = "不会到达"
231
+ if (args.alertThreshold && b > 0) {
232
+ const d = (args.alertThreshold - a) / b - lastDay
233
+ if (d > 0) daysToAlert = Number(d.toFixed(1))
234
+ }
235
+
236
+ return JSON.stringify({
237
+ point_id: args.pointId,
238
+ max_depth_m: args.maxDepth,
239
+ latest_mm: vals[n - 1],
240
+ trend_rate_mm_per_day: Number(b.toFixed(4)),
241
+ stability,
242
+ rates,
243
+ predictions,
244
+ days_to_alert: daysToAlert,
245
+ message: `✅ ${args.pointId} 深层位移趋势:最新 ${vals[n - 1]}mm,速率 ${b.toFixed(4)}mm/d,${stability}`,
246
+ })
247
+ },
248
+ })
@@ -0,0 +1,120 @@
1
+ /// <reference path="../env.d.ts" />
2
+ import { tool } from "nb-railwise/tool"
3
+ import path from "path"
4
+
5
+ export default tool({
6
+ description:
7
+ "处理自动化监测仪器(静力水准、全站仪机器人、测斜仪)的海量CSV/TXT数据文件。输入文件路径,返回清洗后的核心数据指标(本期变化量、累计变化量、速率、超限测点列表)。data_analyst 必须调用此工具,绝不直接读取原始文件。",
8
+ args: {
9
+ filePath: tool.schema.string().describe("用户上传的 CSV、TXT 或 Excel 文件的绝对路径"),
10
+ sensorType: tool.schema
11
+ .enum(["settlement", "inclinometer", "strain_gauge", "convergence", "gnss"])
12
+ .describe(
13
+ "传感器类型:settlement=沉降/静力水准, inclinometer=测斜仪, strain_gauge=应变计, convergence=收敛计, gnss=GNSS",
14
+ ),
15
+ alertThreshold: tool.schema
16
+ .number()
17
+ .positive()
18
+ .optional()
19
+ .describe("报警控制值(mm),用于自动标记超限测点,不传则不做超限判断"),
20
+ periodDays: tool.schema.int().positive().default(7).describe("统计周期天数,默认7天(本期=最近N天)"),
21
+ },
22
+ async execute(args) {
23
+ const file = Bun.file(args.filePath)
24
+ const exists = await file.exists()
25
+
26
+ if (!exists) return JSON.stringify({ error: `文件不存在:${args.filePath},请检查路径是否正确。` })
27
+
28
+ const ext = path.extname(args.filePath).toLowerCase()
29
+ if (![".csv", ".txt", ".dat"].includes(ext))
30
+ return JSON.stringify({
31
+ error: `暂不支持 ${ext} 格式,请转换为 CSV/TXT/DAT 文件后重试。`,
32
+ })
33
+
34
+ const raw = await file.text()
35
+ const lines = raw
36
+ .split("\n")
37
+ .map((l) => l.trim())
38
+ .filter((l) => l.length > 0 && !l.startsWith("#"))
39
+
40
+ if (lines.length < 2) return JSON.stringify({ error: "文件内容为空或仅有标题行,无有效数据。" })
41
+
42
+ const header = lines[0]!.split(/[,\t;]/)
43
+ const rows = lines.slice(1).map((l) => l.split(/[,\t;]/))
44
+
45
+ const timeIdx = header.findIndex((h) => /time|date|时间|日期/i.test(h))
46
+ const idIdx = header.findIndex((h) => /id|point|测点|编号/i.test(h))
47
+ const valIdx = header.findIndex((h) => /value|val|读数|高差|沉降|位移|应变/i.test(h))
48
+
49
+ if (valIdx === -1)
50
+ return JSON.stringify({
51
+ error: `无法识别数值列,请确认 CSV 列头包含 value/val/读数/沉降/位移 等关键字。识别到的列:${header.join(", ")}`,
52
+ })
53
+
54
+ const pointGroups: Record<string, number[]> = {}
55
+ for (const row of rows) {
56
+ const id = idIdx >= 0 ? (row[idIdx] ?? "unknown") : `P${rows.indexOf(row)}`
57
+ const raw = parseFloat(row[valIdx] ?? "")
58
+ if (isNaN(raw)) continue
59
+ if (!pointGroups[id]) pointGroups[id] = []
60
+ pointGroups[id]!.push(raw)
61
+ }
62
+
63
+ const totalPoints = Object.keys(pointGroups).length
64
+ if (totalPoints === 0) return JSON.stringify({ error: "未能从文件中解析到有效的数值数据,请检查文件格式。" })
65
+
66
+ const cutoff = args.periodDays
67
+ const results = Object.entries(pointGroups).map(([id, vals]) => {
68
+ const mean = vals.reduce((a, b) => a + b, 0) / vals.length
69
+ const deviations = vals.map((v) => Math.abs(v - mean))
70
+ const mad = deviations.reduce((a, b) => a + b, 0) / deviations.length
71
+ const threshold3sigma = 3 * (mad * 1.4826)
72
+ const cleaned = vals.filter((v) => Math.abs(v - mean) <= threshold3sigma)
73
+
74
+ const baseline = cleaned[0] ?? 0
75
+ const current = cleaned[cleaned.length - 1] ?? 0
76
+ const cumulative = current - baseline
77
+
78
+ const recent = cleaned.slice(-cutoff)
79
+ const periodChange = recent.length >= 2 ? recent[recent.length - 1]! - recent[0]! : 0
80
+ const rate = recent.length >= 2 ? periodChange / cutoff : 0
81
+
82
+ const removedCount = vals.length - cleaned.length
83
+
84
+ const exceeded = args.alertThreshold ? Math.abs(cumulative) >= args.alertThreshold : false
85
+ const ratioPct = args.alertThreshold
86
+ ? Number(((Math.abs(cumulative) / args.alertThreshold) * 100).toFixed(1))
87
+ : null
88
+
89
+ return {
90
+ point_id: id,
91
+ total_readings: vals.length,
92
+ removed_outliers: removedCount,
93
+ cumulative_mm: Number(cumulative.toFixed(3)),
94
+ period_change_mm: Number(periodChange.toFixed(3)),
95
+ rate_mm_per_day: Number(rate.toFixed(4)),
96
+ exceeded_threshold: exceeded,
97
+ ratio_pct: ratioPct,
98
+ }
99
+ })
100
+
101
+ const exceeded = results.filter((r) => r.exceeded_threshold)
102
+ const maxCumulative = results.reduce((a, b) => (Math.abs(a.cumulative_mm) > Math.abs(b.cumulative_mm) ? a : b))
103
+
104
+ return JSON.stringify({
105
+ file: path.basename(args.filePath),
106
+ sensor_type: args.sensorType,
107
+ total_points: totalPoints,
108
+ period_days: args.periodDays,
109
+ alert_threshold_mm: args.alertThreshold ?? null,
110
+ exceeded_count: exceeded.length,
111
+ max_cumulative_point: maxCumulative.point_id,
112
+ max_cumulative_mm: maxCumulative.cumulative_mm,
113
+ exceeded_points: exceeded.map((r) => r.point_id),
114
+ summary: results,
115
+ data_quality_note: results.some((r) => r.removed_outliers > 0)
116
+ ? `共剔除 ${results.reduce((s, r) => s + r.removed_outliers, 0)} 个异常跳变点(采用 MAD 3σ 方法)`
117
+ : "原始数据质量良好,无异常值剔除",
118
+ })
119
+ },
120
+ })
@@ -0,0 +1,63 @@
1
+ /// <reference path="../env.d.ts" />
2
+ import { tool } from "nb-railwise/tool"
3
+ import { callOsApi, first, list, num, prettyJson, type Obj } from "../lib/os_api"
4
+
5
+ function compactContract(item: Obj) {
6
+ const attachments = list(item.attachments)
7
+ return {
8
+ id: first(item, ["id"]),
9
+ contractNo: first(item, ["contractNo", "code"]),
10
+ projectName: first(item, ["projectName", "name"]),
11
+ category: first(item, ["category", "contractCategory"]),
12
+ type: first(item, ["type", "projectType"]),
13
+ status: first(item, ["status"]),
14
+ partyA: first(item, ["partyA"]),
15
+ partyB: first(item, ["partyB"]),
16
+ amount: num(item.amount ?? item.amountYuan),
17
+ paidAmount: num(item.paidAmount),
18
+ unpaidAmount: num(item.unpaidAmount),
19
+ manager: first(item, ["manager"]),
20
+ collectionOwner: first(item, ["collectionOwner"]),
21
+ signDate: first(item, ["signDate"]),
22
+ startDate: first(item, ["startDate"]),
23
+ endDate: first(item, ["endDate"]),
24
+ attachmentCount: attachments.length,
25
+ attachments: attachments.slice(0, 5).map((attachment) => ({
26
+ id: first(attachment, ["id", "fileId", "token"]),
27
+ name: first(attachment, ["name", "fileName", "title"]),
28
+ url: first(attachment, ["url", "downloadUrl"]),
29
+ })),
30
+ }
31
+ }
32
+
33
+ export default tool({
34
+ description: "Query OS finance contracts, including party B, amount, collection status and attachment summaries.",
35
+ args: {
36
+ keyword: tool.schema.string().optional().describe("Keyword for contract number, project name, party A/B or manager."),
37
+ year: tool.schema.string().optional().describe("Contract year filter."),
38
+ manager: tool.schema.string().optional().describe("Manager filter."),
39
+ contractCategory: tool.schema.string().optional().describe("Contract category filter."),
40
+ status: tool.schema.string().optional().describe("Status filter."),
41
+ pendingOnly: tool.schema.boolean().optional().describe("Return pending contracts only."),
42
+ limit: tool.schema.number().int().min(1).max(100).optional().describe("Maximum result count."),
43
+ },
44
+ async execute(args) {
45
+ const data = await callOsApi("/finance/contracts", {
46
+ query: {
47
+ keyword: args.keyword,
48
+ year: args.year,
49
+ manager: args.manager,
50
+ contractCategory: args.contractCategory,
51
+ status: args.status,
52
+ pendingOnly: args.pendingOnly,
53
+ size: args.limit || 20,
54
+ },
55
+ })
56
+ const root = data && typeof data === "object" && !Array.isArray(data) ? data as Obj : null
57
+ const items = root ? list(root.items) : list(data)
58
+ return prettyJson({
59
+ total: root?.total ?? items.length,
60
+ items: items.map(compactContract),
61
+ })
62
+ },
63
+ })