@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,45 @@
1
+ import { NOT_HANDLED } from "./state.js";
2
+ /** app-management lane: application package CRUD. Twin of src/lanes/app.ts. */
3
+ export const appRoute = (ctx, input) => {
4
+ const q = input.query ?? {};
5
+ const body = (input.body ?? {});
6
+ switch (input.path) {
7
+ case "/api/api/app/apppackage/list":
8
+ return ctx.options.applicationList ?? [...ctx.state.apps.values()].map((app) => ({ appCode: app.code, appName: app.name, id: app.id, groupId: app.groupId, published: app.published !== false }));
9
+ case "/api/api/app/apppackage/list_all_group":
10
+ return [...ctx.state.appGroups.values()].map((group) => ({
11
+ id: group.id,
12
+ code: group.code,
13
+ name: group.name,
14
+ children: [...ctx.state.apps.values()].filter((app) => app.groupId === group.id).map((app) => ({ id: app.id, code: app.code, name: app.name, groupId: app.groupId })),
15
+ }));
16
+ case "/api/api/app/apppackage/create": {
17
+ const code = String(body.code);
18
+ ctx.state.apps.set(code, { id: `app-${ctx.state.apps.size + 1}`, code, name: String(body.name ?? code), groupId: String(body.groupId ?? [...ctx.state.appGroups.keys()][0] ?? "group-1") });
19
+ return null;
20
+ }
21
+ case "/api/api/app/apppackage/get": {
22
+ const app = [...ctx.state.apps.values()].find((row) => row.code === q.appCode);
23
+ return app ? { id: app.id, code: app.code, name: app.name, name_i18n: JSON.stringify({ "zh-CN": app.name }), groupId: app.groupId, enabled: true } : null;
24
+ }
25
+ case "/api/api/app/apppackage/update": {
26
+ // AppIntegrationFacadeImpl.updateAppPackage: id-required whole-record
27
+ // update; explicit groupId honored; blank groupId would be forced to
28
+ // the "default" group; enabled !== true wipes workflow favorites.
29
+ const target = [...ctx.state.apps.values()].find((row) => row.id === body.id);
30
+ if (target) {
31
+ if (typeof body.groupId === "string" && body.groupId)
32
+ target.groupId = body.groupId;
33
+ if (typeof body.name === "string" && body.name)
34
+ target.name = body.name;
35
+ }
36
+ return null;
37
+ }
38
+ case "/api/api/app/apppackage/delete":
39
+ ctx.state.apps.delete(String(q.appCode));
40
+ return null;
41
+ default:
42
+ return NOT_HANDLED;
43
+ }
44
+ };
45
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1,594 @@
1
+ import { businessRefusal, NOT_HANDLED, rawResponse } from "./state.js";
2
+ import { decryptBizServiceMethodConfigJson } from "omc-core";
3
+ import { buildZip, readZipEntries, zipEntryByBasename } from "../zip.js";
4
+ /**
5
+ * B2-registration lane: business integration service registry. Twin of
6
+ * src/lanes/b2-registration.ts. Ticket 35 fidelity: paths match ENDPOINTS
7
+ * exactly (`/api/api/bizservice/service/*`, `/api/api/bizservice/method/*`),
8
+ * service rows carry id/code/name/adapterCode (the delete path resolves the id
9
+ * via get_by_code first), the method-create body keys are {serviceCode,code,
10
+ * name,configJson?,inputParametersJson?,outputParametersJson?} and the declared
11
+ * parameter face round-trips through list_by_service_code, an empty
12
+ * adapterCode is refused 500005 (live 2026-09-12), and a missing service
13
+ * answers the live 500001 envelope instead of errcode 0 + null data.
14
+ *
15
+ * Ticket 92 fidelity: the platform's method-config AES entry is simulated —
16
+ * BizServiceMethodController create/create_list/update decrypt configJson
17
+ * (AES-128-ECB, static key) and persist the PLAINTEXT; a plaintext payload
18
+ * silently decrypts to NULL and is stored NULL. Service configJson stays
19
+ * plaintext on both the write and the read face (BizServiceController).
20
+ */
21
+ function decryptMethodConfig(configJson) {
22
+ if (typeof configJson !== "string" || !configJson)
23
+ return null;
24
+ try {
25
+ const plain = decryptBizServiceMethodConfigJson(configJson);
26
+ JSON.parse(plain);
27
+ return plain;
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ /** Parse a parameters column into the transient model-array readback face. */
34
+ function parseParamArray(json) {
35
+ if (!json)
36
+ return [];
37
+ try {
38
+ const parsed = JSON.parse(json);
39
+ return Array.isArray(parsed) ? parsed : [];
40
+ }
41
+ catch {
42
+ return [];
43
+ }
44
+ }
45
+ /**
46
+ * BizServiceMethodModel.setXxxParameterModels fidelity: when the update body
47
+ * carries the transient model array, the setter re-serializes it (dropping
48
+ * entries without bizPropertyType/externParameterType) and overwrites the
49
+ * JSON column. Returns the overwritten string, or undefined when the body
50
+ * carries no such transient key (the plain column string then applies).
51
+ */
52
+ function applyTransientParameterOverwrite(body, key) {
53
+ if (!Array.isArray(body[key]))
54
+ return undefined;
55
+ const kept = body[key].filter((entry) => entry.bizPropertyType != null || entry.externParameterType != null);
56
+ return JSON.stringify(kept);
57
+ }
58
+ export const b2Route = (ctx, input) => {
59
+ const q = input.query ?? {};
60
+ const body = (input.body ?? {});
61
+ switch (input.path) {
62
+ case "/api/api/bizservice/category/list":
63
+ // Live: the list face prepends the virtual「全部」row with an empty id.
64
+ return [{ id: "", name: "全部" }, ...ctx.state.categories];
65
+ case "/api/api/bizservice/category/create": {
66
+ // Live: category/create {name} (BizServiceCategoryController.java:43-176).
67
+ const name = String(body.name ?? "");
68
+ if (!name.trim())
69
+ return businessRefusal(500005, "分类名称不能为空");
70
+ if (!ctx.state.categories.some((row) => row.name === name))
71
+ ctx.state.categories.push({ id: `fakecat_${name}`, name });
72
+ return null;
73
+ }
74
+ case "/api/api/bizservice/category/update": {
75
+ // Live (BizServiceCategoryController.java:109-140 +
76
+ // BizServiceCategoryServiceImpl.save:54-63): a missing id answers
77
+ // 501052; a duplicate name answers 501004 业务服务目录名称已存在.
78
+ const category = ctx.state.categories.find((row) => row.id === body.id);
79
+ if (!category)
80
+ return businessRefusal(501052, "第三方数据源服务目录不存在");
81
+ const name = String(body.name ?? "");
82
+ if (!name.trim())
83
+ return businessRefusal(500005, "分类名称不能为空");
84
+ if (ctx.state.categories.some((row) => row.name === name && row.id !== category.id))
85
+ return businessRefusal(501004, "业务服务目录名称已存在");
86
+ category.name = name;
87
+ return null;
88
+ }
89
+ case "/api/api/bizservice/category/delete": {
90
+ // Live (BizServiceCategoryController.java:142-165 +
91
+ // BizServiceFacadeImpl.java:196-207): a category still holding
92
+ // services answers 501003 — blocked, no cascade.
93
+ const index = ctx.state.categories.findIndex((row) => row.id === q.id);
94
+ if (index === -1)
95
+ return businessRefusal(501052, "第三方数据源服务目录不存在");
96
+ const referenced = [...ctx.state.services.values()].some((service) => service.serviceCategoryId === q.id);
97
+ if (referenced)
98
+ return businessRefusal(501003, "业务服务目录下存在集成服务,不可删除!");
99
+ ctx.state.categories.splice(index, 1);
100
+ return null;
101
+ }
102
+ case "/api/api/bizservice/dbconnpool/list": {
103
+ // Ticket 104 platform fidelity: the raw list face does NOT mask the
104
+ // password column (BizDatabaseConnectionPoolController.list only
105
+ // decorates creator names) — the TOOL layer strips it from readback.
106
+ return [...ctx.state.dbConnPools.values()].map((pool) => ({ ...pool }));
107
+ }
108
+ case "/api/api/bizservice/dbconnpool/create": {
109
+ // Live fidelity (BizDatabaseConnectionPoolServiceImpl.save:103-130 +
110
+ // ErrorCode.java:615-621): name empty → 502004, code invalid → 502003,
111
+ // duplicate code → 502002; datasourceType defaults to "DATABASE".
112
+ const code = String(body.code ?? "");
113
+ const name = String(body.name ?? "");
114
+ if (!name.trim())
115
+ return businessRefusal(502004, "数据库连接池名称不能为空");
116
+ if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(code))
117
+ return businessRefusal(502003, `数据库连接池编码无效:${code}`);
118
+ if (ctx.state.dbConnPools.has(code))
119
+ return businessRefusal(502002, `数据库连接池已存在:${code}`);
120
+ if (typeof body.jdbcUrl !== "string" || !body.jdbcUrl)
121
+ return businessRefusal(500005, "jdbcUrl 不能为空");
122
+ ctx.state.dbConnPools.set(code, {
123
+ id: `fakepool_${code}`,
124
+ code,
125
+ name,
126
+ description: typeof body.description === "string" ? body.description : undefined,
127
+ databaseType: String(body.databaseType ?? "MySQL"),
128
+ datasourceType: String(body.datasourceType ?? "DATABASE"),
129
+ jdbcUrl: body.jdbcUrl,
130
+ username: typeof body.username === "string" ? body.username : undefined,
131
+ password: typeof body.password === "string" ? body.password : undefined,
132
+ externInfo: typeof body.externInfo === "string" ? body.externInfo : undefined,
133
+ });
134
+ return null;
135
+ }
136
+ case "/api/api/bizservice/dbconnpool/update": {
137
+ // Live fidelity (BizDatabaseConnectionPoolServiceImpl.update:135-168):
138
+ // a FULL-ROW overwrite — only createdTime is carried from the stored
139
+ // row; the tool must echo untouched columns or they are wiped.
140
+ const pool = [...ctx.state.dbConnPools.values()].find((row) => row.id === body.id || row.code === body.code);
141
+ if (!pool)
142
+ return businessRefusal(502001, "数据库连接池不存在");
143
+ if (typeof body.name === "string" && !body.name.trim())
144
+ return businessRefusal(502004, "数据库连接池名称不能为空");
145
+ pool.name = typeof body.name === "string" && body.name ? body.name : pool.name;
146
+ pool.description = body.description !== undefined ? (typeof body.description === "string" ? body.description : undefined) : pool.description;
147
+ pool.databaseType = typeof body.databaseType === "string" && body.databaseType ? body.databaseType : pool.databaseType;
148
+ pool.datasourceType = typeof body.datasourceType === "string" && body.datasourceType ? body.datasourceType : "DATABASE";
149
+ pool.jdbcUrl = typeof body.jdbcUrl === "string" && body.jdbcUrl ? body.jdbcUrl : pool.jdbcUrl;
150
+ pool.username = body.username !== undefined ? (typeof body.username === "string" ? body.username : undefined) : pool.username;
151
+ pool.password = body.password !== undefined ? (typeof body.password === "string" ? body.password : undefined) : pool.password;
152
+ pool.externInfo = body.externInfo !== undefined ? (typeof body.externInfo === "string" ? body.externInfo : undefined) : pool.externInfo;
153
+ return { ...pool };
154
+ }
155
+ case "/api/api/bizservice/dbconnpool/delete": {
156
+ // Live fidelity (BizServiceFacadeImpl delete:173-189): a pool referenced
157
+ // by any service config's dbConnPoolCode answers 502006 — blocked.
158
+ const entry = [...ctx.state.dbConnPools.values()].find((row) => row.id === q.id || row.code === q.id);
159
+ if (!entry)
160
+ return businessRefusal(502001, "数据库连接池不存在");
161
+ const referenced = [...ctx.state.services.values()].some((service) => {
162
+ if (typeof service.configJson !== "string")
163
+ return false;
164
+ try {
165
+ const config = JSON.parse(service.configJson);
166
+ return Array.isArray(config.commonlist) && config.commonlist.some((item) => item.code === "databaseConnectionPoolCode" && item.value === entry.code);
167
+ }
168
+ catch {
169
+ return false;
170
+ }
171
+ });
172
+ if (referenced)
173
+ return businessRefusal(502006, `数据库连接池已经被业务服务引用:${entry.code}`);
174
+ ctx.state.dbConnPools.delete(entry.code);
175
+ return null;
176
+ }
177
+ case "/api/api/bizservice/service/adapter_config": {
178
+ // Ticket 105 platform fidelity (BizServiceController.java:329-344 +
179
+ // BizServiceFacadeImpl.java:322-328): a JSON STRING of the registered
180
+ // protocol adapters, each {code,name,config:{authList,commonList,
181
+ // serviceActionList,methodActionList}} (AdapterConfigParam.java:16-181).
182
+ // Faces mirror RestProtocolAdapterServiceImpl.java:50-90 (RESTFUL) and
183
+ // DataSqlProtocolAdapterServiceImpl.java:43-57 (DATA_SQL).
184
+ const adapterConfig = [
185
+ {
186
+ code: "RESTFUL",
187
+ name: "RESTful服务",
188
+ config: {
189
+ authList: [
190
+ { code: "basic", name: "Basic Authentication", description: "根据username、password来进行鉴权", parameterlist: [
191
+ { code: "username", name: "用户名", description: "请输入用户名", required: true },
192
+ { code: "password", name: "密码", description: "请输入密码", required: true },
193
+ ] },
194
+ { code: "oauth_password", name: "OAuth2 Password", description: "根据客户端凭证,以及username、password来进行鉴权", parameterlist: [
195
+ { code: "client_id", name: "客户端ID", description: "", required: true },
196
+ { code: "client_secret", name: "客户端凭证", description: "", required: true },
197
+ { code: "username", name: "用户名", description: "请输入用户名", required: true },
198
+ { code: "password", name: "密码", description: "请输入密码", required: true },
199
+ { code: "oauth_path", name: "获取token地址", description: "", required: true },
200
+ ] },
201
+ { code: "oauth_client", name: "OAuth2 Client Credentials", description: "根据客户端凭证进行鉴权", parameterlist: [
202
+ { code: "client_id", name: "客户端ID", description: "", required: true },
203
+ { code: "client_secret", name: "客户端凭证", description: "", required: true },
204
+ { code: "oauth_path", name: "获取token地址", description: "", required: true },
205
+ ] },
206
+ { code: "customer", name: "无鉴权", description: "提供url即可", parameterlist: [] },
207
+ ],
208
+ commonList: [{ code: "url", name: "地址URL", description: "HTTP请求路径", required: true }],
209
+ serviceActionList: [
210
+ { code: "edit", name: "编辑", description: "编辑", visible: true },
211
+ { code: "add", name: "新增", description: "新增", visible: true },
212
+ { code: "sync", name: "同步", description: "同步", visible: false },
213
+ { code: "delete", name: "删除", description: "删除", visible: true },
214
+ ],
215
+ methodActionList: [
216
+ { code: "edit", name: "编辑", description: "编辑", visible: true },
217
+ { code: "run", name: "运行", description: "运行", visible: true },
218
+ { code: "quote", name: "查看引用", description: "查看引用", visible: true },
219
+ { code: "delete", name: "删除", description: "删除", visible: true },
220
+ ],
221
+ },
222
+ },
223
+ {
224
+ code: "DATA_SQL",
225
+ name: "数据服务SQL",
226
+ config: {
227
+ authList: [],
228
+ commonList: [{ code: "databaseConnectionPoolCode", name: "数据库连接池编码", description: "数据库连接池", required: true }],
229
+ serviceActionList: [
230
+ { code: "edit", name: "编辑", description: "编辑", visible: true },
231
+ { code: "add", name: "新增", description: "新增", visible: true },
232
+ { code: "sync", name: "同步", description: "同步", visible: false },
233
+ { code: "delete", name: "删除", description: "删除", visible: true },
234
+ ],
235
+ methodActionList: [
236
+ { code: "edit", name: "编辑", description: "编辑", visible: true },
237
+ { code: "run", name: "运行", description: "运行", visible: true },
238
+ { code: "quote", name: "查看引用", description: "查看引用", visible: true },
239
+ { code: "delete", name: "删除", description: "删除", visible: true },
240
+ ],
241
+ },
242
+ },
243
+ ];
244
+ return JSON.stringify(adapterConfig);
245
+ }
246
+ case "/api/api/bizservice/method/get_tablename_list": {
247
+ // Ticket 105 platform fidelity (BizServiceMethodController.java:503-537):
248
+ // service must exist (500001) AND be DATA_TABLE (501010
249
+ // 业务服务不为DataTable类型 — the research doc's "DATA_SQL discovery"
250
+ // reading was wrong); answers the pool's table names.
251
+ const service = ctx.state.services.get(q.serviceCode);
252
+ if (!service)
253
+ return businessRefusal(500001, "业务服务不存在");
254
+ if (service.adapterCode !== "DATA_TABLE")
255
+ return businessRefusal(501010, "业务服务不为DataTable类型");
256
+ return ["h_biz_schema", "h_biz_service"];
257
+ }
258
+ case "/api/api/bizservice/method/get_table_column": {
259
+ // Ticket 105 platform fidelity (BizServiceMethodController.java:538-560
260
+ // + BizServiceFacadeImpl.getDataTableSql:889-910): answers GENERATED SQL
261
+ // TEMPLATES (DataTableSqlModel{sql,sqlType}, SqlType
262
+ // SELECT/UPDATE/DELETE/INSERT/UPSERT) — not column metadata.
263
+ const service = ctx.state.services.get(q.serviceCode);
264
+ if (!service)
265
+ return businessRefusal(500001, "业务服务不存在");
266
+ if (service.adapterCode !== "DATA_TABLE")
267
+ return businessRefusal(501010, "业务服务不为DataTable类型");
268
+ const table = String(q.tableName ?? "");
269
+ return [
270
+ { sql: `SELECT * FROM ${table}`, sqlType: "SELECT" },
271
+ { sql: `INSERT INTO ${table} (...) VALUES (...)`, sqlType: "INSERT" },
272
+ { sql: `UPDATE ${table} SET ... WHERE ...`, sqlType: "UPDATE" },
273
+ { sql: `DELETE FROM ${table} WHERE ...`, sqlType: "DELETE" },
274
+ ];
275
+ }
276
+ case "/api/api/bizservice/service/create": {
277
+ // Live: create with an empty adapterCode → 500005 业务服务关联适配器编码为空.
278
+ if (typeof body.adapterCode !== "string" || !body.adapterCode)
279
+ return businessRefusal(500005, "业务服务关联适配器编码为空");
280
+ const code = String(body.code ?? "");
281
+ ctx.state.services.set(code, {
282
+ id: `fakesvc_${code}`,
283
+ code,
284
+ serviceCode: code,
285
+ name: body.name,
286
+ adapterCode: body.adapterCode,
287
+ serviceCategoryId: typeof body.serviceCategoryId === "string" ? body.serviceCategoryId : undefined,
288
+ configJson: typeof body.configJson === "string" ? body.configJson : undefined,
289
+ methods: [],
290
+ });
291
+ return null;
292
+ }
293
+ case "/api/api/bizservice/service/update": {
294
+ // Live: update requires the row id (BizServiceController.java:103-128);
295
+ // the tool always resolves it via get_by_code first. Fidelity note
296
+ // (live incident 2026-09-14): the platform update is a FULL-ROW
297
+ // overwrite — body keys that are absent/typed-out null the stored
298
+ // column. The fake mirrors that overwrite semantics (no merge), so an
299
+ // update body that forgets configJson fails the readback here exactly
300
+ // like it would on the platform.
301
+ if (typeof body.id !== "string" || !body.id)
302
+ return businessRefusal(500001, "业务服务不存在");
303
+ let updated = null;
304
+ for (const service of ctx.state.services.values()) {
305
+ if (service.id === body.id) {
306
+ service.name = body.name;
307
+ service.configJson = typeof body.configJson === "string" ? body.configJson : null;
308
+ service.serviceCategoryId = typeof body.serviceCategoryId === "string" ? body.serviceCategoryId : undefined;
309
+ service.shared = body.shared === true;
310
+ service.userIdList = Array.isArray(body.userIdList) ? body.userIdList : [];
311
+ service.modifierList = Array.isArray(body.modifierList) ? body.modifierList : [];
312
+ updated = { id: service.id, code: service.code, name: service.name, adapterCode: service.adapterCode ?? "", ...(service.configJson !== undefined && service.configJson !== null ? { configJson: service.configJson } : {}) };
313
+ }
314
+ }
315
+ return updated;
316
+ }
317
+ case "/api/api/bizservice/service/get_by_code": {
318
+ const service = ctx.state.services.get(q.serviceCode);
319
+ // Live: get_by_code throws PortalException 500001 业务服务不存在.
320
+ return service
321
+ ? { id: service.id, code: service.code, name: service.name, adapterCode: service.adapterCode ?? "", ...(service.serviceCategoryId !== undefined ? { serviceCategoryId: service.serviceCategoryId } : {}), ...(service.configJson !== undefined ? { configJson: service.configJson } : {}) }
322
+ : businessRefusal(500001, "业务服务不存在");
323
+ }
324
+ case "/api/api/bizservice/method/create": {
325
+ const service = ctx.state.services.get(String(body.serviceCode));
326
+ if (service)
327
+ service.methods.push({
328
+ id: `fakemethod_${String(body.serviceCode)}_${String(body.code ?? "")}`,
329
+ code: String(body.code ?? ""),
330
+ name: body.name,
331
+ // Platform entry: decrypt then persist plaintext; NULL on failure.
332
+ configJson: decryptMethodConfig(body.configJson),
333
+ inputParametersJson: typeof body.inputParametersJson === "string" ? body.inputParametersJson : undefined,
334
+ outputParametersJson: typeof body.outputParametersJson === "string" ? body.outputParametersJson : undefined,
335
+ });
336
+ return null;
337
+ }
338
+ case "/api/api/bizservice/method/get": {
339
+ const method = [...ctx.state.services.values()].flatMap((service) => service.methods).find((candidate) => candidate.id === q.id);
340
+ return method ? { ...method, configJson: method.configJson ?? null, inputParametersJson: method.inputParametersJson ?? null, outputParametersJson: method.outputParametersJson ?? null } : businessRefusal(500001, "业务方法不存在");
341
+ }
342
+ case "/api/api/bizservice/method/update": {
343
+ const service = [...ctx.state.services.values()].find((candidate) => candidate.methods.some((method) => method.id === body.id));
344
+ const method = service?.methods.find((candidate) => candidate.id === body.id);
345
+ if (!method)
346
+ return businessRefusal(500001, "业务方法不存在");
347
+ method.code = String(body.code ?? method.code);
348
+ method.name = body.name;
349
+ if (body.configJson !== undefined)
350
+ method.configJson = decryptMethodConfig(body.configJson);
351
+ // Live 2026-09-15 fidelity (BizServiceMethodModel.java:88-146): the
352
+ // transient inputParameterModels/outputParameterModels setters
353
+ // RE-SERIALIZE and overwrite the JSON columns, filtering out entries
354
+ // without bizPropertyType/externParameterType (an untyped face becomes
355
+ // "[]"). The plain *ParametersJson string setters persist verbatim.
356
+ method.inputParametersJson = applyTransientParameterOverwrite(body, "inputParameterModels") ?? (typeof body.inputParametersJson === "string" ? body.inputParametersJson : method.inputParametersJson);
357
+ method.outputParametersJson = applyTransientParameterOverwrite(body, "outputParameterModels") ?? (typeof body.outputParametersJson === "string" ? body.outputParametersJson : method.outputParametersJson);
358
+ return null;
359
+ }
360
+ case "/api/api/bizservice/method/delete": {
361
+ // Live fidelity: delete keys on the row id (comma-separated on the
362
+ // platform face); a method still referenced by a business-rule mapping
363
+ // answers 500108 业务方法已绑定默认方法,不可删除.
364
+ let removed = false;
365
+ for (const targetId of String(q.id ?? "").split(",").filter(Boolean)) {
366
+ for (const service of ctx.state.services.values()) {
367
+ const method = service.methods.find((candidate) => candidate.id === targetId);
368
+ if (!method)
369
+ continue;
370
+ if (method.referenced)
371
+ return businessRefusal(500108, "业务方法已绑定默认方法,不可删除");
372
+ service.methods = service.methods.filter((candidate) => candidate.id !== targetId);
373
+ removed = true;
374
+ }
375
+ }
376
+ if (!removed)
377
+ return businessRefusal(500001, "业务方法不存在");
378
+ return null;
379
+ }
380
+ case "/api/api/bizservice/method/list_by_service_code":
381
+ return (ctx.state.services.get(q.serviceCode)?.methods ?? []).map((method) => ({
382
+ id: method.id,
383
+ code: method.code,
384
+ name: method.name,
385
+ serviceCode: q.serviceCode,
386
+ configJson: method.configJson ?? null,
387
+ inputParametersJson: method.inputParametersJson ?? null,
388
+ outputParametersJson: method.outputParametersJson ?? null,
389
+ // Live readback also parses the columns into the transient model
390
+ // arrays (inputParameterModels/outputParameterModels on the model).
391
+ inputParameterModels: parseParamArray(method.inputParametersJson),
392
+ outputParameterModels: parseParamArray(method.outputParametersJson),
393
+ referenced: method.referenced === true,
394
+ }));
395
+ case "/api/api/bizservice/service/delete": {
396
+ for (const [code, service] of ctx.state.services) {
397
+ if (service.id === q.id || code === q.id)
398
+ ctx.state.services.delete(code);
399
+ }
400
+ return null;
401
+ }
402
+ // -----------------------------------------------------------------------
403
+ // Ticket 106 — the IE (import/export) face
404
+ // -----------------------------------------------------------------------
405
+ case "/api/api/runtime/query/upload_file": {
406
+ // QueryRuntimeController.java:2414-2428: multipart field `file`, the
407
+ // body answers a PLAIN STRING refId (not an envelope).
408
+ if (!input.multipart)
409
+ return businessRefusal(10005, "非法的参数");
410
+ ctx.state.ieUploadSeq += 1;
411
+ const refId = `fakeref_${ctx.state.ieUploadSeq}`;
412
+ ctx.state.ieUploads.set(refId, Buffer.from(input.multipart.content));
413
+ return rawResponse(refId);
414
+ }
415
+ case "/api/api/bizservice/service/export": {
416
+ // BizServiceIEController.java:110-148 + BizServiceIEFacadeImpl:135-148:
417
+ // the package copies the stored rows VERBATIM (service configJson
418
+ // plaintext incl. auth values; method configJson stored plaintext) and
419
+ // never touches the connection pools. Unknown codes are silently
420
+ // skipped (the tool preflights them away).
421
+ const services = [];
422
+ const methodsMap = {};
423
+ for (const code of (Array.isArray(body.serviceCodes) ? body.serviceCodes : [])) {
424
+ const service = ctx.state.services.get(code);
425
+ if (!service)
426
+ continue;
427
+ services.push({
428
+ code: service.code,
429
+ name: service.name,
430
+ adapterCode: service.adapterCode,
431
+ serviceCategoryId: service.serviceCategoryId,
432
+ configJson: service.configJson ?? null,
433
+ shared: service.shared === true,
434
+ userIdList: service.userIdList ?? [],
435
+ modifierList: service.modifierList ?? [],
436
+ });
437
+ methodsMap[code] = service.methods.map((method) => ({
438
+ code: method.code,
439
+ name: method.name,
440
+ serviceCode: code,
441
+ // Live 2026-09-15 fidelity: the serialized model carries BOTH the
442
+ // transient decoded `config` object and the plaintext `configJson`
443
+ // string column (bizService-<uuid>/manifest.json).
444
+ ...(method.configJson ? { config: JSON.parse(method.configJson), configJson: method.configJson } : { configJson: method.configJson ?? null }),
445
+ inputParametersJson: method.inputParametersJson ?? null,
446
+ outputParametersJson: method.outputParametersJson ?? null,
447
+ referenced: method.referenced === true,
448
+ }));
449
+ }
450
+ const manifest = { systemVersion: "8.6.26", services, methodsMap };
451
+ // Live fidelity: the controller compresses the staging DIRECTORY, so
452
+ // every entry carries a bizService-<uuid>/ prefix.
453
+ const staging = `bizService-${String(Math.random().toFixed(32)).replaceAll(".", "")}`;
454
+ const zip = buildZip([
455
+ { name: `${staging}/manifest.json`, data: Buffer.from(JSON.stringify(manifest), "utf8") },
456
+ { name: `${staging}/version.md`, data: Buffer.from("8.6.26", "utf8") },
457
+ ]);
458
+ return rawResponse(zip);
459
+ }
460
+ case "/api/api/bizservice/service/check":
461
+ case "/api/api/bizservice/service/import": {
462
+ // Both faces consume their OWN uploaded refId (one-shot:
463
+ // FileOperateHelper2.java:43-57 removes the cache entry on download).
464
+ const refId = String(body.fileName ?? "");
465
+ const uploaded = ctx.state.ieUploads.get(refId);
466
+ ctx.state.ieUploads.delete(refId);
467
+ if (uploaded === undefined)
468
+ return businessRefusal(10005, "非法的参数");
469
+ let manifest;
470
+ try {
471
+ const entries = readZipEntries(uploaded);
472
+ const manifestEntry = zipEntryByBasename(entries, "manifest.json");
473
+ if (manifestEntry === undefined)
474
+ throw new Error("no manifest");
475
+ manifest = JSON.parse(manifestEntry.toString("utf8"));
476
+ if (!Array.isArray(manifest.services) || manifest.services.length === 0)
477
+ throw new Error("empty services");
478
+ }
479
+ catch {
480
+ return businessRefusal(10005, "格式校验错误");
481
+ }
482
+ const modifyCodes = (Array.isArray(body.modifyServiceCodes) ? body.modifyServiceCodes : []);
483
+ const overWrite = body.overWrite === true;
484
+ const isCheck = input.path.endsWith("/check");
485
+ if (isCheck) {
486
+ // BizServiceIEFacadeImpl.checkBizService: flag repeated codes, fill
487
+ // serviceName/groupName, validate renames, report misses.
488
+ const reported = [...modifyCodes];
489
+ for (const service of manifest.services) {
490
+ const code = String(service.code ?? "");
491
+ const local = ctx.state.services.get(code);
492
+ if (!local)
493
+ continue;
494
+ const category = ctx.state.categories.find((row) => row.id === local.serviceCategoryId);
495
+ const modify = reported.find((row) => row.oldServiceCode === code);
496
+ if (!modify) {
497
+ reported.push({ serviceName: local.name, groupName: category?.name ?? "", isRepeated: true, oldServiceCode: code });
498
+ }
499
+ else {
500
+ modify.serviceName = local.name;
501
+ modify.groupName = category?.name ?? "";
502
+ const newCode = String(modify.newServiceCode ?? "");
503
+ if (!/^[a-zA-Z][a-zA-Z0-9_]{0,27}$/.test(newCode))
504
+ return businessRefusal(580001, `${newCode}编码命名不合法`);
505
+ modify.isRepeated = ctx.state.services.has(newCode);
506
+ }
507
+ }
508
+ return { fileName: refId, modifyServiceCodes: reported, missedDataSourceCodes: [], missedUserList: [] };
509
+ }
510
+ // import (BizServiceIEFacadeImpl.importBizServices, @Transactional)
511
+ if (overWrite)
512
+ modifyCodes.length = 0;
513
+ for (const service of manifest.services) {
514
+ const packagedCode = String(service.code ?? "");
515
+ if (typeof body.groupId === "string" && body.groupId)
516
+ service.serviceCategoryId = body.groupId;
517
+ const local = ctx.state.services.get(packagedCode);
518
+ const modify = modifyCodes.find((row) => row.oldServiceCode === packagedCode);
519
+ if (local != null && modify == null && !overWrite)
520
+ return businessRefusal(309008, `${packagedCode}服务编码重复未解决`);
521
+ const finalCode = local != null && (overWrite || modify == null) ? packagedCode : String(modify?.newServiceCode ?? packagedCode);
522
+ const methods = manifest.methodsMap?.[packagedCode] ?? [];
523
+ if (local != null && overWrite) {
524
+ local.name = service.name;
525
+ local.adapterCode = service.adapterCode;
526
+ local.serviceCategoryId = service.serviceCategoryId;
527
+ local.configJson = service.configJson;
528
+ }
529
+ else {
530
+ ctx.state.services.set(finalCode, {
531
+ id: `fakesvc_${finalCode}`,
532
+ code: finalCode,
533
+ serviceCode: finalCode,
534
+ name: service.name,
535
+ adapterCode: service.adapterCode,
536
+ serviceCategoryId: service.serviceCategoryId,
537
+ configJson: service.configJson,
538
+ methods: [],
539
+ });
540
+ for (const method of methods)
541
+ method.serviceCode = finalCode;
542
+ }
543
+ const target = ctx.state.services.get(finalCode);
544
+ for (const method of methods) {
545
+ const existing = target.methods.find((row) => row.code === method.code);
546
+ const row = {
547
+ id: existing?.id ?? `fakemethod_${finalCode}_${method.code}`,
548
+ code: method.code,
549
+ name: method.name,
550
+ // The IE path persists the package verbatim — no AES anywhere.
551
+ configJson: method.configJson ?? null,
552
+ inputParametersJson: method.inputParametersJson ?? undefined,
553
+ outputParametersJson: method.outputParametersJson ?? undefined,
554
+ referenced: false,
555
+ };
556
+ if (existing)
557
+ Object.assign(existing, row);
558
+ else
559
+ target.methods.push(row);
560
+ }
561
+ }
562
+ return { fileName: refId, modifyServiceCodes: [], missedDataSourceCodes: [], missedUserList: [] };
563
+ }
564
+ case "/api/api/bizservice/method/service_test": {
565
+ // Ticket 107 (BizServiceMethodController.java:398-412 +
566
+ // BizServiceFacadeImpl.java:426-464): resolve the method, dispatch to
567
+ // the (simulated) adapter runtime, answer the INNER result map
568
+ // {success,msg,data}. ZERO config writes — the route only reads the
569
+ // stored rows (the tool's readback proves the config survived).
570
+ const service = ctx.state.services.get(String(body.serviceCode ?? ""));
571
+ if (!service)
572
+ return businessRefusal(500001, "业务服务不存在");
573
+ const method = service.methods.find((candidate) => candidate.code === body.code);
574
+ if (!method)
575
+ return businessRefusal(500001, "业务方法不存在");
576
+ const args = (body.testInputParametersMap ?? {});
577
+ // Test affordance (dogfood round20): a method whose code ends in
578
+ // `_errenv` simulates a same-instance RESTFUL target that answers HTTP
579
+ // 200 with a CloudPivot error envelope (`{errcode:50000,...}`, e.g.
580
+ // "corpId can not be blank") — the platform still reports success:true.
581
+ if (method.code.endsWith("_errenv")) {
582
+ return { success: true, msg: "", data: { errcode: 50000, errmsg: "corpId can not be blank" } };
583
+ }
584
+ return {
585
+ success: true,
586
+ msg: "",
587
+ data: { adapterCode: service.adapterCode, methodCode: method.code, received: args },
588
+ };
589
+ }
590
+ default:
591
+ return NOT_HANDLED;
592
+ }
593
+ };
594
+ //# sourceMappingURL=b2-registration.js.map