@appaflytech/zeyrek-ai-mcp 0.0.36 → 0.0.40

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 (210) hide show
  1. package/.env.example +15 -0
  2. package/README.md +5 -5
  3. package/dist/config.js +37 -0
  4. package/dist/index.js +2 -0
  5. package/dist/knowledge/access.js +802 -0
  6. package/dist/knowledge/backendConstraints.js +18 -1
  7. package/dist/knowledge/case.js +41 -0
  8. package/dist/knowledge/componentCatalog.js +524 -53
  9. package/dist/knowledge/componentEvents.js +132 -7
  10. package/dist/knowledge/connectDataBind.js +851 -0
  11. package/dist/knowledge/connectDataDefinition.js +344 -0
  12. package/dist/knowledge/connectDataExecute.js +93 -0
  13. package/dist/knowledge/connectDataRules.js +128 -0
  14. package/dist/knowledge/decision.js +121 -0
  15. package/dist/knowledge/expressionPrelude.js +60 -0
  16. package/dist/knowledge/layoutSummary.js +140 -2
  17. package/dist/knowledge/microserviceParameters.js +58 -0
  18. package/dist/knowledge/mining.js +94 -0
  19. package/dist/knowledge/mobileComponentCatalog.js +876 -0
  20. package/dist/knowledge/mobileComponentEvents.js +259 -0
  21. package/dist/knowledge/mobileStore.js +396 -0
  22. package/dist/knowledge/normalize.js +240 -72
  23. package/dist/knowledge/outlines.js +36 -0
  24. package/dist/knowledge/platform/lint.js +385 -0
  25. package/dist/knowledge/platform/mobile.js +142 -0
  26. package/dist/knowledge/platform/mobileLayoutLint.js +324 -0
  27. package/dist/knowledge/platform/mobileRules.js +231 -0
  28. package/dist/knowledge/platform/mobileSpacing.js +82 -0
  29. package/dist/knowledge/platform/mobileTabs.js +108 -0
  30. package/dist/knowledge/platform/nativeBoxModel.js +44 -0
  31. package/dist/knowledge/platform/profile.js +25 -0
  32. package/dist/knowledge/platform/resolve.js +122 -0
  33. package/dist/knowledge/platform/web.js +39 -0
  34. package/dist/knowledge/policy.js +18 -4
  35. package/dist/knowledge/process.js +510 -0
  36. package/dist/knowledge/reservedTables.js +79 -0
  37. package/dist/knowledge/rowFilter.js +43 -0
  38. package/dist/knowledge/rules.js +485 -22
  39. package/dist/knowledge/schemaGuard.js +11 -0
  40. package/dist/knowledge/styleResolve.js +64 -10
  41. package/dist/knowledge/styleResolveNative.js +529 -0
  42. package/dist/knowledge/themeResolve.js +3 -3
  43. package/dist/observability/trace.js +13 -5
  44. package/dist/platform/argumentAliases.js +128 -0
  45. package/dist/platform/bodyFile.js +103 -0
  46. package/dist/platform/capture/actions.js +398 -0
  47. package/dist/platform/capture/browser.js +1 -0
  48. package/dist/platform/capture/capture.js +683 -0
  49. package/dist/platform/capture/console-log.js +122 -0
  50. package/dist/platform/capture/index.js +11 -0
  51. package/dist/platform/capture/readiness.js +103 -0
  52. package/dist/platform/capture/summary.js +77 -0
  53. package/dist/platform/capture/target.js +5 -0
  54. package/dist/platform/capture/targets/studio-mobile.js +15 -0
  55. package/dist/platform/capture/targets/studio-web.js +5 -0
  56. package/dist/platform/diagnostics.js +10 -4
  57. package/dist/platform/domSummary.js +1 -1
  58. package/dist/platform/errors.js +163 -10
  59. package/dist/platform/http.js +29 -8
  60. package/dist/platform/impersonation.js +45 -0
  61. package/dist/platform/jsonArray.js +24 -0
  62. package/dist/platform/responses.js +39 -5
  63. package/dist/platform/schemaReadMemory.js +60 -0
  64. package/dist/platform/secrets.js +68 -2
  65. package/dist/platform/strictArguments.js +68 -12
  66. package/dist/platform/tokenVault.js +187 -4
  67. package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
  68. package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
  69. package/dist/resources/examples/bpm-satinalma.json +376 -0
  70. package/dist/resources/examples/connect-data-convert-verify.json +107 -0
  71. package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
  72. package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
  73. package/dist/resources/examples/connect-data-query-params.json +50 -0
  74. package/dist/resources/examples/connect-data-table-entity.json +147 -0
  75. package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
  76. package/dist/resources/examples/middleware-authenticate.json +1 -1
  77. package/dist/resources/examples/mobile-app-shell-drawer.json +244 -0
  78. package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
  79. package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
  80. package/dist/resources/examples/mobile-form-screen.json +365 -0
  81. package/dist/resources/examples/mobile-list-detail-form.json +703 -0
  82. package/dist/resources/examples/page-detail-profile.json +2 -1
  83. package/dist/resources/examples/page-dynamic-form.json +3 -2
  84. package/dist/resources/examples/page-flex-sections.json +1 -1
  85. package/dist/resources/examples/page-html-embed.json +1 -1
  86. package/dist/resources/examples/workflow-basic.json +8 -2
  87. package/dist/resources/guides/access.md +810 -0
  88. package/dist/resources/guides/bpm.md +691 -0
  89. package/dist/resources/guides/case.md +153 -0
  90. package/dist/resources/guides/component.md +92 -5
  91. package/dist/resources/guides/decision.md +185 -0
  92. package/dist/resources/guides/entity.md +50 -8
  93. package/dist/resources/guides/expressions.md +14 -5
  94. package/dist/resources/guides/functions.md +1 -1
  95. package/dist/resources/guides/getting-started.md +38 -1
  96. package/dist/resources/guides/layout.md +11 -7
  97. package/dist/resources/guides/merge.md +42 -7
  98. package/dist/resources/guides/microservice.md +88 -3
  99. package/dist/resources/guides/middleware.md +69 -5
  100. package/dist/resources/guides/mobile-layout.md +193 -0
  101. package/dist/resources/guides/mobile-publish.md +148 -0
  102. package/dist/resources/guides/mobile.md +565 -0
  103. package/dist/resources/guides/page.md +469 -82
  104. package/dist/resources/guides/theme.md +8 -2
  105. package/dist/resources/guides/translation.md +1 -1
  106. package/dist/resources/guides/workflow.md +58 -1
  107. package/dist/resources/read.js +41 -0
  108. package/dist/resources/rules.js +11 -5
  109. package/dist/resources/schema-fingerprint.json +7 -5
  110. package/dist/resources/schemas/AccessExplanationDto.json +144 -0
  111. package/dist/resources/schemas/AccessMatrixDto.json +170 -0
  112. package/dist/resources/schemas/AccessPermissionDto.json +57 -0
  113. package/dist/resources/schemas/AccessPolicyDto.json +223 -0
  114. package/dist/resources/schemas/AccessResourceDto.json +86 -0
  115. package/dist/resources/schemas/AppSessionDto.json +53 -0
  116. package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
  117. package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
  118. package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
  119. package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
  120. package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
  121. package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
  122. package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
  123. package/dist/resources/schemas/CreateEntityCommand.json +118 -0
  124. package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
  125. package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
  126. package/dist/resources/schemas/CreateQueryCommand.json +4 -0
  127. package/dist/resources/schemas/CreateWorkflowCommand.json +146 -1
  128. package/dist/resources/schemas/DataJobDto.json +6 -1
  129. package/dist/resources/schemas/DecisionDto.json +302 -0
  130. package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
  131. package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
  132. package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
  133. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +24 -0
  134. package/dist/resources/schemas/MicroserviceDto.json +15 -3
  135. package/dist/resources/schemas/OrganizationProjectDto.json +343 -0
  136. package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
  137. package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
  138. package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
  139. package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
  140. package/dist/resources/schemas/PreviewWorkflowCommand.json +25 -1
  141. package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
  142. package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
  143. package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
  144. package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
  145. package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
  146. package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
  147. package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
  148. package/dist/resources/schemas/StartMobileBuildCommand.json +54 -0
  149. package/dist/resources/schemas/StepDto.json +25 -1
  150. package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
  151. package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
  152. package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
  153. package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
  154. package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
  155. package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
  156. package/dist/resources/schemas/UpdateViewCommand.json +0 -2
  157. package/dist/resources/schemas/UpdateWorkflowCommand.json +146 -1
  158. package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +333 -0
  159. package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
  160. package/dist/server.js +31 -1
  161. package/dist/tools/access.js +369 -2
  162. package/dist/tools/accessOperations.js +135 -0
  163. package/dist/tools/accessPolicies.js +170 -0
  164. package/dist/tools/accessRuntime.js +203 -0
  165. package/dist/tools/applications.js +63 -3
  166. package/dist/tools/bpm.js +343 -0
  167. package/dist/tools/bpmGovernance.js +266 -0
  168. package/dist/tools/bpmOperations.js +230 -0
  169. package/dist/tools/bpmReporting.js +154 -0
  170. package/dist/tools/bpmRouting.js +150 -0
  171. package/dist/tools/bpmRuntime.js +396 -0
  172. package/dist/tools/case.js +178 -0
  173. package/dist/tools/caseRuntime.js +89 -0
  174. package/dist/tools/common.js +19 -6
  175. package/dist/tools/components.js +118 -14
  176. package/dist/tools/connectData.js +450 -0
  177. package/dist/tools/connectors.js +193 -0
  178. package/dist/tools/datasources.js +3 -3
  179. package/dist/tools/decisions.js +206 -0
  180. package/dist/tools/dependencies.js +1 -1
  181. package/dist/tools/deployments.js +38 -3
  182. package/dist/tools/designSchemaPatch.js +34 -9
  183. package/dist/tools/dtos.js +36 -5
  184. package/dist/tools/entities.js +79 -12
  185. package/dist/tools/environments.js +1 -1
  186. package/dist/tools/externalComponents.js +26 -5
  187. package/dist/tools/flows.js +119 -13
  188. package/dist/tools/folders.js +1 -1
  189. package/dist/tools/imports.js +5 -2
  190. package/dist/tools/layouts.js +50 -17
  191. package/dist/tools/merges.js +2 -2
  192. package/dist/tools/microservices.js +77 -25
  193. package/dist/tools/mobile.js +280 -0
  194. package/dist/tools/mobileStore.js +572 -0
  195. package/dist/tools/modules.js +48 -0
  196. package/dist/tools/organizationStructure.js +137 -0
  197. package/dist/tools/pages.js +467 -100
  198. package/dist/tools/platformInfo.js +28 -8
  199. package/dist/tools/profileWrite.js +92 -0
  200. package/dist/tools/projects.js +71 -9
  201. package/dist/tools/rows.js +69 -13
  202. package/dist/tools/schemaOps.js +61 -1
  203. package/dist/tools/screenWrite.js +36 -7
  204. package/dist/tools/screens.js +95 -5
  205. package/dist/tools/templates.js +26 -1
  206. package/dist/tools/translations.js +1 -1
  207. package/dist/tools/variables.js +11 -5
  208. package/dist/tools/workflows.js +10 -5
  209. package/package.json +2 -2
  210. package/dist/platform/screenshot.js +0 -1162
@@ -0,0 +1,396 @@
1
+ import { guid, projectParam, environmentParam, pagingParams, asUserIdParam, readOnly, writes, updates } from './common.js';
2
+ import { PolicyBlockError } from '../knowledge/policy.js';
3
+ import { PlatformApiError } from '../platform/errors.js';
4
+ import { assertProcessNotProduction, bpmIdentityHeaders } from './bpm.js';
5
+ import { handle } from '../platform/responses.js';
6
+ import { z } from 'zod';
7
+ const asUserParam = z.string().min(1).optional()
8
+ .describe("Uygulama kullanıcısının kimliği (ham JWT ya da access_session_create'in tokenRefs'teki tok_… referansı). İstek gateway'in Studio jetonu yerine bu kişi adına gider; süreç başlatan ve görev eylemini yapan bu kişi olur.");
9
+ const asUserIdBpmParam = asUserIdParam
10
+ .describe("Parolasız kullanıcı gözü (users satır id'si): istek gateway kimliğiyle gider ve 'impersonated-user-id' başlığını taşır, platform özneyi o kullanıcı olarak kurar. /bpm/* uçları platform şemasıyla [Authorize] edildiği için UYGULAMA JETONU bu uçlarda 401 alır — parolasız yol budur. Yalnız development ya da dal; asUser ile birlikte verilemez.");
11
+ const expectFailureParam = z.boolean().optional()
12
+ .describe('true: bu çağrıda hata BEKLENİYOR (kasıtlı negatif test: bayat version 409, yetkisiz kişi 403). Ölçüm plansız hata saymaz.');
13
+ const taskActions = ['claim', 'unclaim', 'complete', 'approve', 'reject', 'request-info', 'provide-info', 'reassign', 'undo', 'sign', 'start-assessment', 'submit-assessment'];
14
+ const documentOperations = ['generate', 'stamp', 'print'];
15
+ const instanceOperations = ['cancel', 'suspend', 'resume', 'withdraw', 'reopen', 'retry', 'skip', 'jump', 'watch', 'unwatch'];
16
+ const maxInlineLedgerBytes = 256 * 1024;
17
+ export function registerBpmRuntimeTools(server, platform) {
18
+ const config = platform.config;
19
+ server.registerTool('process_execute', {
20
+ title: 'Süreci başlat',
21
+ description: "Aktif sürümden süreç örneği başlatır (POST bpm/processes/{slug}/start). asUser verilirse başlatan o uygulama kullanıcısıdır; yetki process.start. Tanımda singleton: per-subject varsa aynı satırda açık örnek dönerse YENİ örnek açılmaz, mevcut örnek döner. importAt verilirse örnek başlangıçtan değil o düğümden açılır (POST bpm/instances/import — kâğıttan ya da eski sistemden yarı yolda geçiş): defterin ilk satırı instance-imported, startedAt tarihsel başlangıçtır (gelecek olamaz), düğüm tek görev açarsa assigneeUserId'ye atanır; düğüm başlangıç/bitiş ya da bölge içi olamaz; yetki process.manage. production hedefi istek gitmeden durur (BPM-001). Ayrıntı: platform_guide('bpm') §Yürütme ve kutu, §Operasyon.",
22
+ inputSchema: {
23
+ project: projectParam,
24
+ environment: environmentParam,
25
+ slug: z.string().min(1).max(100),
26
+ applicationId: guid.optional(),
27
+ subjectRowId: z.string().max(64).optional(),
28
+ vars: z.record(z.unknown()).optional(),
29
+ importAt: z.object({
30
+ nodeId: z.string().min(1).max(100),
31
+ assigneeUserId: z.string().max(64).optional(),
32
+ startedAt: z.string().min(10).max(40).describe('ISO 8601 tarihsel başlangıç')
33
+ }).optional().describe('yarı yolda örnek: başlangıç yerine bu düğümden açılır'),
34
+ asUser: asUserParam,
35
+ asUserId: asUserIdBpmParam,
36
+ expectFailure: expectFailureParam
37
+ },
38
+ annotations: writes
39
+ }, handle(config, async ({ project, environment, slug, applicationId, subjectRowId, vars, importAt, asUser, asUserId }) => {
40
+ assertProcessNotProduction('process_execute', environment, config);
41
+ if (importAt) {
42
+ if (Number.isNaN(Date.parse(importAt.startedAt)))
43
+ throw new PlatformApiError('process_execute: importAt.startedAt ISO 8601 tarih olmalı (istek gönderilmedi).', 400);
44
+ return platform.tenant('POST', project, '/bpm/instances/import', {
45
+ environment,
46
+ body: { slug, applicationId, nodeId: importAt.nodeId, assigneeUserId: importAt.assigneeUserId, subjectRowId, vars, startedAt: importAt.startedAt },
47
+ headers: await bpmIdentityHeaders(platform, 'process_execute', project, environment, { asUser, asUserId })
48
+ });
49
+ }
50
+ return platform.tenant('POST', project, `/bpm/processes/${encodeURIComponent(slug)}/start`, {
51
+ environment,
52
+ body: { applicationId, subjectRowId, vars },
53
+ headers: await bpmIdentityHeaders(platform, 'process_execute', project, environment, { asUser, asUserId })
54
+ });
55
+ }));
56
+ server.registerTool('process_trigger', {
57
+ title: 'Sürece olay gönder',
58
+ description: "kind signal: açık örneğe sinyal (POST bpm/signals) — bölge olay işleyicisi (region.on[]), completeOn bekleyen görev ya da 'membership-added'; 'withdraw' ve 'reopen' adları yerleşiktir, instanceId zorunlu. kind message: mesaj korelasyonu (POST bpm/messages) — wait düğümünde name + correlationKey ile bekleyen örnekleri ilerletir, eşleşen yoksa message start'lı tanımı başlatır; instanceId verilmez. correlationKey'siz mesaj o adı anahtarsız bekleyen BÜTÜN örnekleri ilerletir (bekleyen yoksa message start'lı BÜTÜN tanımları başlatır) — bu yüzden anahtarsız gönderim broadcast:true ister. Mesaj eventId en çok 150 karakter. eventId verirseniz aynı olay ikinci kez işlenmez. bufferSeconds (en çok 86400): alıcı yoksa mesaj o süre tamponda bekler ve sonradan mesaj bekleyen wait düğümü açılınca hemen tüketilir — yanıt 202 ve buffered:true. kind event-source: name adlı olay kaynağına olay (POST bpm/event-sources/{name}/events; eventId zorunlu, en çok 120 karakter, payload kaynağın correlationKey ifadesiyle mesaja döner). kind mail-source: sourceId'li e-posta kaynağını şimdi yoklar (POST bpm/mail-sources/{id}/poll; yanıt readCount, sentCount, unmatchedCount, skippedCount). Kaynaklar connector modülünde (process_event_source_create, process_mail_source_create).",
59
+ inputSchema: {
60
+ project: projectParam,
61
+ environment: environmentParam,
62
+ kind: z.enum(['signal', 'message', 'event-source', 'mail-source']).optional().describe('varsayılan signal'),
63
+ sourceId: guid.optional().describe('kind mail-source için e-posta kaynağı id'),
64
+ instanceId: guid.optional().describe('kind signal için zorunlu'),
65
+ correlationKey: z.string().max(200).optional().describe('kind message için'),
66
+ name: z.string().min(1).max(100).optional().describe("signal, message ve event-source için zorunlu; mail-source almaz. ör. 'duzenleme-istegi', 'membership-added', 'row-event'"),
67
+ payload: z.record(z.unknown()).optional(),
68
+ eventId: z.string().max(200).optional(),
69
+ broadcast: z.boolean().optional().describe('kind message: correlationKey olmadan bütün bekleyenlere gönderimi onaylar'),
70
+ bufferSeconds: z.number().int().min(0).max(86400).optional().describe('kind message: alıcı yoksa tamponda bekleme süresi (sn)'),
71
+ asUser: asUserParam,
72
+ asUserId: asUserIdBpmParam,
73
+ expectFailure: expectFailureParam
74
+ },
75
+ annotations: writes
76
+ }, handle(config, async ({ project, environment, kind, sourceId, instanceId, correlationKey, name, payload, eventId, broadcast, bufferSeconds, asUser, asUserId }) => {
77
+ assertProcessNotProduction('process_trigger', environment, config);
78
+ if (kind === 'event-source') {
79
+ if (!eventId)
80
+ throw new PlatformApiError("process_trigger: kind 'event-source' eventId ister — aynı olayın ikinci kez işlenmemesi ona bağlı (istek gönderilmedi).", 400);
81
+ if (eventId.length > 120)
82
+ throw new PlatformApiError('process_trigger: olay kaynağı eventId en çok 120 karakter (istek gönderilmedi).', 400);
83
+ if (!name)
84
+ throw new PlatformApiError("process_trigger: kind 'event-source' name (olay kaynağı adı) ister (istek gönderilmedi).", 400);
85
+ return platform.tenant('POST', project, `/bpm/event-sources/${encodeURIComponent(name)}/events`, { environment, body: { eventId, payload } });
86
+ }
87
+ if (kind === 'mail-source') {
88
+ if (!sourceId)
89
+ throw new PlatformApiError("process_trigger: kind 'mail-source' sourceId ister (process_mail_source_list) (istek gönderilmedi).", 400);
90
+ return platform.tenant('POST', project, `/bpm/mail-sources/${sourceId}/poll`, { environment });
91
+ }
92
+ if (!name)
93
+ throw new PlatformApiError(`process_trigger: kind '${kind ?? 'signal'}' name ister (istek gönderilmedi).`, 400);
94
+ if (kind === 'message') {
95
+ if (!correlationKey && !broadcast)
96
+ throw new PlatformApiError("process_trigger: correlationKey'siz mesaj o adı anahtarsız bekleyen bütün örnekleri ilerletir; bilerek yapıyorsanız broadcast:true verin (istek gönderilmedi).", 400);
97
+ if (eventId && eventId.length > 150)
98
+ throw new PlatformApiError('process_trigger: mesaj eventId en çok 150 karakter (istek gönderilmedi).', 400);
99
+ return platform.tenant('POST', project, '/bpm/messages', {
100
+ environment,
101
+ body: { name, correlationKey, payload, eventId, bufferSeconds },
102
+ headers: await bpmIdentityHeaders(platform, 'process_trigger', project, environment, { asUser, asUserId })
103
+ });
104
+ }
105
+ if (!instanceId)
106
+ throw new PlatformApiError("process_trigger: kind 'signal' instanceId ister (istek gönderilmedi).", 400);
107
+ return platform.tenant('POST', project, '/bpm/signals', {
108
+ environment,
109
+ body: { instanceId, name, payload, eventId },
110
+ headers: await bpmIdentityHeaders(platform, 'process_trigger', project, environment, { asUser, asUserId })
111
+ });
112
+ }));
113
+ server.registerTool('process_instance_list', {
114
+ title: 'Süreç örneklerini listele',
115
+ description: 'Süreç örneklerini listeler; tanım, durum, iş satırı ve başlatan ile süzülür.',
116
+ inputSchema: {
117
+ project: projectParam,
118
+ environment: environmentParam,
119
+ definitionId: guid.optional(),
120
+ state: z.enum(['running', 'waiting-timer', 'suspended', 'faulted', 'completed', 'cancelled', 'withdrawn', 'expired']).optional(),
121
+ subjectRowId: z.string().max(64).optional(),
122
+ initiatorUserId: z.string().max(64).optional(),
123
+ asUser: asUserParam,
124
+ asUserId: asUserIdBpmParam,
125
+ ...pagingParams
126
+ },
127
+ annotations: readOnly
128
+ }, handle(config, async ({ project, environment, asUser, asUserId, ...query }) => platform.tenant('GET', project, '/bpm/instances', {
129
+ environment,
130
+ query,
131
+ headers: await bpmIdentityHeaders(platform, 'process_instance_list', project, environment, { asUser, asUserId })
132
+ })));
133
+ server.registerTool('process_instance_get', {
134
+ title: 'Süreç örneğini getir',
135
+ description: "Örneği getirir. view:'timeline' defteri (sıralı olaylar, hash zinciri), güncel ve sonraki düğümleri ve isLedgerValid'i döner — zincir kırıksa defter kurcalanmıştır. view:'events' defteri CSV olarak verir (sequence, occurredAt, kind, nodeId, taskId, actorUserId, actorKind, payload, previousHash, hash; en çok 256 KB satır içi — büyükse zaman çizelgesini okuyun). view:'verify' hash zincirini doğrular: { isValid, brokenAtSequence, eventCount }. Arşivlenmiş örnek bu uçlarda 404 döner.",
136
+ inputSchema: {
137
+ project: projectParam,
138
+ environment: environmentParam,
139
+ id: guid,
140
+ view: z.enum(['summary', 'timeline', 'events', 'verify']).optional().describe('varsayılan summary'),
141
+ asUser: asUserParam,
142
+ asUserId: asUserIdBpmParam
143
+ },
144
+ annotations: readOnly
145
+ }, handle(config, async ({ project, environment, id, view, asUser, asUserId }) => {
146
+ const headers = await bpmIdentityHeaders(platform, 'process_instance_get', project, environment, { asUser, asUserId });
147
+ if (view === 'verify')
148
+ return platform.tenant('GET', project, `/bpm/instances/${id}/events/verify`, { environment, headers });
149
+ if (view === 'events') {
150
+ const payload = await platform.tenant('GET', project, `/bpm/instances/${id}/events`, { environment, query: { format: 'csv' }, expectBinary: true, headers });
151
+ if (payload.bytes.byteLength > maxInlineLedgerBytes) {
152
+ throw new PlatformApiError(`process_instance_get: defter CSV'si ${payload.bytes.byteLength} B, satır içi sınır ${maxInlineLedgerBytes} B; view:'timeline' ile okuyun ya da dosyayı uçtan (GET bpm/instances/${id}/events?format=csv) indirin.`, 413);
153
+ }
154
+ return { format: 'csv', sizeBytes: payload.bytes.byteLength, csv: payload.bytes.toString('utf8').replace(/^\uFEFF/, '') };
155
+ }
156
+ return platform.tenant('GET', project, view === 'timeline' ? `/bpm/instances/${id}/timeline` : `/bpm/instances/${id}`, { environment, headers });
157
+ }));
158
+ server.registerTool('process_instance_update', {
159
+ title: 'Süreç örneğine müdahale',
160
+ description: "Örneğe tek kapıdan müdahale: operation cancel | suspend (reason ZORUNLU; tanımda onCancel varsa önce telafi koşar, başarısızsa örnek faulted kalır ve iptal edilmez) | resume | withdraw (yalnız başlatan, withdrawPolicy; tanımdaki onCancel telafisi ve writeBack'i cancel.cause='withdrawn' ile koşar, açık görevler ve zamanlayıcılar kapanır) | reopen (bitiş düğümünün reopenWindow süresi içinde; nodeId opsiyonel) | retry (yalnız faulted; servis denemeleri sıfırlanır) | skip (nodeId'deki token düğümü tamamlanmış sayılıp ilerler, reason ZORUNLU, defterde node-skipped) | jump (nodeId → targetNodeId, reason ZORUNLU, atlanan görevler iptal, defterde instance-jumped) | watch / unwatch (karar ve kapanış bildirimlerini izle; taskId ile tek görev). Yetki process.manage (withdraw, watch hariç). production hedefi istek gitmeden durur (BPM-001).",
161
+ inputSchema: {
162
+ project: projectParam,
163
+ environment: environmentParam,
164
+ id: guid,
165
+ operation: z.enum(instanceOperations),
166
+ reason: z.string().max(1000).optional(),
167
+ nodeId: z.string().max(100).optional(),
168
+ targetNodeId: z.string().max(100).optional().describe('jump için'),
169
+ taskId: guid.optional().describe('watch / unwatch için tek görev'),
170
+ asUser: asUserParam,
171
+ asUserId: asUserIdBpmParam,
172
+ expectFailure: expectFailureParam
173
+ },
174
+ annotations: updates
175
+ }, handle(config, async ({ project, environment, id, operation, reason, nodeId, targetNodeId, taskId, asUser, asUserId }) => {
176
+ assertProcessNotProduction('process_instance_update', environment, config);
177
+ if ((operation === 'cancel' || operation === 'suspend' || operation === 'skip' || operation === 'jump') && !reason?.trim()) {
178
+ throw new PlatformApiError(`process_instance_update: '${operation}' gerekçe ister — 'reason' verin (platform reddeder, istek gönderilmedi).`, 400);
179
+ }
180
+ if (operation === 'jump' && !targetNodeId)
181
+ throw new PlatformApiError("process_instance_update: 'jump' targetNodeId ister (istek gönderilmedi).", 400);
182
+ if (operation === 'watch' || operation === 'unwatch') {
183
+ return platform.tenant(operation === 'watch' ? 'POST' : 'DELETE', project, `/bpm/instances/${id}/watch`, {
184
+ environment,
185
+ query: taskId ? { taskId } : undefined,
186
+ headers: await bpmIdentityHeaders(platform, 'process_instance_update', project, environment, { asUser, asUserId })
187
+ });
188
+ }
189
+ const bodies = {
190
+ reopen: { nodeId },
191
+ cancel: { reason },
192
+ suspend: { reason },
193
+ retry: { reason },
194
+ skip: { nodeId, reason },
195
+ jump: { fromNodeId: nodeId, targetNodeId, reason }
196
+ };
197
+ const body = bodies[operation] ?? {};
198
+ return platform.tenant('POST', project, `/bpm/instances/${id}/${operation}`, {
199
+ environment,
200
+ body,
201
+ headers: await bpmIdentityHeaders(platform, 'process_instance_update', project, environment, { asUser, asUserId })
202
+ });
203
+ }));
204
+ server.registerTool('process_task_list', {
205
+ title: 'Görev kutusunu listele',
206
+ description: "Kişinin görev kutusu: tab mine (atanan) | group (aday olduğu, üstlenilmemiş) | delegated | started (başlattığı süreçler) | done | all (process.view-all/manage) | planned (plannedStart'ı gelmemiş görevler). scope application (applicationId verilirse o uygulamanın görevleri) | realm (projedeki tüm uygulamalar; satırda applicationSlug rozeti). q doğal dil süzgecidir ('bu ay geciken satınalmalar'): platform AI ile { definition, overdue, from, to, minPriority } süzgecine çevirir, liste yine görünürlük kurallarıyla sorgulanır ve yanıtta appliedFilter döner (tanınmayan istek boş süzgeç; tahmin yok; dakikalık sınır aşılırsa 429 AiRateLimited). q dış AI modelini çağırır ve öneri tablosuna iz yazar; bu yüzden q ile production hedefi durur (BPM-001). asUser verilmezse gateway'in kendi kullanıcısının kutusu döner.",
207
+ inputSchema: {
208
+ project: projectParam,
209
+ environment: environmentParam,
210
+ tab: z.enum(['mine', 'group', 'delegated', 'started', 'done', 'all', 'planned']).optional().describe('varsayılan mine'),
211
+ scope: z.enum(['application', 'realm']).optional().describe('varsayılan application'),
212
+ applicationId: guid.optional(),
213
+ instanceId: guid.optional(),
214
+ q: z.string().min(1).max(500).optional().describe("doğal dil süzgeci, ör. 'bu ay geciken satınalmalar'; yanıtta appliedFilter"),
215
+ asUser: asUserParam,
216
+ asUserId: asUserIdBpmParam,
217
+ ...pagingParams
218
+ },
219
+ annotations: readOnly
220
+ }, handle(config, async ({ project, environment, asUser, asUserId, ...query }) => {
221
+ if (query.q)
222
+ assertProcessNotProduction('process_task_list', environment, config);
223
+ return platform.tenant('GET', project, '/bpm/tasks', { environment, query, headers: await bpmIdentityHeaders(platform, 'process_task_list', project, environment, { asUser, asUserId }) });
224
+ }));
225
+ server.registerTool('process_task_get', {
226
+ title: 'Görevi getir',
227
+ description: "Görevi, kullanılabilir eylemleriyle (actions) ve güncel 'version' değeriyle getirir. Görev eyleminden önce okuyun: claim/complete bayat version ile 409 AggregateVersionConflictException, başkasının üstlendiği görevde 409 TaskAlreadyClaimed alır. view 'assistant': görev yardımcısı — question yoksa özet (GET …/assistant/summary), varsa yanıt (POST …/assistant/ask); yanıt { aiSuggestion: { text, suggestedAction, rationale, similarOutcomes, promptHash, suggestionId } }. AI ÖNERİSİDİR, karar değildir: görevi kapatmaz, outcome yazmaz; kararı process_task_update ile kişi verir. Korumalı alanlar modele maskeli gider; her çağrı öneri tablosuna ve süreç defterine (ai-suggestion) iz yazar ve dakikalık sınıra sayılır (429 AiRateLimited); bu yüzden production hedefi durur (BPM-001). Aracın annotation'ı bu yan etki yüzünden readOnly değildir (katalogda bilinçli istisna).",
228
+ inputSchema: {
229
+ project: projectParam,
230
+ environment: environmentParam,
231
+ id: guid,
232
+ view: z.enum(['task', 'assistant']).optional().describe('varsayılan task'),
233
+ question: z.string().min(1).max(2000).optional().describe("yalnız view 'assistant': göreve dair soru"),
234
+ asUser: asUserParam,
235
+ asUserId: asUserIdBpmParam
236
+ },
237
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
238
+ }, handle(config, async ({ project, environment, id, view, question, asUser, asUserId }) => {
239
+ const headers = await bpmIdentityHeaders(platform, 'process_task_get', project, environment, { asUser, asUserId });
240
+ if (view !== 'assistant') {
241
+ if (question !== undefined)
242
+ throw new PlatformApiError("process_task_get: 'question' yalnız view: 'assistant' ile verilir (istek gönderilmedi).", 400);
243
+ return platform.tenant('GET', project, `/bpm/tasks/${id}`, { environment, headers });
244
+ }
245
+ assertProcessNotProduction('process_task_get', environment, config);
246
+ const assistant = question === undefined
247
+ ? await platform.tenant('GET', project, `/bpm/tasks/${id}/assistant/summary`, { environment, headers })
248
+ : await platform.tenant('POST', project, `/bpm/tasks/${id}/assistant/ask`, { environment, body: { question }, headers });
249
+ return { aiSuggestion: assistant.data ?? assistant, note: 'AI önerisi — karar değil; görev durumu değişmedi.' };
250
+ }));
251
+ server.registerTool('process_task_update', {
252
+ title: 'Görev eylemi',
253
+ description: "Görev eylemlerinin tek kapısı: action claim | unclaim | complete (actionId ile özel eylem; completes:false eylem görevi açık bırakıp sinyal yayar) | approve | reject | request-info (question) | provide-info (answer) | reassign (targetUserId, reason) | undo (undoWindow içinde) | sign (signature taşıyan düğüm: password ZORUNLU, düğüm signature.reauth:false değilse — BPM-018; meaning verilmezse düğümün anlamı, düğümde anlam yoksa projenin Bpm:SignatureMeanings sözlüğünden biri; asUser oturumundaki ilk imzada imzalayanın email'i de; fileId imzalanan belge, imzadan sonra dosya kilitlenir; 401 ReauthenticationRequired yeniden parola ister, 422 SignatureRequired imzasız tamamlama denemesidir) | start-assessment (assessment taşıyan user-task: soru bankasından deneme açar, yanıtta questions[] cevapsız; açık deneme varsa onu döner, hak bittiyse 422) | submit-assessment (answers { soruId: cevap }: puanlanır; geçerse okuma kanıtı eylemiyle görev kapanır, kalırsa hak varsa görev açık kalır, hak biterse sonuç assessment-failed; değerlendirmesiz okuma kanıtı 422 TaskStateTransitionNotAllowed). Parola gateway günlüğüne maskeli yazılır, yanıta girmez. asUser ZORUNLU (BPM-006): görev eylemi uygulama kullanıcısı adına yapılır, Studio jetonu 403 alır. version process_task_get'ten; 409 (TaskAlreadyClaimed ya da AggregateVersionConflictException) gelirse görevi yeniden okuyun. production hedefi durur (BPM-001).",
254
+ inputSchema: {
255
+ project: projectParam,
256
+ environment: environmentParam,
257
+ id: guid,
258
+ action: z.enum(taskActions),
259
+ actionId: z.string().max(100).optional().describe("complete için özel eylem id'si (tanımdaki actions[].id)"),
260
+ payload: z.record(z.unknown()).optional(),
261
+ comment: z.string().max(4000).optional(),
262
+ version: z.string().optional().describe("process_task_get yanıtındaki version METİN olarak (tick boyutlu sayı; number olarak gönderilirse yuvarlanır ve 409 alır)"),
263
+ targetUserId: z.string().max(64).optional(),
264
+ reason: z.string().max(1000).optional(),
265
+ question: z.string().max(4000).optional(),
266
+ answer: z.string().max(4000).optional(),
267
+ meaning: z.string().max(100).optional().describe("sign: imza anlamı (author | reviewer | approver | responsible | verifier ya da projede tanımlı)"),
268
+ email: z.string().max(200).optional().describe('sign: oturumdaki ilk imzada kullanıcı e-postası'),
269
+ password: z.string().max(200).optional().describe('sign: yeniden kimlik doğrulama parolası'),
270
+ fileId: guid.optional().describe('sign: imzalanan belge'),
271
+ answers: z.record(z.unknown()).optional().describe('submit-assessment: soru id → cevap'),
272
+ asUser: asUserParam,
273
+ asUserId: asUserIdBpmParam,
274
+ expectFailure: expectFailureParam
275
+ },
276
+ annotations: writes
277
+ }, handle(config, async ({ project, environment, id, action, actionId, payload, comment, version, targetUserId, reason, question, answer, meaning, email, password, fileId, answers, asUser, asUserId }) => {
278
+ assertProcessNotProduction('process_task_update', environment, config);
279
+ if (!asUser && !asUserId) {
280
+ throw new PolicyBlockError([{
281
+ ruleId: 'BPM-006',
282
+ severity: 'block',
283
+ message: "process_task_update kimliksiz gönderilmez: görev eylemi bir uygulama kullanıcısı adına yapılır. İki yol var — development'ta parolasız 'asUserId' (users satır id'si, 'impersonated-user-id' başlığı) ya da 'asUser' (access_session_create ile giriş yapıp tokenRefs'teki tok_… değeri). /bpm/* uçları platform şemasıyla [Authorize] edildiği için uygulama jetonu 401 alabilir; parolasız yol asUserId'dir.",
284
+ guide: 'ngapps://guides/bpm'
285
+ }]);
286
+ }
287
+ const payloadAnswers = payload?.answers;
288
+ if (action === 'submit-assessment') {
289
+ if (answers !== undefined && payloadAnswers !== undefined) {
290
+ throw new PlatformApiError("process_task_update: 'submit-assessment' cevapları ya 'answers' ya 'payload.answers' ile verilir, ikisi birden değil (istek gönderilmedi).", 400);
291
+ }
292
+ const submitted = answers ?? payloadAnswers;
293
+ if (!submitted || typeof submitted !== 'object' || Array.isArray(submitted) || Object.keys(submitted).length === 0) {
294
+ throw new PlatformApiError("process_task_update: 'submit-assessment' cevapsız gönderilmez — boş deneme 0 puan alır ve bir hak tüketir, son hakta görev assessment-failed ile kapanır. start-assessment yanıtındaki her soru id'si için answers { soruId: cevap } verin (istek gönderilmedi).", 400);
295
+ }
296
+ }
297
+ if (action === 'sign' && !password) {
298
+ const task = await platform.tenant('GET', project, `/bpm/tasks/${id}`, { environment, headers: await bpmIdentityHeaders(platform, 'process_task_update', project, environment, { asUser, asUserId }) });
299
+ if (task.data?.signature?.reauth !== false) {
300
+ throw new PolicyBlockError([{
301
+ ruleId: 'BPM-018',
302
+ severity: 'block',
303
+ message: "process_task_update action 'sign' password olmadan gönderilmez: düğümün imzası (signature.reauth false değil) her seferinde imzalayanın parolasıyla yeniden kimlik doğrulaması ister (21 CFR 11.200); parolasız istek 401 ReauthenticationRequired döner ve imza girişimi kilit sayacına yazılır. meaning verilmezse düğümün anlamı kullanılır.",
304
+ guide: 'ngapps://guides/bpm'
305
+ }], false);
306
+ }
307
+ }
308
+ const bodies = {
309
+ claim: { version },
310
+ unclaim: {},
311
+ complete: { action: actionId ?? 'complete', payload, comment, version },
312
+ approve: { payload, comment, version },
313
+ reject: { payload, comment, version },
314
+ 'request-info': { question },
315
+ 'provide-info': { answer },
316
+ reassign: { targetUserId, reason },
317
+ undo: {},
318
+ sign: { meaning, email, password, fileId, comment, payload, version },
319
+ 'start-assessment': {},
320
+ 'submit-assessment': { action: 'submit-assessment', payload: { ...(payload ?? {}), answers: answers ?? payloadAnswers }, comment, version }
321
+ };
322
+ const paths = { 'start-assessment': 'assessment', 'submit-assessment': 'complete' };
323
+ return platform.tenant('POST', project, `/bpm/tasks/${id}/${paths[action] ?? action}`, {
324
+ environment,
325
+ body: bodies[action],
326
+ headers: await bpmIdentityHeaders(platform, 'process_task_update', project, environment, { asUser, asUserId })
327
+ });
328
+ }));
329
+ server.registerTool('process_preference_get', {
330
+ title: 'Bildirim tercihini getir',
331
+ description: 'Kullanıcının süreç bildirim tercihi: mode immediate | digest | sla-only, quietFrom/quietTo (sessiz saatler), digestAt (özet saati), timeZone, phone (sms kanalının alıcısı). Kayıt yoksa varsayılan (immediate, UTC) döner.',
332
+ inputSchema: { project: projectParam, environment: environmentParam, asUser: asUserParam, asUserId: asUserIdBpmParam },
333
+ annotations: readOnly
334
+ }, handle(config, async ({ project, environment, asUser, asUserId }) => platform.tenant('GET', project, '/bpm/notification-preferences', { environment, headers: await bpmIdentityHeaders(platform, 'process_preference_get', project, environment, { asUser, asUserId }) })));
335
+ server.registerTool('process_preference_update', {
336
+ title: 'Bildirim tercihini kaydet',
337
+ description: "Bildirim tercihini tam gövdeyle kaydeder: verilmeyen quietFrom/quietTo sessiz saati kaldırır. digest: bildirimler digestAt saatinde tek e-postada toplanır; sla-only: yalnız SLA hatırlatma/ihlal bildirimleri anında gider, diğerleri özete düşer. Sessiz saat her kategoriye uygulanır ve gönderimi pencere sonuna erteler. Saatler '08:00' ya da '08:00:00'. phone: notify düğümünün 'sms' kanalı bu numaraya gider (+905551112233; sağlayıcı kasadaki notification-sms kimliğinin bağlayıcısı, netgsm ya da twilio). teams ve slack kanalları kullanıcının e-postasıyla gider (notification-teams, notification-slack kimlikleri).",
338
+ inputSchema: {
339
+ project: projectParam,
340
+ environment: environmentParam,
341
+ mode: z.enum(['immediate', 'digest', 'sla-only']),
342
+ quietFrom: z.string().regex(/^\d{2}:\d{2}(:\d{2})?$/).optional(),
343
+ quietTo: z.string().regex(/^\d{2}:\d{2}(:\d{2})?$/).optional(),
344
+ digestAt: z.string().regex(/^\d{2}:\d{2}(:\d{2})?$/).optional(),
345
+ timeZone: z.string().min(1).max(100),
346
+ phone: z.string().regex(/^[+]?[0-9]{10,15}$/).optional().describe('sms kanalı alıcısı, ülke koduyla; verilmezse numara silinir'),
347
+ asUser: asUserParam,
348
+ asUserId: asUserIdBpmParam
349
+ },
350
+ annotations: updates
351
+ }, handle(config, async ({ project, environment, mode, quietFrom, quietTo, digestAt, timeZone, phone, asUser, asUserId }) => {
352
+ assertProcessNotProduction('process_preference_update', environment, config);
353
+ const seconds = (value) => value && /^\d{2}:\d{2}$/.test(value) ? `${value}:00` : value;
354
+ return platform.tenant('PUT', project, '/bpm/notification-preferences', {
355
+ environment,
356
+ body: { mode, quietFrom: seconds(quietFrom), quietTo: seconds(quietTo), digestAt: seconds(digestAt), timeZone, phone },
357
+ headers: await bpmIdentityHeaders(platform, 'process_preference_update', project, environment, { asUser, asUserId })
358
+ });
359
+ }));
360
+ server.registerTool('process_document_execute', {
361
+ title: 'Belge üret / damgala / bas',
362
+ description: "operation generate (varsayılan; POST bpm/documents/generate): DOCX şablonundan (templateFileId, {{alan}} ve {{a.b}} yer tutucuları, table belge sonuna tablo ekler) docx | pdf | html belge üretir ve files'a yazar; stamp (POST bpm/documents/{fileId}/stamp): revizyon/durum başlığı ve filigran basar; print (POST bpm/documents/{fileId}/print): 'KONTROLSÜZ KOPYA' damgalı basım kopyası üretir, instanceId + nodeId verilirse defter document-printed yazar. Yanıt dosya { id, name, extension, size, folderId }; indirmek için file_download. Süreç içinde aynı iş document düğümüdür.",
363
+ inputSchema: {
364
+ project: projectParam,
365
+ environment: environmentParam,
366
+ operation: z.enum(documentOperations).optional(),
367
+ templateFileId: guid.optional().describe('generate: DOCX şablonu'),
368
+ data: z.record(z.unknown()).optional().describe('generate: yer tutucu değerleri'),
369
+ format: z.enum(['docx', 'pdf', 'html']).optional().describe('generate: varsayılan docx'),
370
+ table: z.record(z.unknown()).optional().describe("generate: { headers: ['Kalem', 'Tutar'], rows: [['Vida', '12']] }"),
371
+ stamp: z.record(z.unknown()).optional().describe('generate: { revision, state, watermark, note }'),
372
+ fileName: z.string().max(200).optional(),
373
+ folderId: guid.optional(),
374
+ fileId: guid.optional().describe('stamp ve print: hedef dosya'),
375
+ revision: z.string().max(50).optional().describe('stamp'),
376
+ state: z.string().max(50).optional().describe('stamp'),
377
+ watermark: z.string().max(100).optional().describe('stamp'),
378
+ instanceId: guid.optional().describe('print: defter kaydı için örnek'),
379
+ nodeId: z.string().max(100).optional().describe('print: defter kaydı için düğüm')
380
+ },
381
+ annotations: writes
382
+ }, handle(config, async ({ project, environment, operation, templateFileId, data, format, table, stamp, fileName, folderId, fileId, revision, state, watermark, instanceId, nodeId }) => {
383
+ assertProcessNotProduction('process_document_execute', environment, config);
384
+ const resolvedOperation = operation ?? 'generate';
385
+ if (resolvedOperation === 'generate') {
386
+ if (!templateFileId)
387
+ throw new PlatformApiError("process_document_execute: generate templateFileId ister (istek gönderilmedi).", 400);
388
+ return platform.tenant('POST', project, '/bpm/documents/generate', { environment, body: { templateFileId, data, format, table, stamp, fileName, folderId } });
389
+ }
390
+ if (!fileId)
391
+ throw new PlatformApiError(`process_document_execute: ${resolvedOperation} fileId ister (istek gönderilmedi).`, 400);
392
+ return resolvedOperation === 'stamp'
393
+ ? platform.tenant('POST', project, `/bpm/documents/${fileId}/stamp`, { environment, body: { revision, state, watermark } })
394
+ : platform.tenant('POST', project, `/bpm/documents/${fileId}/print`, { environment, body: { instanceId, nodeId } });
395
+ }));
396
+ }
@@ -0,0 +1,178 @@
1
+ import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, updates, destructive, microOps } from './common.js';
2
+ import { applyPolicyMode, attachFindings, PolicyBlockError } from '../knowledge/policy.js';
3
+ import { assertInline, writeLocalFile } from './bpmGovernance.js';
4
+ import { assertProcessNotProduction, dataOf } from './bpm.js';
5
+ import { resolvePatchOperations } from '../knowledge/outlines.js';
6
+ import { withNormalization } from '../knowledge/normalize.js';
7
+ import { PlatformApiError } from '../platform/errors.js';
8
+ import { caseFindings } from '../knowledge/case.js';
9
+ import { handle } from '../platform/responses.js';
10
+ import { readFile } from 'node:fs/promises';
11
+ import fastJsonPatch from 'fast-json-patch';
12
+ import { resolve } from 'node:path';
13
+ import { z } from 'zod';
14
+ const maxInlineXmlBytes = 256_000;
15
+ const maxCmmnBytes = 5_000_000;
16
+ const caseParam = z.record(z.unknown())
17
+ .describe("Vaka modeli (CMMN 1.1): { slug, name, description, caseFile[{ id, default }], planItems[{ id, type: stage | human-task | milestone, parent, required, repeatable, manualActivation, discretionary, autoComplete (stage), assignee { rule: user | role | initiator, arg }, entry[], exit[] }], sentries[{ id, onParts[{ source, event: complete | start | terminate | occur | update }], ifPart }] }. Ayrıntı platform_guide('case').");
18
+ const policyOverrideParam = z.boolean().optional()
19
+ .describe("true: politika block bulgularını warn'a indirir (bilinçli istisna; yanıtta işaretlenir)");
20
+ const patchOperationsParam = z.array(z.record(z.unknown())).min(1)
21
+ .describe("RFC 6902 JSON Patch işlemleri, yollar vaka modeline göre. Öğe hedeflerken İNDEKS YERİNE SEÇİCİ: { op: 'replace', path: '/planItems[id=hukuk-gorusu]/assignee/arg', value: 'hukuk' }; yeni öğe '/planItems/-'.");
22
+ function lintCase(config, caseModel, policyOverride) {
23
+ if (config.policyMode === 'off')
24
+ return [];
25
+ const findings = applyPolicyMode(config, caseFindings(caseModel), policyOverride === true);
26
+ const blocking = findings.filter(finding => finding.severity === 'block');
27
+ if (blocking.length > 0)
28
+ throw new PolicyBlockError(blocking);
29
+ return findings;
30
+ }
31
+ export function registerCaseTools(server, platform) {
32
+ const config = platform.config;
33
+ server.registerTool('case_definition_list', {
34
+ title: 'Vaka tanımlarını listele',
35
+ description: "CMMN vaka tanımlarını listeler (GET bpm/cases): slug, ad, aktif ve taslak sürüm numarası. Vaka yapılandırılmamış iş içindir (şikâyet, soruşturma); akış belli ise süreç tanımı (process_definition_*). Süreçten vaka 'case' düğümüyle başlar.",
36
+ inputSchema: { project: projectParam, environment: environmentParam, searchTerm: z.string().max(200).optional(), applicationId: guid.optional(), ...pagingParams },
37
+ annotations: readOnly
38
+ }, handle(config, ({ project, environment, ...query }) => platform.tenant('GET', project, '/bpm/cases', { environment, query })));
39
+ server.registerTool('case_definition_get', {
40
+ title: 'Vaka tanımını getir',
41
+ description: "Vaka tanımını getirir (GET bpm/cases/{id}): case güncel model (taslak varsa taslak), activeCase yayındaki model, versions[] (draft → active → retiring). CMMN XML için case_cmmn_get.",
42
+ inputSchema: { project: projectParam, environment: environmentParam, id: guid },
43
+ annotations: readOnly
44
+ }, handle(config, ({ project, environment, id }) => platform.tenant('GET', project, `/bpm/cases/${id}`, { environment })));
45
+ server.registerTool('case_definition_create', {
46
+ title: 'Vaka tanımı oluştur',
47
+ description: "Yeni vaka tanımı yazar (POST bpm/cases; taslak sürüm). İlk kullanımdan önce platform_guide('case') okuyun. Gateway yazmadan önce CASE-001'i denetler (tanımsız plan öğesine/vaka dosyasına bakan onPart ya da tanımsız entry/exit sentry'si, block); backend diğer kuralları 422 CaseInvalid bulgu listesiyle döner. Yayın case_definition_publish, başlatma case_execute.",
48
+ inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid.optional(), case: caseParam, policyOverride: policyOverrideParam },
49
+ annotations: writes
50
+ }, handle(config, async ({ project, environment, applicationId, case: caseModel, policyOverride }) => {
51
+ assertProcessNotProduction('case_definition_create', environment, config);
52
+ const findings = lintCase(config, caseModel, policyOverride);
53
+ return attachFindings(dataOf(await platform.tenant('POST', project, '/bpm/cases', { environment, body: { applicationId, case: caseModel } })), findings);
54
+ }));
55
+ server.registerTool('case_definition_update', {
56
+ title: 'Vaka tanımını güncelle',
57
+ description: "Vaka tanımının taslağını tam modelle yazar (PUT bpm/cases/{id}; taslak yoksa yeni sürüm numarası açılır). Backend sürüm denetimi yapmaz: son yazan kazanır; hedefli değişiklik için case_definition_patch. İlk yayından sonra slug değişmez (400). CASE-001 denetimi case_definition_create ile aynı.",
58
+ inputSchema: { project: projectParam, environment: environmentParam, id: guid, case: caseParam, policyOverride: policyOverrideParam },
59
+ annotations: updates
60
+ }, handle(config, async ({ project, environment, id, case: caseModel, policyOverride }) => {
61
+ assertProcessNotProduction('case_definition_update', environment, config);
62
+ const findings = lintCase(config, caseModel, policyOverride);
63
+ return attachFindings(dataOf(await platform.tenant('PUT', project, `/bpm/cases/${id}`, { environment, body: { case: caseModel } })), findings);
64
+ }));
65
+ server.registerTool('case_definition_patch', {
66
+ title: 'Vaka tanımına patch uygula',
67
+ description: "Tam modeli bağlama almadan hedefli değişiklik: gateway güncel modeli okur (GET), seçicileri (/planItems[id=x], /sentries[id=x], /caseFile[id=x]) çözer, yamayı yerelde uygular, CASE-001'den geçirir ve tam modeli taslağa yazar (PUT). Backend sürüm denetimi yok: okuma ile yazma arasında başka yazan olursa değişikliği ezilir. Yanıt { patchTargets, case }.",
68
+ inputSchema: {
69
+ project: projectParam,
70
+ environment: environmentParam,
71
+ id: guid,
72
+ operations: patchOperationsParam.optional(),
73
+ ops: patchOperationsParam.optional().describe("'operations' ile aynı alan (eşanlamlı)"),
74
+ policyOverride: policyOverrideParam
75
+ },
76
+ annotations: updates
77
+ }, handle(config, async ({ project, environment, id, operations: givenOperations, ops, policyOverride }) => {
78
+ assertProcessNotProduction('case_definition_patch', environment, config);
79
+ const operations = microOps('case_definition_patch', ops, givenOperations);
80
+ const current = dataOf(await platform.tenant('GET', project, `/bpm/cases/${id}`, { environment }));
81
+ const model = structuredClone((current.case ?? {}));
82
+ const resolved = resolvePatchOperations(model, operations, 'case_definition_get');
83
+ let patched;
84
+ try {
85
+ patched = fastJsonPatch.applyPatch(model, resolved.operations, true, false).newDocument;
86
+ }
87
+ catch (error) {
88
+ throw new PlatformApiError(`case_definition_patch: yama uygulanamadı — ${error instanceof Error ? error.message : String(error)} (istek gönderilmedi). Yolları case_definition_get çıktısına göre kurun.`, 400);
89
+ }
90
+ const findings = lintCase(config, patched, policyOverride);
91
+ const saved = dataOf(await platform.tenant('PUT', project, `/bpm/cases/${id}`, { environment, body: { case: patched } }));
92
+ return withNormalization(attachFindings({ patchTargets: resolved.targets, case: saved.case ?? patched }, findings), resolved.notes);
93
+ }));
94
+ server.registerTool('case_definition_delete', {
95
+ title: 'Vaka tanımını sil',
96
+ description: "Vaka tanımını ve sürümlerini siler (DELETE bpm/cases/{id}). Bir süreç tanımının 'case' düğümü başvuruyorsa 422 ReferencedByProcess; herhangi bir vakası (açık ya da kapalı) varsa 400 InvalidOperation.",
97
+ inputSchema: { project: projectParam, environment: environmentParam, id: guid },
98
+ annotations: destructive
99
+ }, handle(config, async ({ project, environment, id }) => {
100
+ assertProcessNotProduction('case_definition_delete', environment, config);
101
+ return platform.tenant('DELETE', project, `/bpm/cases/${id}`, { environment });
102
+ }));
103
+ server.registerTool('case_definition_preview', {
104
+ title: 'Vaka modelini denetle (kuru)',
105
+ description: "Vaka modelini YAN ETKİSİZ ve İSTEK GÖNDERMEDEN gateway kurallarıyla denetler (platform_guide('case') §Model) (CASE-001: onPart/entry/exit başvuruları). Yanıt { valid, policyFindings }. Backend'de ayrı doğrulama ucu yoktur: kalan kurallar (kebab-case kimlik, parent döngüsü, human-task assignee, repeatable için giriş sentry'si, onPart olay türü) case_definition_create/update yazımında 422 CaseInvalid bulgu listesiyle döner.",
106
+ inputSchema: { case: caseParam },
107
+ annotations: readOnly
108
+ }, handle(config, async ({ case: caseModel }) => {
109
+ const findings = caseFindings(caseModel);
110
+ return { valid: !findings.some(finding => finding.severity === 'block'), policyFindings: findings };
111
+ }));
112
+ server.registerTool('case_definition_publish', {
113
+ title: 'Vaka tanımını yayınla',
114
+ description: "Taslağı aktif sürüm yapar (POST bpm/cases/{id}/publish); önceki aktif sürüm retiring olur, çalışan vakalar başladıkları sürümde kalır. Gateway yayından önce taslağı CASE-001 ile denetler. production hedefi durur (BPM-001).",
115
+ inputSchema: { project: projectParam, environment: environmentParam, id: guid, policyOverride: policyOverrideParam },
116
+ annotations: writes
117
+ }, handle(config, async ({ project, environment, id, policyOverride }) => {
118
+ assertProcessNotProduction('case_definition_publish', environment, config);
119
+ const current = dataOf(await platform.tenant('GET', project, `/bpm/cases/${id}`, { environment }));
120
+ const findings = lintCase(config, (current.case ?? {}), policyOverride);
121
+ return attachFindings(dataOf(await platform.tenant('POST', project, `/bpm/cases/${id}/publish`, { environment })), findings);
122
+ }));
123
+ server.registerTool('case_cmmn_get', {
124
+ title: 'Vaka tanımını CMMN 1.1 XML olarak dışa aktar',
125
+ description: "Vaka tanımını CMMN 1.1 XML'e çevirir (GET bpm/cases/{id}/cmmn; zeyrek: uzantısı atamayı, çok dilli ad/açıklamayı, vaka dosyası varsayılanını ve sırayı taşır): taslak, yoksa aktif, yoksa son sürüm. Yanıt { caseId, versionNumber, state, sizeBytes, xml }; 256 KB üstü için outPath verin.",
126
+ inputSchema: {
127
+ project: projectParam,
128
+ environment: environmentParam,
129
+ id: guid,
130
+ outPath: z.string().min(1).optional().describe('verilirse XML bu yerel yola yazılır ve yanıt yalnız özet döner')
131
+ },
132
+ annotations: readOnly
133
+ }, handle(config, async ({ project, environment, id, outPath }) => {
134
+ const response = await platform.tenantWithHeaders('GET', project, `/bpm/cases/${id}/cmmn`, { environment, expectBinary: true });
135
+ const payload = response.body;
136
+ const xml = payload.bytes.toString('utf8').replace(/^/, '');
137
+ const summary = {
138
+ caseId: id,
139
+ versionNumber: Number(response.headers['x-case-version']) || null,
140
+ state: response.headers['x-case-version-state'] ?? null,
141
+ sizeBytes: payload.bytes.byteLength
142
+ };
143
+ if (outPath)
144
+ return { ...summary, path: await writeLocalFile('case_cmmn_get', outPath, xml) };
145
+ const inline = { ...summary, xml };
146
+ assertInline('case_cmmn_get', config, inline);
147
+ return inline;
148
+ }));
149
+ server.registerTool('case_cmmn_update', {
150
+ title: 'CMMN 1.1 XML içe aktar',
151
+ description: "CMMN 1.1 XML'deki vakayı içe aktarır (POST bpm/cases/import-cmmn): case id'si (slug) mevcut tanımın taslağına yazılır, yoksa yeni tanım oluşur. xml (≤ 256 KB) ya da filePath (≤ 5 MB) — biri. stage/humanTask/milestone dışı plan öğesi (processTask, caseTask, event listener), çözülmeyen başvuru ve DTD/dış varlık içeren XML 400 InvalidParameter; model kuralları 422 CaseInvalid.",
152
+ inputSchema: {
153
+ project: projectParam,
154
+ environment: environmentParam,
155
+ applicationId: guid.optional(),
156
+ xml: z.string().min(1).max(maxInlineXmlBytes).optional(),
157
+ filePath: z.string().min(1).optional().describe('yerel .cmmn dosyası')
158
+ },
159
+ annotations: updates
160
+ }, handle(config, async ({ project, environment, applicationId, xml, filePath }) => {
161
+ assertProcessNotProduction('case_cmmn_update', environment, config);
162
+ if (Boolean(xml) === Boolean(filePath))
163
+ throw new PlatformApiError("case_cmmn_update: 'xml' ya da 'filePath' — yalnız biri (istek gönderilmedi).", 400);
164
+ let content = xml ?? '';
165
+ if (filePath) {
166
+ try {
167
+ content = await readFile(resolve(filePath), 'utf8');
168
+ }
169
+ catch (error) {
170
+ throw new PlatformApiError(`case_cmmn_update: dosya okunamadı — ${resolve(filePath)} (${error instanceof Error ? error.message : String(error)}). Yol gateway sürecinin çalıştığı makineye göre çözülür (istek gönderilmedi).`, 400);
171
+ }
172
+ }
173
+ content = content.replace(/^/, '');
174
+ if (Buffer.byteLength(content, 'utf8') > maxCmmnBytes)
175
+ throw new PlatformApiError(`case_cmmn_update: XML en çok ${maxCmmnBytes} B (istek gönderilmedi).`, 413);
176
+ return platform.tenant('POST', project, '/bpm/cases/import-cmmn', { environment, body: { applicationId, xml: content } });
177
+ }));
178
+ }