@stalfh233/omc-cli 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +304 -3
  3. package/coverage/m1-coverage-manifest-v1.json +1278 -0
  4. package/dist/approval-token.js +102 -0
  5. package/dist/args.js +25 -0
  6. package/dist/artifacts.js +119 -0
  7. package/dist/bench/call-face-eval.js +256 -0
  8. package/dist/bench/context-attribution.js +151 -0
  9. package/dist/bench/discovery-cost-eval.js +230 -0
  10. package/dist/bench/driver.js +81 -0
  11. package/dist/bench/evals.js +236 -0
  12. package/dist/bench/fake-http-server.js +65 -0
  13. package/dist/bench/instrument.js +87 -0
  14. package/dist/bench/intent-face-eval.js +343 -0
  15. package/dist/bench/run.js +166 -0
  16. package/dist/bench/scenario.js +343 -0
  17. package/dist/bench/types.js +76 -0
  18. package/dist/bi-wire.js +41 -0
  19. package/dist/bizservice-config.js +581 -0
  20. package/dist/call.js +153 -0
  21. package/dist/capability-absences.js +23 -0
  22. package/dist/capability-overview.js +492 -0
  23. package/dist/capability-shape.js +154 -0
  24. package/dist/cli-contract.js +70 -0
  25. package/dist/cli-output.js +73 -0
  26. package/dist/cli.js +1418 -0
  27. package/dist/code-rules.js +69 -0
  28. package/dist/command-transport.js +155 -0
  29. package/dist/config-store.js +195 -0
  30. package/dist/context.js +21 -0
  31. package/dist/contract-consistency.js +66 -0
  32. package/dist/contract-resources.js +62 -0
  33. package/dist/coverage-consistency.js +62 -0
  34. package/dist/coverage-registry.js +76 -0
  35. package/dist/coverage.js +130 -0
  36. package/dist/data-list-filter.js +114 -0
  37. package/dist/discovery.js +390 -0
  38. package/dist/endpoints.js +154 -0
  39. package/dist/environment-policy.js +26 -0
  40. package/dist/execution-metadata.js +1092 -0
  41. package/dist/fake/app.js +45 -0
  42. package/dist/fake/b2-registration.js +594 -0
  43. package/dist/fake/businessrule.js +242 -0
  44. package/dist/fake/datarule.js +72 -0
  45. package/dist/fake/dictionary.js +108 -0
  46. package/dist/fake/environment.js +30 -0
  47. package/dist/fake/field.js +167 -0
  48. package/dist/fake/form.js +101 -0
  49. package/dist/fake/index.js +121 -0
  50. package/dist/fake/list-view.js +289 -0
  51. package/dist/fake/model.js +136 -0
  52. package/dist/fake/online-js.js +18 -0
  53. package/dist/fake/report.js +253 -0
  54. package/dist/fake/routes.js +47 -0
  55. package/dist/fake/rule-lifecycle.js +37 -0
  56. package/dist/fake/runtime-data.js +367 -0
  57. package/dist/fake/state.js +67 -0
  58. package/dist/fake/workflow.js +364 -0
  59. package/dist/field-change.js +200 -0
  60. package/dist/field-families.js +896 -0
  61. package/dist/form-layout.js +111 -0
  62. package/dist/form-support.js +821 -0
  63. package/dist/goal-routes.js +468 -0
  64. package/dist/governed-execution.js +87 -0
  65. package/dist/human-summary.js +212 -0
  66. package/dist/identity.js +62 -0
  67. package/dist/intent/baseline.js +57 -0
  68. package/dist/intent/capabilities/bizservice.js +274 -0
  69. package/dist/intent/capabilities/businessrule.js +493 -0
  70. package/dist/intent/capabilities/datarule.js +187 -0
  71. package/dist/intent/capabilities/field.js +545 -0
  72. package/dist/intent/capabilities/form.js +136 -0
  73. package/dist/intent/capabilities/index.js +64 -0
  74. package/dist/intent/capabilities/listview.js +157 -0
  75. package/dist/intent/capabilities/model.js +100 -0
  76. package/dist/intent/capabilities/onlinejs.js +108 -0
  77. package/dist/intent/capabilities/report.js +355 -0
  78. package/dist/intent/capabilities/workflow.js +458 -0
  79. package/dist/intent/capability.js +6 -0
  80. package/dist/intent/cli.js +91 -0
  81. package/dist/intent/compare.js +56 -0
  82. package/dist/intent/compiler.js +79 -0
  83. package/dist/intent/dsl.js +129 -0
  84. package/dist/intent/plan-file.js +63 -0
  85. package/dist/intent/readback.js +65 -0
  86. package/dist/intent/schema.js +158 -0
  87. package/dist/intent/validation.js +30 -0
  88. package/dist/intent/yaml.js +315 -0
  89. package/dist/json-column.js +68 -0
  90. package/dist/lanes/app-contract.js +95 -0
  91. package/dist/lanes/app-coverage.js +16 -0
  92. package/dist/lanes/app.js +174 -0
  93. package/dist/lanes/apply-changes.js +231 -0
  94. package/dist/lanes/b2-registration-contract.js +292 -0
  95. package/dist/lanes/b2-registration-coverage.js +48 -0
  96. package/dist/lanes/b2-registration.js +1187 -0
  97. package/dist/lanes/businessrule-contract.js +232 -0
  98. package/dist/lanes/businessrule-coverage.js +16 -0
  99. package/dist/lanes/businessrule.js +221 -0
  100. package/dist/lanes/contract-support.js +65 -0
  101. package/dist/lanes/coverage-declaration.js +9 -0
  102. package/dist/lanes/datarule-contract.js +155 -0
  103. package/dist/lanes/datarule-coverage.js +19 -0
  104. package/dist/lanes/datarule-protocol.js +308 -0
  105. package/dist/lanes/datarule.js +818 -0
  106. package/dist/lanes/dictionary-contract.js +78 -0
  107. package/dist/lanes/dictionary-coverage.js +24 -0
  108. package/dist/lanes/dictionary.js +235 -0
  109. package/dist/lanes/environment-contract.js +70 -0
  110. package/dist/lanes/environment-coverage.js +14 -0
  111. package/dist/lanes/environment.js +163 -0
  112. package/dist/lanes/field-contract.js +223 -0
  113. package/dist/lanes/field-coverage.js +27 -0
  114. package/dist/lanes/field.js +374 -0
  115. package/dist/lanes/form-contract.js +97 -0
  116. package/dist/lanes/form-coverage.js +16 -0
  117. package/dist/lanes/form.js +185 -0
  118. package/dist/lanes/lane-ids.js +34 -0
  119. package/dist/lanes/list-view-contract.js +175 -0
  120. package/dist/lanes/list-view-coverage.js +20 -0
  121. package/dist/lanes/list-view-shapes.js +1207 -0
  122. package/dist/lanes/list-view.js +578 -0
  123. package/dist/lanes/meta-contract.js +85 -0
  124. package/dist/lanes/meta.js +255 -0
  125. package/dist/lanes/model-contract.js +168 -0
  126. package/dist/lanes/model-coverage.js +20 -0
  127. package/dist/lanes/model.js +986 -0
  128. package/dist/lanes/online-js-contract.js +99 -0
  129. package/dist/lanes/online-js-coverage.js +28 -0
  130. package/dist/lanes/online-js.js +127 -0
  131. package/dist/lanes/report-contract.js +144 -0
  132. package/dist/lanes/report-coverage.js +21 -0
  133. package/dist/lanes/report.js +476 -0
  134. package/dist/lanes/rule-graph.js +1846 -0
  135. package/dist/lanes/rule-lifecycle-contract.js +92 -0
  136. package/dist/lanes/rule-lifecycle-coverage.js +20 -0
  137. package/dist/lanes/rule-lifecycle.js +176 -0
  138. package/dist/lanes/runtime-data-contract.js +264 -0
  139. package/dist/lanes/runtime-data-coverage.js +25 -0
  140. package/dist/lanes/runtime-data.js +1054 -0
  141. package/dist/lanes/workflow-contract.js +223 -0
  142. package/dist/lanes/workflow-coverage.js +40 -0
  143. package/dist/lanes/workflow.js +1813 -0
  144. package/dist/online-js-layout.js +58 -0
  145. package/dist/online-js-source.js +276 -0
  146. package/dist/package-tool.js +51 -0
  147. package/dist/package.js +73 -0
  148. package/dist/plan.js +73 -0
  149. package/dist/read.js +144 -0
  150. package/dist/redact.js +28 -0
  151. package/dist/rule-support.js +587 -0
  152. package/dist/runtime-support.js +134 -0
  153. package/dist/server.js +92 -0
  154. package/dist/session-manager.js +30 -0
  155. package/dist/session.js +149 -0
  156. package/dist/skills.js +112 -0
  157. package/dist/support.js +98 -0
  158. package/dist/tool-types.js +127 -0
  159. package/dist/tools.js +59 -0
  160. package/dist/usage-log.js +197 -0
  161. package/dist/wire.js +287 -0
  162. package/dist/workflow-support.js +99 -0
  163. package/dist/write-lease.js +26 -0
  164. package/dist/write-lock.js +109 -0
  165. package/dist/write.js +176 -0
  166. package/dist/zip.js +156 -0
  167. package/docs/tool-surface-map.md +42 -0
  168. package/package.json +71 -6
  169. package/skills/omc-acceptance-criteria.md +47 -0
  170. package/skills/omc-business-configuration.md +257 -0
  171. package/skills/omc-capabilities.md +197 -0
  172. package/skills/omc-capability-scouting.md +55 -0
  173. package/skills/omc-config-draft-review.md +171 -0
  174. package/skills/omc-five-piece-flow.md +35 -0
  175. package/skills/omc-glossary.md +86 -0
  176. package/skills/omc-refusals.md +110 -0
  177. package/skills/omc-requirement-analysis.md +161 -0
  178. package/skills/omc-requirement-vocabulary.md +51 -0
  179. package/skills/omc-start-here.md +82 -0
  180. package/skills/omc-tool-selection.md +119 -0
  181. package/skills/omc-write-hazards.md +87 -0
@@ -0,0 +1,476 @@
1
+ import { z } from "zod";
2
+ import { ENDPOINTS } from "../endpoints.js";
3
+ import { capabilityArg, environmentArg } from "../args.js";
4
+ import { governedRead } from "../read.js";
5
+ import { writeTool } from "../write.js";
6
+ import { defineTool, ToolRefusalError } from "../tool-types.js";
7
+ import { wireCall, WireError } from "../wire.js";
8
+ import { biCall, DEFAULT_BI_CORP_ID } from "../bi-wire.js";
9
+ import { REPORT_LANE_CONTRACT } from "./report-contract.js";
10
+ const LANE = REPORT_LANE_CONTRACT.lane;
11
+ const TOOLS = REPORT_LANE_CONTRACT.tools;
12
+ /**
13
+ * The default BI dashboard `global` (theme21). A brand-new report page has no
14
+ * BI dashboard record until it is saved, and the BI save requires a `global`
15
+ * JSON string; this is the live-captured default so a freshly created report
16
+ * renders instead of failing or coming up unstyled.
17
+ */
18
+ export const DEFAULT_REPORT_GLOBAL = Object.freeze({
19
+ styles: {
20
+ theme: {
21
+ type: "theme21",
22
+ colors: ["#677EFF", "#FF794D", "#33C5E8", "#FFC836", "#66CA5A", "#FD787F", "#6FACED", "#FAAC3D", "#C178FE", "#D1F16D", "#FD787F", "#FFDA60", "#FF90A5", "#65E0E3", "#FA993D", "#46D399", "#8D6AFF", "#6FD0ED", "#A573FF", "#FD656D"],
23
+ },
24
+ paintCoatTheme: "default",
25
+ paintCoat: { type: "bgColor", value: "#F1F2F5", image: null, showMode: "draw" },
26
+ elementCoat: { type: "bgColor", value: "#FFFFFF" },
27
+ fontSetting: { titleColor: "#121933", fontColor: null },
28
+ globalGapSetting: { globalGapSetting: "dense", gridGapSetting: "dense", innerGapSetting: "dense" },
29
+ gridLayout: { rowHeight: 32, colNumber: 32 },
30
+ roundedCornerSetting: { roundedCornerSetting: "default", isCustom: false, customCorner: "" },
31
+ globalTimer: { autoRefreshEnabled: false, refreshInterval: 5 },
32
+ arrangementMode: "multiRow",
33
+ },
34
+ });
35
+ const chartObject = z.record(z.string(), z.unknown());
36
+ function authOf(corpId, reportCode, appCode) {
37
+ return { corpId: corpId ?? DEFAULT_BI_CORP_ID, reportCode, appCode };
38
+ }
39
+ /** Parse a JSON string, returning the raw text when it is not JSON. */
40
+ function parseJsonText(value) {
41
+ if (typeof value !== "string")
42
+ return value;
43
+ const text = value.trim();
44
+ if (!text)
45
+ return undefined;
46
+ try {
47
+ return JSON.parse(text);
48
+ }
49
+ catch {
50
+ return value;
51
+ }
52
+ }
53
+ function isMissingDashboard(error) {
54
+ return error instanceof WireError && /dashboard\.not\.exist|The dashboard does not exist/i.test(error.message);
55
+ }
56
+ /** Recursively collect report functions from a report tree (queryReportChartTree[*]). */
57
+ function collectReports(node, out) {
58
+ if (Array.isArray(node)) {
59
+ for (const item of node)
60
+ collectReports(item, out);
61
+ return;
62
+ }
63
+ if (!node || typeof node !== "object")
64
+ return;
65
+ const record = node;
66
+ if (Array.isArray(record.appReportChartList)) {
67
+ const charts = record.appReportChartList.map((chart) => ({
68
+ uuid: chart.uuid,
69
+ title: chart.title,
70
+ type: chart.type,
71
+ objectId: chart.objectId,
72
+ }));
73
+ out.push({
74
+ code: record.code,
75
+ name: record.name,
76
+ appCode: record.appCode,
77
+ objectId: charts.length > 0 ? charts[0].objectId : record.reportObjectId,
78
+ charts,
79
+ });
80
+ }
81
+ for (const key of ["children", "appFunctionList", "appReportChartList"]) {
82
+ if (record[key] !== undefined)
83
+ collectReports(record[key], out);
84
+ }
85
+ }
86
+ /**
87
+ * The report page's parent folder id. `report/get` does NOT return `parentId`
88
+ * directly — live it must come from the last `folderList` entry (app → … →
89
+ * immediate folder). /api/report/update 500s ("服务异常") when parentId is
90
+ * absent, so this is required rather than cosmetic. The fake seeds a plain
91
+ * `parentId`, which is preferred when present.
92
+ */
93
+ function pageParentId(page) {
94
+ if (typeof page.parentId === "string" && page.parentId)
95
+ return page.parentId;
96
+ const list = Array.isArray(page.folderList) ? page.folderList : [];
97
+ const last = list[list.length - 1];
98
+ return last && typeof last.id === "string" ? last.id : undefined;
99
+ }
100
+ function reportPageView(page) {
101
+ return {
102
+ id: page.id,
103
+ reportObjectId: page.reportObjectId ?? page.id,
104
+ code: page.code,
105
+ name: page.name,
106
+ nameI18n: parseJsonText(page.name_i18n),
107
+ i18nMessage: page.i18nMessage,
108
+ appCode: page.appCode,
109
+ icon: page.icon,
110
+ description: page.description,
111
+ pcAble: page.pcAble,
112
+ mobileAble: page.mobileAble,
113
+ parentId: pageParentId(page),
114
+ };
115
+ }
116
+ function chartView(chart) {
117
+ return {
118
+ uuid: chart.uuid,
119
+ title: chart.title,
120
+ type: chart.type,
121
+ dataSourceId: chart.dataSourceId,
122
+ mainTable: chart.mainTable,
123
+ position: parseJsonText(chart.position),
124
+ };
125
+ }
126
+ /** Read the BI config, treating a missing dashboard as present:false rather than an error. */
127
+ async function readBiConfig(session, auth, objectId) {
128
+ try {
129
+ const data = (await biCall(session, {
130
+ path: ENDPOINTS.biReportConfig,
131
+ body: { objectId },
132
+ query: { lang: "cn" },
133
+ auth,
134
+ }));
135
+ if (!data || typeof data !== "object")
136
+ return { present: false, charts: [] };
137
+ return {
138
+ present: true,
139
+ corpId: data.corpId,
140
+ title: data.title,
141
+ charts: Array.isArray(data.charts) ? data.charts : [],
142
+ global: parseJsonText(data.global),
143
+ attributes: parseJsonText(data.attributes),
144
+ };
145
+ }
146
+ catch (error) {
147
+ if (isMissingDashboard(error))
148
+ return { present: false, charts: [] };
149
+ throw error;
150
+ }
151
+ }
152
+ function requireCode(code) {
153
+ const value = code.trim();
154
+ if (!/^[A-Za-z][A-Za-z0-9_]{0,59}$/.test(value))
155
+ throw new ToolRefusalError({ status: "refused", blocker: "report-code-invalid", message: "报表页 code 必须字母开头、≤60、仅字母数字下划线" });
156
+ return value;
157
+ }
158
+ function jsonString(value, fallback) {
159
+ if (value === undefined)
160
+ return JSON.stringify(fallback);
161
+ return typeof value === "string" ? value : JSON.stringify(value);
162
+ }
163
+ /**
164
+ * The chart query model the BI `chartdata` endpoint expects. The stored chart
165
+ * (from report/v2/config) keeps its query definition in `data` as a JSON string
166
+ * and models `limit` as `{ value, showOthers }`; the API wants the flattened
167
+ * `dimension`/`metric`/… at the top level with `limit` as an Integer. Verified
168
+ * by replaying the viewer's own request (live 2026-09-30): sending the raw chart
169
+ * answers server.error; this normalization answers with the series.
170
+ */
171
+ function chartParamModel(chart) {
172
+ const decoded = typeof chart.data === "string" ? parseJsonText(chart.data) : chart.data;
173
+ const d = decoded && typeof decoded === "object" && !Array.isArray(decoded) ? decoded : {};
174
+ const rawLimit = d.limit;
175
+ const limit = rawLimit && typeof rawLimit === "object" ? (rawLimit.value ?? null) : (rawLimit ?? null);
176
+ const relations = typeof chart.relations === "string" ? parseJsonText(chart.relations) : chart.relations;
177
+ return {
178
+ title: chart.title,
179
+ id: chart.id ?? chart.uuid,
180
+ authorization: chart.authorization,
181
+ dataSourceId: chart.dataSourceId,
182
+ useType: chart.useType,
183
+ type: chart.type,
184
+ dimension: d.dimension ?? [],
185
+ dynamicParams: d.dynamicParams ?? [],
186
+ metric: d.metric ?? [],
187
+ filter: d.filter ?? [],
188
+ sort: d.sort ?? [],
189
+ limit,
190
+ relations: relations ?? [],
191
+ mainTable: chart.mainTable,
192
+ options: d.options ?? {},
193
+ filterNone: d.filterNone,
194
+ };
195
+ }
196
+ export function registerReportLane(tools) {
197
+ tools.push(defineTool({
198
+ name: "report.tree",
199
+ layer: "low",
200
+ kind: "read",
201
+ lane: LANE,
202
+ scope: TOOLS["report.tree"].scope,
203
+ description: TOOLS["report.tree"].description,
204
+ inputSchema: { environment: environmentArg, capability: capabilityArg, appCode: z.string().optional() },
205
+ handler: async (ctx, args, self) => governedRead(ctx, self, args, async (session) => {
206
+ const data = args.appCode
207
+ ? (await wireCall(session, { path: ENDPOINTS.reportTreeByApp, query: { appCode: args.appCode } })).data
208
+ : (await wireCall(session, { path: ENDPOINTS.reportTreeAll, method: "POST", body: {} })).data;
209
+ const reports = [];
210
+ collectReports(data, reports);
211
+ return { appCode: args.appCode ?? null, reportCount: reports.length, reports };
212
+ }),
213
+ }));
214
+ tools.push(defineTool({
215
+ name: "report.get",
216
+ layer: "low",
217
+ kind: "read",
218
+ lane: LANE,
219
+ scope: TOOLS["report.get"].scope,
220
+ description: TOOLS["report.get"].description,
221
+ inputSchema: { environment: environmentArg, capability: capabilityArg, code: z.string().min(1), corpId: z.string().optional(), full: z.boolean().default(false) },
222
+ handler: async (ctx, args, self) => governedRead(ctx, self, args, async (session) => {
223
+ const page = (await wireCall(session, { path: ENDPOINTS.reportGet, query: { code: args.code } })).data;
224
+ const objectId = String(page.id ?? page.reportObjectId ?? "");
225
+ const auth = authOf(args.corpId, String(page.code ?? args.code), String(page.appCode ?? ""));
226
+ const bi = objectId ? await readBiConfig(session, auth, objectId) : { present: false, charts: [], title: undefined, global: undefined, attributes: undefined };
227
+ const biView = {
228
+ present: bi.present,
229
+ corpId: bi.corpId ?? args.corpId ?? DEFAULT_BI_CORP_ID,
230
+ title: bi.title,
231
+ chartCount: bi.charts.length,
232
+ charts: bi.charts.map(chartView),
233
+ globalPresent: bi.global !== undefined && JSON.stringify(bi.global) !== "{}",
234
+ attributesPresent: bi.attributes !== undefined && JSON.stringify(bi.attributes) !== "{}",
235
+ };
236
+ return {
237
+ report: reportPageView(page),
238
+ bi: biView,
239
+ global: bi.global,
240
+ attributes: bi.attributes,
241
+ charts: bi.charts,
242
+ ...(args.full ? { rawPage: page } : {}),
243
+ };
244
+ }),
245
+ }));
246
+ tools.push(defineTool({
247
+ name: "report.chartdata",
248
+ layer: "low",
249
+ kind: "read",
250
+ lane: LANE,
251
+ scope: TOOLS["report.chartdata"].scope,
252
+ description: TOOLS["report.chartdata"].description,
253
+ inputSchema: {
254
+ environment: environmentArg,
255
+ capability: capabilityArg,
256
+ reportCode: z.string().min(1),
257
+ appCode: z.string().min(1),
258
+ chart: chartObject,
259
+ dataSourceId: z.string().optional(),
260
+ corpId: z.string().optional(),
261
+ lang: z.string().default("en-US"),
262
+ },
263
+ handler: async (ctx, args, self) => governedRead(ctx, self, args, async (session) => {
264
+ const raw = args.chart;
265
+ const chart = chartParamModel(raw);
266
+ const data = await biCall(session, {
267
+ path: ENDPOINTS.biChartData,
268
+ query: { lang: args.lang },
269
+ body: { chart, dataSourceId: args.dataSourceId ?? raw.dataSourceId, corpId: args.corpId ?? DEFAULT_BI_CORP_ID },
270
+ auth: authOf(args.corpId, args.reportCode, args.appCode),
271
+ });
272
+ return { data };
273
+ }),
274
+ }));
275
+ tools.push(defineTool({
276
+ name: "report.datasource.list",
277
+ layer: "low",
278
+ kind: "read",
279
+ lane: LANE,
280
+ scope: TOOLS["report.datasource.list"].scope,
281
+ description: TOOLS["report.datasource.list"].description,
282
+ inputSchema: {
283
+ environment: environmentArg,
284
+ capability: capabilityArg,
285
+ kind: z.enum(["model", "advanced", "all"]).default("all"),
286
+ reportCode: z.string().min(1),
287
+ appCode: z.string().min(1),
288
+ corpId: z.string().optional(),
289
+ },
290
+ handler: async (ctx, args, self) => governedRead(ctx, self, args, async (session) => {
291
+ const auth = authOf(args.corpId, args.reportCode, args.appCode);
292
+ const fetchOne = (path) => biCall(session, { path, body: {}, query: { lang: "cn" }, auth }).then((data) => (Array.isArray(data) ? data : []));
293
+ const model = args.kind === "advanced" ? [] : await fetchOne(ENDPOINTS.biDatasourceModelList);
294
+ const advanced = args.kind === "model" ? [] : await fetchOne(ENDPOINTS.biDatasourceAdvancedList);
295
+ const seen = new Set();
296
+ const merged = [...model, ...advanced].filter((entry) => {
297
+ const id = String(entry.dataSourceId ?? entry.objectId ?? "");
298
+ if (!id || seen.has(id))
299
+ return false;
300
+ seen.add(id);
301
+ return true;
302
+ });
303
+ return { kind: args.kind, count: merged.length, datasources: merged };
304
+ }),
305
+ }));
306
+ writeTool(tools, "report.datasource.link", LANE, TOOLS["report.datasource.link"].scope, TOOLS["report.datasource.link"].description, {
307
+ reportCode: z.string().min(1),
308
+ appCode: z.string().min(1),
309
+ dataSourceInfos: z.array(z.object({ dataSourceId: z.string().min(1), useType: z.number().int().optional() })).min(1),
310
+ corpId: z.string().optional(),
311
+ }, async (session, args) => {
312
+ const auth = authOf(args.corpId, args.reportCode, args.appCode);
313
+ const infos = args.dataSourceInfos.map((info) => ({ dataSourceId: info.dataSourceId, useType: info.useType ?? 1 }));
314
+ const linked = (await biCall(session, { path: ENDPOINTS.biDatasourceModelLink, body: { dataSourceInfos: infos }, query: { lang: "cn" }, auth }));
315
+ const models = Array.isArray(linked) ? linked : [];
316
+ return {
317
+ performed: { errcode: 0, linked: infos.map((info) => info.dataSourceId) },
318
+ readback: { linkedCount: models.length, models: models.map((model) => ({ dataSourceId: model.dataSourceId, displayName: model.displayName, tableName: model.schema?.tableName })) },
319
+ };
320
+ });
321
+ writeTool(tools, "report.create", LANE, TOOLS["report.create"].scope, TOOLS["report.create"].description, {
322
+ code: z.string().min(1),
323
+ name: z.string().min(1).max(60),
324
+ appCode: z.string().min(1),
325
+ parentId: z.string().min(1),
326
+ icon: z.string().default("h-icon-all-Report"),
327
+ nameI18n: z.union([z.record(z.string(), z.string()), z.string()]).optional(),
328
+ pcAble: z.boolean().default(true),
329
+ mobileAble: z.boolean().default(true),
330
+ corpId: z.string().optional(),
331
+ }, async (session, args) => {
332
+ const code = requireCode(args.code);
333
+ const created = (await wireCall(session, {
334
+ path: ENDPOINTS.reportCreate,
335
+ method: "POST",
336
+ body: {
337
+ code,
338
+ name: args.name,
339
+ parentId: args.parentId,
340
+ icon: args.icon,
341
+ name_i18n: typeof args.nameI18n === "string" ? args.nameI18n : JSON.stringify(args.nameI18n ?? { zh: args.name }),
342
+ pcAble: args.pcAble,
343
+ mobileAble: args.mobileAble,
344
+ appCode: args.appCode,
345
+ },
346
+ })).data;
347
+ const page = (await wireCall(session, { path: ENDPOINTS.reportGet, query: { code } })).data;
348
+ const objectId = String(page.id ?? page.reportObjectId ?? "");
349
+ if (objectId) {
350
+ await biCall(session, {
351
+ path: ENDPOINTS.biReportPageSave,
352
+ body: { report: { objectId, title: args.name, attributes: "{}", global: JSON.stringify(DEFAULT_REPORT_GLOBAL) } },
353
+ auth: authOf(args.corpId, code, args.appCode),
354
+ });
355
+ }
356
+ void created;
357
+ return {
358
+ performed: { errcode: 0, code, appCode: args.appCode, objectId },
359
+ readback: { present: page.code === code, report: reportPageView(page), biInitialized: objectId !== "" },
360
+ };
361
+ }, (args) => requireCode(args.code));
362
+ writeTool(tools, "report.update", LANE, TOOLS["report.update"].scope, TOOLS["report.update"].description, {
363
+ code: z.string().min(1),
364
+ name: z.string().min(1).max(60).optional(),
365
+ icon: z.string().optional(),
366
+ nameI18n: z.union([z.record(z.string(), z.string()), z.string()]).optional(),
367
+ pcAble: z.boolean().optional(),
368
+ mobileAble: z.boolean().optional(),
369
+ parentId: z.string().optional(),
370
+ }, async (session, args) => {
371
+ const page = (await wireCall(session, { path: ENDPOINTS.reportGet, query: { code: args.code } })).data;
372
+ if (!page || !page.id)
373
+ throw new ToolRefusalError({ status: "refused", blocker: "report-not-found", message: `报表页 ${args.code} 不存在` });
374
+ const name = args.name ?? String(page.name ?? "");
375
+ const performed = await wireCall(session, {
376
+ path: ENDPOINTS.reportUpdate,
377
+ method: "POST",
378
+ body: {
379
+ id: page.id,
380
+ code: page.code ?? args.code,
381
+ appCode: page.appCode,
382
+ name,
383
+ parentId: args.parentId ?? pageParentId(page),
384
+ icon: args.icon ?? page.icon,
385
+ name_i18n: typeof args.nameI18n === "string" ? args.nameI18n : JSON.stringify(args.nameI18n ?? parseJsonText(page.name_i18n) ?? { zh: name }),
386
+ pcAble: args.pcAble ?? page.pcAble,
387
+ mobileAble: args.mobileAble ?? page.mobileAble,
388
+ },
389
+ });
390
+ const after = (await wireCall(session, { path: ENDPOINTS.reportGet, query: { code: args.code } })).data;
391
+ return { performed: { errcode: performed.errcode }, readback: { present: Boolean(after && after.code), report: reportPageView(after) } };
392
+ });
393
+ writeTool(tools, "report.delete", LANE, TOOLS["report.delete"].scope, TOOLS["report.delete"].description, {
394
+ code: z.string().min(1),
395
+ corpId: z.string().optional(),
396
+ }, async (session, args) => {
397
+ const page = (await wireCall(session, { path: ENDPOINTS.reportGet, query: { code: args.code } })).data;
398
+ let dashboardRemoved = false;
399
+ if (page && page.id) {
400
+ try {
401
+ await biCall(session, {
402
+ path: ENDPOINTS.biReportRemove,
403
+ body: { objectId: page.id },
404
+ auth: authOf(args.corpId, String(page.code ?? args.code), String(page.appCode ?? "")),
405
+ });
406
+ dashboardRemoved = true;
407
+ }
408
+ catch (error) {
409
+ if (!isMissingDashboard(error))
410
+ throw error;
411
+ }
412
+ }
413
+ const performed = await wireCall(session, { path: ENDPOINTS.reportDelete, method: "POST", query: { code: args.code } });
414
+ const tree = (await wireCall(session, { path: ENDPOINTS.reportTreeByApp, query: { appCode: String(page?.appCode ?? "") } })).data;
415
+ const remaining = [];
416
+ collectReports(tree, remaining);
417
+ const stillListed = remaining.some((report) => report.code === args.code);
418
+ return { performed: { errcode: performed.errcode, code: args.code, dashboardRemoved }, readback: { absent: !stillListed, remainingInTree: stillListed } };
419
+ });
420
+ writeTool(tools, "report.configure", LANE, TOOLS["report.configure"].scope, TOOLS["report.configure"].description, {
421
+ reportCode: z.string().min(1),
422
+ appCode: z.string().min(1),
423
+ global: z.union([z.record(z.string(), z.unknown()), z.string()]).optional(),
424
+ attributes: z.union([z.record(z.string(), z.unknown()), z.string()]).optional(),
425
+ charts: z.array(chartObject).optional(),
426
+ removeUnlistedCharts: z.boolean().default(false),
427
+ corpId: z.string().optional(),
428
+ }, async (session, args) => {
429
+ const auth = authOf(args.corpId, args.reportCode, args.appCode);
430
+ const page = (await wireCall(session, { path: ENDPOINTS.reportGet, query: { code: args.reportCode } })).data;
431
+ const objectId = String(page?.id ?? page?.reportObjectId ?? "");
432
+ if (!objectId)
433
+ throw new ToolRefusalError({ status: "refused", blocker: "report-not-found", message: `报表页 ${args.reportCode} 不存在` });
434
+ const current = await readBiConfig(session, auth, objectId);
435
+ if (!current.present)
436
+ throw new ToolRefusalError({ status: "refused", blocker: "report-dashboard-absent", message: `报表页 ${args.reportCode} 的 BI dashboard 不存在;先 report.create 初始化或用 report.get 确认 present` });
437
+ const existing = new Map(current.charts.map((chart) => [String(chart.uuid), chart]));
438
+ const declared = args.charts ?? [];
439
+ const toAdd = [];
440
+ const toUpdate = [];
441
+ const kept = new Set();
442
+ for (const chart of declared) {
443
+ const uuid = chart.uuid;
444
+ if (typeof uuid !== "string" || !uuid)
445
+ throw new ToolRefusalError({ status: "refused", blocker: "report-chart-uuid-required", message: "report.configure 的每个图表必须带 uuid(既有=更新,新图表自带 uuid)" });
446
+ kept.add(uuid);
447
+ // The BI write face scopes charts by their parent report objectId; stamp
448
+ // it so callers (and the intent compiler) need not know it.
449
+ const stamped = { ...chart, objectId };
450
+ (existing.has(uuid) ? toUpdate : toAdd).push(stamped);
451
+ }
452
+ const toRemove = args.removeUnlistedCharts ? [...existing.keys()].filter((uuid) => !kept.has(uuid)) : [];
453
+ const performed = {};
454
+ if (toAdd.length)
455
+ performed.added = (await biCall(session, { path: ENDPOINTS.biChartAdd, body: { charts: toAdd }, auth }));
456
+ if (toUpdate.length)
457
+ performed.updated = (await biCall(session, { path: ENDPOINTS.biChartUpdate, body: { charts: toUpdate }, auth }));
458
+ if (toRemove.length)
459
+ performed.removed = (await biCall(session, { path: ENDPOINTS.biChartRemoveMany, body: { objectIds: toRemove }, auth }));
460
+ if (args.global !== undefined)
461
+ performed.global = (await biCall(session, { path: ENDPOINTS.biReportSaveGlobal, body: { objectId, global: jsonString(args.global, {}) }, auth }));
462
+ if (args.attributes !== undefined)
463
+ performed.attributes = (await biCall(session, { path: ENDPOINTS.biReportSaveAttributes, body: { objectId, attributes: jsonString(args.attributes, {}) }, auth }));
464
+ const after = await readBiConfig(session, auth, objectId);
465
+ return {
466
+ performed: { ...performed, added: toAdd.length, updated: toUpdate.length, removed: toRemove.length },
467
+ readback: {
468
+ chartCount: after.charts.length,
469
+ charts: after.charts.map(chartView),
470
+ globalPresent: after.global !== undefined && JSON.stringify(after.global) !== "{}",
471
+ attributesPresent: after.attributes !== undefined && JSON.stringify(after.attributes) !== "{}",
472
+ },
473
+ };
474
+ });
475
+ }
476
+ //# sourceMappingURL=report.js.map