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.
- package/agent-pack/README.md +16 -0
- package/agent-pack/assets/agent/chief_manager.md +69 -0
- package/agent-pack/assets/agent/commercial_specialist.md +47 -0
- package/agent-pack/assets/agent/data_analyst.md +54 -0
- package/agent-pack/assets/agent/docs.md +38 -0
- package/agent-pack/assets/agent/duplicate-pr.md +25 -0
- package/agent-pack/assets/agent/qa_inspector.md +58 -0
- package/agent-pack/assets/agent/qa_reviewer.md +57 -0
- package/agent-pack/assets/agent/solution_architect.md +53 -0
- package/agent-pack/assets/agent/technical_writer.md +61 -0
- package/agent-pack/assets/agent/translator.md +34 -0
- package/agent-pack/assets/agent/triage.md +139 -0
- package/agent-pack/assets/command/ai-deps.md +24 -0
- package/agent-pack/assets/command/bid-intel.md +29 -0
- package/agent-pack/assets/command/bid-prepare.md +24 -0
- package/agent-pack/assets/command/commit.md +37 -0
- package/agent-pack/assets/command/daily-report.md +26 -0
- package/agent-pack/assets/command/data-check.md +22 -0
- package/agent-pack/assets/command/emergency-response.md +66 -0
- package/agent-pack/assets/command/issues.md +23 -0
- package/agent-pack/assets/command/learn.md +42 -0
- package/agent-pack/assets/command/monthly-report.md +35 -0
- package/agent-pack/assets/command/payment-reminder.md +27 -0
- package/agent-pack/assets/command/plan-draft.md +28 -0
- package/agent-pack/assets/command/review-response.md +23 -0
- package/agent-pack/assets/command/rmslop.md +15 -0
- package/agent-pack/assets/command/safety-check.md +20 -0
- package/agent-pack/assets/command/spellcheck.md +5 -0
- package/agent-pack/assets/command/trend-analysis.md +38 -0
- package/agent-pack/assets/command/weekly-report.md +28 -0
- package/agent-pack/assets/lib/os_api.ts +100 -0
- package/agent-pack/assets/skill/adjustment-report/SKILL.md +88 -0
- package/agent-pack/assets/skill/approval-flow-intelligence/SKILL.md +123 -0
- package/agent-pack/assets/skill/bidding-knowledge/SKILL.md +98 -0
- package/agent-pack/assets/skill/bun-file-io/SKILL.md +42 -0
- package/agent-pack/assets/skill/business-finance/SKILL.md +91 -0
- package/agent-pack/assets/skill/business-operations-analytics/SKILL.md +120 -0
- package/agent-pack/assets/skill/cad-bim-review/SKILL.md +84 -0
- package/agent-pack/assets/skill/canvas-design/SKILL.md +141 -0
- package/agent-pack/assets/skill/construction-monitoring/SKILL.md +89 -0
- package/agent-pack/assets/skill/customer-portal-brief/SKILL.md +121 -0
- package/agent-pack/assets/skill/data-analysis/SKILL.md +79 -0
- package/agent-pack/assets/skill/di-bao-monitoring/SKILL.md +680 -0
- package/agent-pack/assets/skill/di-bao-monitoring/agents/openai.yaml +4 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/crossing-static-level-input-template.csv +2 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/crossing-total-station-input-template.csv +2 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/crossing-total-station-report-template.md +59 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/daily-report-template.md +90 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/data-input-template.csv +2 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/initial-value-report-template.md +92 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/monthly-report-template.md +103 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/plan-template.md +673 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/point-alias-map-template.json +25 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/project-brief-template.md +59 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/project-summary-template.md +154 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/warning-notice-template.md +58 -0
- package/agent-pack/assets/skill/di-bao-monitoring/assets/weekly-report-template.md +49 -0
- 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
- package/agent-pack/assets/skill/di-bao-monitoring/assets//345/277/253/346/212/245/346/250/241/346/235/277.xlsx +0 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/crossing-static-level-report.md +115 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/crossing-total-station-report.md +206 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/data-processing.md +170 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/elevated-line.md +319 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/initial-and-summary-workflow.md +222 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/key-difficulties.md +216 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/monitoring-catalog.md +80 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/phase0-intake.md +169 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/phase1-drafting.md +185 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/phase2-internal-review.md +166 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/report-workflow.md +181 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/review-checklist.md +140 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/review-response-template.md +87 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/scoping-and-pricing.md +214 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/source-analysis.md +137 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/technical-standards.md +188 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/toc-template-A.md +142 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/toc-template-B.md +100 -0
- package/agent-pack/assets/skill/di-bao-monitoring/references/warning-workflow.md +63 -0
- package/agent-pack/assets/skill/di-bao-monitoring/scripts/build_crossing_total_station_xlsx.py +2438 -0
- package/agent-pack/assets/skill/di-bao-monitoring/scripts/evaluate_alarms.py +215 -0
- package/agent-pack/assets/skill/di-bao-monitoring/scripts/fetch_adjusted_total_station.py +946 -0
- package/agent-pack/assets/skill/di-bao-monitoring/scripts/fetch_shhh_static_level.py +556 -0
- package/agent-pack/assets/skill/di-bao-monitoring/scripts/run_crossing_4h_report.sh +141 -0
- package/agent-pack/assets/skill/di-bao-monitoring/scripts/summarize_crossing_total_station.py +303 -0
- package/agent-pack/assets/skill/docx-generation/SKILL.md +141 -0
- package/agent-pack/assets/skill/excel-operations/SKILL.md +121 -0
- package/agent-pack/assets/skill/frontend-design/SKILL.md +151 -0
- package/agent-pack/assets/skill/humanizer/SKILL.md +90 -0
- package/agent-pack/assets/skill/monitoring-design/SKILL.md +100 -0
- package/agent-pack/assets/skill/operational-monitoring/SKILL.md +135 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/checklists/archive-self-check.md +54 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/schemas/convergence-result-table.csv +5 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/schemas/horizontal-result-table.csv +3 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/schemas/settlement-result-table.csv +7 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/scripts/init-archive-tree.sh +65 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/templates/control-network-report.md +102 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/templates/daily-log.md +47 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/templates/i-angle-check.md +38 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/templates/monitoring-scheme.md +234 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/templates/period-report.md +95 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/templates/point-acceptance-record.md +51 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/templates/point-installation-record.md +38 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/templates/summary-report.md +89 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/templates/warning-bulletin.md +61 -0
- package/agent-pack/assets/skill/operational-monitoring/assets/templates/weekly-monthly-report.md +46 -0
- package/agent-pack/assets/skill/operational-monitoring/references/archive-and-delivery.md +146 -0
- package/agent-pack/assets/skill/operational-monitoring/references/baseline-network.md +131 -0
- package/agent-pack/assets/skill/operational-monitoring/references/convergence-monitoring.md +134 -0
- package/agent-pack/assets/skill/operational-monitoring/references/data-processing.md +178 -0
- package/agent-pack/assets/skill/operational-monitoring/references/horizontal-displacement.md +128 -0
- package/agent-pack/assets/skill/operational-monitoring/references/monitoring-points.md +132 -0
- package/agent-pack/assets/skill/operational-monitoring/references/monitoring-scheme.md +178 -0
- package/agent-pack/assets/skill/operational-monitoring/references/period-report.md +108 -0
- package/agent-pack/assets/skill/operational-monitoring/references/regulations-and-frequency.md +127 -0
- package/agent-pack/assets/skill/operational-monitoring/references/settlement-monitoring.md +116 -0
- package/agent-pack/assets/skill/operational-monitoring/references/summary-report.md +128 -0
- package/agent-pack/assets/skill/operational-monitoring/references/warning-and-disposal.md +118 -0
- package/agent-pack/assets/skill/ops-monitoring/SKILL.md +93 -0
- package/agent-pack/assets/skill/railwise-knowledge-curation/SKILL.md +92 -0
- package/agent-pack/assets/skill/report-dibao/SKILL.md +60 -0
- package/agent-pack/assets/skill/report-writing/SKILL.md +103 -0
- package/agent-pack/assets/skill/resource-dispatch-intelligence/SKILL.md +125 -0
- package/agent-pack/assets/skill/standard-reference/SKILL.md +101 -0
- package/agent-pack/assets/skill/weekly-work-intelligence/SKILL.md +123 -0
- package/agent-pack/assets/template/adjustment-trend.json +53 -0
- package/agent-pack/assets/template/bid-technical-proposal.json +42 -0
- package/agent-pack/assets/template/compliance-review.json +35 -0
- package/agent-pack/assets/template/daily-monitor-report.json +34 -0
- package/agent-pack/assets/template/field-data-qa.json +47 -0
- package/agent-pack/assets/template/monitoring-plan.json +28 -0
- package/agent-pack/assets/template/monthly-monitor-report.json +47 -0
- package/agent-pack/assets/template/project-ppt.json +47 -0
- package/agent-pack/assets/template/review-response.json +28 -0
- package/agent-pack/assets/template/schema.json +72 -0
- package/agent-pack/assets/template/weekly-monitor-report.json +33 -0
- package/agent-pack/assets/theme/mytheme.json +223 -0
- package/agent-pack/assets/tool/angle_convert.ts +151 -0
- package/agent-pack/assets/tool/axial_force.ts +186 -0
- package/agent-pack/assets/tool/chart_generator.ts +185 -0
- package/agent-pack/assets/tool/control_network.ts +413 -0
- package/agent-pack/assets/tool/coord_transform.ts +333 -0
- package/agent-pack/assets/tool/cpiii_adjustment.ts +446 -0
- package/agent-pack/assets/tool/cross_section.ts +356 -0
- package/agent-pack/assets/tool/deformation_rate.ts +234 -0
- package/agent-pack/assets/tool/distance_calculator.ts +239 -0
- package/agent-pack/assets/tool/excel_export.ts +494 -0
- package/agent-pack/assets/tool/format_parser.ts +181 -0
- package/agent-pack/assets/tool/github-pr-search.ts +57 -0
- package/agent-pack/assets/tool/github-pr-search.txt +10 -0
- package/agent-pack/assets/tool/github-triage.ts +113 -0
- package/agent-pack/assets/tool/github-triage.txt +6 -0
- package/agent-pack/assets/tool/inclinometer.ts +248 -0
- package/agent-pack/assets/tool/monitoring_csv.ts +120 -0
- package/agent-pack/assets/tool/os_contract_query.ts +63 -0
- package/agent-pack/assets/tool/os_dibao_project_files.ts +75 -0
- package/agent-pack/assets/tool/os_file_download_ref.ts +48 -0
- package/agent-pack/assets/tool/os_file_preview.ts +46 -0
- package/agent-pack/assets/tool/os_gbrain_think.ts +31 -0
- package/agent-pack/assets/tool/os_ppt_generate.ts +37 -0
- package/agent-pack/assets/tool/os_project_context.ts +24 -0
- package/agent-pack/assets/tool/os_report_publish.ts +28 -0
- package/agent-pack/assets/tool/os_wiki_ingest.ts +38 -0
- package/agent-pack/assets/tool/os_wiki_query.ts +26 -0
- package/agent-pack/assets/tool/pile_stakeout.ts +299 -0
- package/agent-pack/assets/tool/report_export.ts +279 -0
- package/agent-pack/assets/tool/shield_guidance.ts +311 -0
- package/agent-pack/assets/tool/standard_query.ts +391 -0
- package/agent-pack/assets/tool/survey_calculator.ts +415 -0
- package/agent-pack/assets/tool/water_level.ts +209 -0
- package/agent-pack/bin/install.js +183 -0
- package/agent-pack/package.json +695 -0
- package/package.json +14 -8
- 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
|
+
})
|