@appaflytech/zeyrek-ai-mcp 0.0.28 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +15 -0
- package/README.md +5 -5
- package/dist/config.js +37 -0
- package/dist/index.js +2 -0
- package/dist/knowledge/access.js +802 -0
- package/dist/knowledge/backendConstraints.js +18 -1
- package/dist/knowledge/case.js +41 -0
- package/dist/knowledge/componentCatalog.js +494 -34
- package/dist/knowledge/componentEvents.js +132 -7
- package/dist/knowledge/connectDataBind.js +819 -0
- package/dist/knowledge/connectDataDefinition.js +341 -0
- package/dist/knowledge/connectDataExecute.js +93 -0
- package/dist/knowledge/connectDataRules.js +128 -0
- package/dist/knowledge/decision.js +121 -0
- package/dist/knowledge/expressionPrelude.js +59 -0
- package/dist/knowledge/layoutSummary.js +140 -2
- package/dist/knowledge/microserviceParameters.js +58 -0
- package/dist/knowledge/mining.js +94 -0
- package/dist/knowledge/mobileComponentCatalog.js +859 -0
- package/dist/knowledge/mobileComponentEvents.js +239 -0
- package/dist/knowledge/normalize.js +236 -67
- package/dist/knowledge/outlines.js +36 -0
- package/dist/knowledge/platform/lint.js +385 -0
- package/dist/knowledge/platform/mobile.js +142 -0
- package/dist/knowledge/platform/mobileLayoutLint.js +324 -0
- package/dist/knowledge/platform/mobileRules.js +231 -0
- package/dist/knowledge/platform/mobileSpacing.js +82 -0
- package/dist/knowledge/platform/mobileTabs.js +108 -0
- package/dist/knowledge/platform/nativeBoxModel.js +44 -0
- package/dist/knowledge/platform/profile.js +25 -0
- package/dist/knowledge/platform/resolve.js +122 -0
- package/dist/knowledge/platform/web.js +39 -0
- package/dist/knowledge/policy.js +18 -4
- package/dist/knowledge/process.js +510 -0
- package/dist/knowledge/reservedTables.js +79 -0
- package/dist/knowledge/rowFilter.js +43 -0
- package/dist/knowledge/rules.js +319 -21
- package/dist/knowledge/schemaGuard.js +11 -0
- package/dist/knowledge/styleResolve.js +50 -4
- package/dist/knowledge/styleResolveNative.js +527 -0
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/observability/trace.js +6 -3
- package/dist/platform/argumentAliases.js +128 -0
- package/dist/platform/bodyFile.js +103 -0
- package/dist/platform/capture/actions.js +333 -0
- package/dist/platform/capture/browser.js +1 -0
- package/dist/platform/{screenshot.js → capture/capture.js} +105 -519
- package/dist/platform/capture/console-log.js +122 -0
- package/dist/platform/capture/index.js +11 -0
- package/dist/platform/capture/readiness.js +103 -0
- package/dist/platform/capture/summary.js +77 -0
- package/dist/platform/capture/target.js +5 -0
- package/dist/platform/capture/targets/studio-mobile.js +15 -0
- package/dist/platform/capture/targets/studio-web.js +5 -0
- package/dist/platform/diagnostics.js +10 -4
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/errors.js +162 -10
- package/dist/platform/http.js +29 -8
- package/dist/platform/impersonation.js +45 -0
- package/dist/platform/jsonArray.js +24 -0
- package/dist/platform/responses.js +39 -5
- package/dist/platform/schemaReadMemory.js +60 -0
- package/dist/platform/secrets.js +17 -1
- package/dist/platform/strictArguments.js +68 -12
- package/dist/platform/tokenVault.js +187 -4
- package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
- package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
- package/dist/resources/examples/bpm-satinalma.json +376 -0
- package/dist/resources/examples/connect-data-convert-verify.json +107 -0
- package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
- package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
- package/dist/resources/examples/connect-data-query-params.json +50 -0
- package/dist/resources/examples/connect-data-table-entity.json +147 -0
- package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
- package/dist/resources/examples/mobile-app-shell-drawer.json +243 -0
- package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
- package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
- package/dist/resources/examples/mobile-form-screen.json +365 -0
- package/dist/resources/examples/mobile-list-detail-form.json +672 -0
- package/dist/resources/examples/page-detail-profile.json +2 -1
- package/dist/resources/examples/page-dynamic-form.json +3 -2
- package/dist/resources/examples/page-flex-sections.json +1 -1
- package/dist/resources/examples/page-html-embed.json +1 -1
- package/dist/resources/examples/workflow-basic.json +8 -2
- package/dist/resources/guides/access.md +796 -0
- package/dist/resources/guides/bpm.md +686 -0
- package/dist/resources/guides/case.md +153 -0
- package/dist/resources/guides/component.md +91 -4
- package/dist/resources/guides/decision.md +185 -0
- package/dist/resources/guides/entity.md +50 -8
- package/dist/resources/guides/expressions.md +14 -5
- package/dist/resources/guides/functions.md +1 -1
- package/dist/resources/guides/getting-started.md +38 -1
- package/dist/resources/guides/layout.md +11 -7
- package/dist/resources/guides/microservice.md +81 -2
- package/dist/resources/guides/middleware.md +44 -2
- package/dist/resources/guides/mobile-layout.md +193 -0
- package/dist/resources/guides/mobile.md +495 -0
- package/dist/resources/guides/page.md +454 -75
- package/dist/resources/guides/theme.md +8 -2
- package/dist/resources/guides/translation.md +1 -1
- package/dist/resources/guides/workflow.md +58 -1
- package/dist/resources/read.js +41 -0
- package/dist/resources/rules.js +11 -5
- package/dist/resources/schema-fingerprint.json +7 -5
- package/dist/resources/schemas/AccessExplanationDto.json +144 -0
- package/dist/resources/schemas/AccessMatrixDto.json +170 -0
- package/dist/resources/schemas/AccessPermissionDto.json +57 -0
- package/dist/resources/schemas/AccessPolicyDto.json +223 -0
- package/dist/resources/schemas/AccessResourceDto.json +86 -0
- package/dist/resources/schemas/AppSessionDto.json +53 -0
- package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
- package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
- package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
- package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
- package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
- package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
- package/dist/resources/schemas/CreateEntityCommand.json +118 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
- package/dist/resources/schemas/CreateQueryCommand.json +4 -0
- package/dist/resources/schemas/CreateWorkflowCommand.json +143 -1
- package/dist/resources/schemas/DataJobDto.json +6 -1
- package/dist/resources/schemas/DecisionDto.json +302 -0
- package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
- package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
- package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +21 -1
- package/dist/resources/schemas/MicroserviceDto.json +15 -3
- package/dist/resources/schemas/OrganizationProjectDto.json +335 -0
- package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
- package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
- package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
- package/dist/resources/schemas/PreviewWorkflowCommand.json +22 -1
- package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
- package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
- package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
- package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
- package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
- package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
- package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
- package/dist/resources/schemas/StepDto.json +22 -1
- package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
- package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
- package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
- package/dist/resources/schemas/UpdateViewCommand.json +0 -2
- package/dist/resources/schemas/UpdateWorkflowCommand.json +143 -1
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
- package/dist/server.js +27 -1
- package/dist/tools/access.js +369 -2
- package/dist/tools/accessOperations.js +135 -0
- package/dist/tools/accessPolicies.js +170 -0
- package/dist/tools/accessRuntime.js +203 -0
- package/dist/tools/applications.js +60 -3
- package/dist/tools/bpm.js +343 -0
- package/dist/tools/bpmGovernance.js +266 -0
- package/dist/tools/bpmOperations.js +230 -0
- package/dist/tools/bpmReporting.js +154 -0
- package/dist/tools/bpmRouting.js +150 -0
- package/dist/tools/bpmRuntime.js +396 -0
- package/dist/tools/case.js +178 -0
- package/dist/tools/caseRuntime.js +89 -0
- package/dist/tools/common.js +7 -0
- package/dist/tools/components.js +118 -14
- package/dist/tools/connectData.js +449 -0
- package/dist/tools/connectors.js +193 -0
- package/dist/tools/datasources.js +3 -3
- package/dist/tools/decisions.js +206 -0
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +2 -2
- package/dist/tools/designSchemaPatch.js +34 -9
- package/dist/tools/dtos.js +36 -5
- package/dist/tools/entities.js +79 -12
- package/dist/tools/externalComponents.js +26 -5
- package/dist/tools/flows.js +122 -14
- package/dist/tools/folders.js +1 -1
- package/dist/tools/imports.js +5 -2
- package/dist/tools/layouts.js +50 -17
- package/dist/tools/merges.js +1 -1
- package/dist/tools/microservices.js +77 -25
- package/dist/tools/mobile.js +221 -0
- package/dist/tools/modules.js +48 -0
- package/dist/tools/organizationStructure.js +137 -0
- package/dist/tools/pages.js +465 -98
- package/dist/tools/platformInfo.js +27 -8
- package/dist/tools/profileWrite.js +92 -0
- package/dist/tools/projects.js +71 -9
- package/dist/tools/rows.js +69 -13
- package/dist/tools/schemaOps.js +61 -1
- package/dist/tools/screenWrite.js +37 -8
- package/dist/tools/screens.js +95 -5
- package/dist/tools/templates.js +26 -1
- package/dist/tools/translations.js +1 -1
- package/dist/tools/variables.js +11 -5
- package/dist/tools/workflows.js +10 -5
- package/package.json +2 -2
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { guid, projectParam, environmentParam, definitionParam, pagingParams, asUserIdParam, readOnly, writes, updates, destructive, microOps } from './common.js';
|
|
2
|
+
import { normalizeProcessDefinition, overridePatchFindings, processNodeSchemas, bpmnExtensionFindings, staleScenarioFindings } from '../knowledge/process.js';
|
|
3
|
+
import { evaluatePolicy, enforcePolicy, attachFindings, applyPolicyMode, PolicyBlockError } from '../knowledge/policy.js';
|
|
4
|
+
import { processOutline, resolvePart, resolvePatchOperations } from '../knowledge/outlines.js';
|
|
5
|
+
import { unmappedMembers, withUnmappedMembers } from '../knowledge/schemaGuard.js';
|
|
6
|
+
import { constraintSummary } from '../knowledge/backendConstraints.js';
|
|
7
|
+
import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
|
|
8
|
+
import { withNormalization } from '../knowledge/normalize.js';
|
|
9
|
+
import { impersonationHeaders } from '../platform/impersonation.js';
|
|
10
|
+
import { identityHeaders } from '../platform/tokenVault.js';
|
|
11
|
+
import { PlatformApiError } from '../platform/errors.js';
|
|
12
|
+
import { baseVersionOf } from '../platform/patch.js';
|
|
13
|
+
import { policyRules } from '../knowledge/rules.js';
|
|
14
|
+
import { handle } from '../platform/responses.js';
|
|
15
|
+
import fastJsonPatch from 'fast-json-patch';
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
const policyOverrideParam = z.boolean().optional()
|
|
18
|
+
.describe("true: politika block bulgularını warn'a indirir (bilinçli istisna; yanıtta işaretlenir)");
|
|
19
|
+
const patchOperationsParam = z.array(z.record(z.unknown())).min(1)
|
|
20
|
+
.describe("RFC 6902 JSON Patch işlemleri, yollar tanım modeline göre. Düğüm hedeflerken İNDEKS YERİNE SEÇİCİ: { op: 'replace', path: '/nodes[id=kontrol-onay]/policy', value: 'all' }; bölge içi düğüm '/nodes[id=bolge]/nodes[id=onay]/…'. Platform seçicisiz sayısal '/nodes/2' yolunu reddeder. Yeni düğüm '/nodes/-'.");
|
|
21
|
+
export function assertProcessNotProduction(tool, environment, config) {
|
|
22
|
+
if ((environment ?? config.defaultEnvironment) !== 'production')
|
|
23
|
+
return;
|
|
24
|
+
throw new PolicyBlockError([{
|
|
25
|
+
ruleId: 'BPM-001',
|
|
26
|
+
severity: 'block',
|
|
27
|
+
message: `${tool}: süreç yayını ve yürütmesi production'a gönderilmez. Hedef 'development' ya da bir dal olmalı; production'a taşıma insan onaylı deployment işidir.`,
|
|
28
|
+
guide: 'ngapps://guides/bpm'
|
|
29
|
+
}], false);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* BPM uçları (`/bpm/*`) platform şemasıyla `[Authorize]` edilir: uygulama oturumu jetonunu
|
|
33
|
+
* `Authorization` başlığına koymak kimlik doğrulamayı düşürür ve 401 verir (ölçüldü 2026-09-22,
|
|
34
|
+
* S-1 koşusu). Uygulama kullanıcısı adına iş yapmanın desteklenen yolu `impersonated-user-id`
|
|
35
|
+
* başlığıdır: `AppSessionMiddleware` platform yolundaki istekte çerezi okumaz ama kimliği
|
|
36
|
+
* doğrulanmış istekte bu başlığı görürse özneyi o kullanıcı olarak kurar (production dışında).
|
|
37
|
+
*/
|
|
38
|
+
export async function bpmIdentityHeaders(platform, tool, project, environment, identity) {
|
|
39
|
+
const eye = await impersonationHeaders(platform, tool, project, environment, identity, { runtime: true });
|
|
40
|
+
return eye ?? identityHeaders(identity.asUser, undefined, undefined);
|
|
41
|
+
}
|
|
42
|
+
const asUserIdBpmParam = asUserIdParam
|
|
43
|
+
.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 alabilir — parolasız yol budur. Yalnız development ya da dal; asUser ile birlikte verilemez.");
|
|
44
|
+
const asUserParam = z.string().min(1).optional()
|
|
45
|
+
.describe("Uygulama yöneticisinin kimliği (ham JWT ya da access_session_create tokenRefs'teki tok_… referansı). Uyarlama yazımı zorunlu ister (BPM-013): Studio jetonu uyarlama yazamaz.");
|
|
46
|
+
const overrideOperations = ['save', 'reset', 'migrate-open'];
|
|
47
|
+
function overrideOutline(view) {
|
|
48
|
+
const definitionOverride = (view.override ?? null);
|
|
49
|
+
return {
|
|
50
|
+
definitionId: view.definitionId ?? null,
|
|
51
|
+
slug: view.slug ?? null,
|
|
52
|
+
activeVersionNumber: view.activeVersionNumber ?? null,
|
|
53
|
+
overrideRevision: view.overrideRevision ?? 0,
|
|
54
|
+
isReadOnly: view.isReadOnly === true,
|
|
55
|
+
conflictState: definitionOverride?.conflictState ?? 'none',
|
|
56
|
+
conflictDetail: definitionOverride?.conflictDetail ?? null,
|
|
57
|
+
patch: definitionOverride?.patch ?? [],
|
|
58
|
+
editableScopes: view.editableScopes ?? [],
|
|
59
|
+
outline: {
|
|
60
|
+
...processOutline({ id: view.definitionId, slug: view.slug, definition: view.effectiveModel ?? {} }),
|
|
61
|
+
hint: "Etkin tanım (aktif sürüm + uyarlama). Uyarlamayı process_override_update ile değiştirin; process_definition_patch geliştiricinin taslağını değiştirir, uyarlamayı değil."
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function dataOf(payload) {
|
|
66
|
+
const record = (payload ?? {});
|
|
67
|
+
return (record.data && typeof record.data === 'object' ? record.data : record);
|
|
68
|
+
}
|
|
69
|
+
export function registerBpmTools(server, platform) {
|
|
70
|
+
const config = platform.config;
|
|
71
|
+
server.registerTool('process_definition_list', {
|
|
72
|
+
title: 'Süreç tanımlarını listele',
|
|
73
|
+
description: "Süreç (BPM) tanımlarını listeler: slug, ad, aktif sürüm. Süreç ≠ workflow: süreç insan görevlerinde bekleyen durumlu akıştır, workflow tek istekte biten sunucu akışıdır. view:'export' projedeki bütün tanımları (tüm sürümler, taslak, RACI) ve iş takvimlerini yedek olarak döner — { exportedAt, calendars[], definitions[] }; geri yükleme process_definition_create { restore }. Yedek büyüktür, yalnız yedek için çağırın; yetki process.manage.",
|
|
74
|
+
inputSchema: { project: projectParam, environment: environmentParam, view: z.enum(['list', 'export']).optional().describe('varsayılan list'), searchTerm: z.string().optional(), applicationId: guid.optional(), ...pagingParams },
|
|
75
|
+
annotations: readOnly
|
|
76
|
+
}, handle(config, ({ project, environment, view, ...query }) => view === 'export'
|
|
77
|
+
? platform.tenant('GET', project, '/bpm/export-definitions', { environment })
|
|
78
|
+
: platform.tenant('GET', project, '/bpm/definitions', { environment, query })));
|
|
79
|
+
server.registerTool('process_definition_get', {
|
|
80
|
+
title: 'Süreç tanımını getir',
|
|
81
|
+
description: "Süreç tanımını getirir. Önce view:'outline' (düğüm · tür · derece · atama · SLA · bölge olayları); tek düğüm için view:'part', part:'definition.nodes[id=<düğüm>]'. Tam gövde son çaredir.",
|
|
82
|
+
inputSchema: {
|
|
83
|
+
project: projectParam,
|
|
84
|
+
environment: environmentParam,
|
|
85
|
+
id: guid,
|
|
86
|
+
view: z.enum(['full', 'outline', 'part']).optional().describe('varsayılan outline'),
|
|
87
|
+
part: z.string().optional().describe("view:'part' için yol, ör. definition.nodes[id=onay]")
|
|
88
|
+
},
|
|
89
|
+
annotations: readOnly
|
|
90
|
+
}, handle(config, async ({ project, environment, id, view, part }) => {
|
|
91
|
+
const payload = await platform.tenant('GET', project, `/bpm/definitions/${id}`, { environment });
|
|
92
|
+
const definition = dataOf(payload);
|
|
93
|
+
if (view === 'full')
|
|
94
|
+
return payload;
|
|
95
|
+
if (view === 'part') {
|
|
96
|
+
if (!part)
|
|
97
|
+
throw new Error("view:'part' için 'part' parametresi zorunlu.");
|
|
98
|
+
return { part, value: resolvePart(definition, part) };
|
|
99
|
+
}
|
|
100
|
+
return processOutline(definition);
|
|
101
|
+
}));
|
|
102
|
+
server.registerTool('process_definition_create', {
|
|
103
|
+
title: 'Süreç tanımı oluştur',
|
|
104
|
+
description: "Yeni süreç tanımı yazar (taslak sürüm). bpmn verilirse BPMN 2.0 XML'den tanım oluşturur (POST bpm/definitions/import-bpmn; ≤ 256 KB, büyük dosya için boş tanım + process_bpmn_update { filePath }; zeyrek: uzantısız XML'de derece locked, insan adımı ataması yer tutucu 'initiator' — BPM-016 uyarısı; workflow'suz serviceTask 422). İlk kullanımdan önce platform_guide('bpm') okuyun; altın örnekler ngapps://examples/bpm-gorev-kontrol, bpm-satinalma, bpm-iso-dokuman. Tanım ve HER düğüm 'description' taşır (BPM-002, block). Tanım 'analysis: { purpose, summary }', HER düğüm 'analysis: { what, data, why, example }' taşır, her metin { en, tr } iş diliyle; example adımın gerçek hayattan küçük bir vakası (BPM-023, block; Studio'nun Analiz dokümanı sekmesi okur — platform_guide('bpm') § Analiz dokümanı). Kısa biçimler (assignee: 'unit-manager of initiator', sla: 'P2D', edges: [[from, to]]) gateway'de tam biçime çevrilir ve gatewayNormalized ile bildirilir. Yanıt outline'dır; yayın için process_definition_preview ardından process_definition_publish. " + constraintSummary('CreateProcessDefinitionCommand'),
|
|
105
|
+
inputSchema: {
|
|
106
|
+
project: projectParam,
|
|
107
|
+
environment: environmentParam,
|
|
108
|
+
applicationId: guid.optional(),
|
|
109
|
+
definition: definitionParam.optional().describe('yeni tanım; restore ya da bpmn verilmezse zorunlu'),
|
|
110
|
+
bpmn: z.string().min(1).max(256_000).optional().describe('BPMN 2.0 XML — definition ve restore yerine'),
|
|
111
|
+
restore: z.object({
|
|
112
|
+
exportedAt: z.string().min(10).max(40),
|
|
113
|
+
calendars: z.array(z.record(z.unknown())).max(500),
|
|
114
|
+
definitions: z.array(z.record(z.unknown())).max(1000),
|
|
115
|
+
confirmOverwrite: z.literal(true).describe('YIKICI: aynı kodlu takvimler ve değişmiş aktif sürümler üzerine yazılır — bilerek true verin')
|
|
116
|
+
}).optional().describe("YIKICI geri yükleme — process_definition_list { view: 'export' } çıktısının data'sı + confirmOverwrite: true. Takvimler üzerine yazılır; tanımlar içe aktarılır (aktif sürümü farklı olan yeni sürümle YAYINLANIR; hedefte yayınlanmamış farklı taslak varsa o tanım atlanır ve skippedSlugs'ta döner); yedekte olmayan tanım silinmez, emekliye ayrılmaz. Her aktif/taslak sürüm BPM kurallarından geçer (BPM-002 …). production'a gitmez (BPM-001)."),
|
|
117
|
+
policyOverride: policyOverrideParam
|
|
118
|
+
},
|
|
119
|
+
annotations: writes
|
|
120
|
+
}, handle(config, async ({ project, environment, applicationId, definition, bpmn, restore, policyOverride }) => {
|
|
121
|
+
if (bpmn) {
|
|
122
|
+
if (definition || restore)
|
|
123
|
+
throw new PlatformApiError('process_definition_create: bpmn, definition ve restore birlikte verilmez (istek gönderilmedi).', 400);
|
|
124
|
+
const bpmnFindings = config.policyMode === 'off' ? [] : applyPolicyMode(config, bpmnExtensionFindings(bpmn), policyOverride);
|
|
125
|
+
return attachFindings(processOutline(dataOf(await platform.tenant('POST', project, '/bpm/definitions/import-bpmn', { environment, body: { applicationId, xml: bpmn } }))), bpmnFindings);
|
|
126
|
+
}
|
|
127
|
+
if (restore) {
|
|
128
|
+
if (definition)
|
|
129
|
+
throw new PlatformApiError('process_definition_create: definition ve restore birlikte verilmez (istek gönderilmedi).', 400);
|
|
130
|
+
assertProcessNotProduction('process_definition_create', environment, config);
|
|
131
|
+
const restoreFindings = enforcePolicy(restore.definitions.flatMap(restoredDefinition => (restoredDefinition.versions ?? [])
|
|
132
|
+
.filter(version => (version.state === 'active' || version.state === 'draft') && version.model)
|
|
133
|
+
.flatMap(version => evaluatePolicy(config, policyRules, 'process', version.model, { operation: 'create' }, policyOverride))));
|
|
134
|
+
const { confirmOverwrite: _confirmOverwrite, ...restoreBody } = restore;
|
|
135
|
+
return attachFindings(await platform.tenant('POST', project, '/bpm/export-definitions', { environment, body: restoreBody }), restoreFindings);
|
|
136
|
+
}
|
|
137
|
+
if (!definition)
|
|
138
|
+
throw new PlatformApiError('process_definition_create: definition zorunlu (ya da yedekten geri yükleme için restore) (istek gönderilmedi).', 400);
|
|
139
|
+
const normalized = normalizeProcessDefinition(definition);
|
|
140
|
+
const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'process', definition, { operation: 'create' }, policyOverride));
|
|
141
|
+
const drift = unmappedMembers('CreateProcessDefinitionCommand', { applicationId, definition });
|
|
142
|
+
const payload = await platform.tenant('POST', project, '/bpm/definitions', { environment, body: { applicationId, definition } });
|
|
143
|
+
const saved = dataOf(payload);
|
|
144
|
+
return attachFindings(withUnmappedMembers(withWriteDiff(withNormalization(processOutline(saved), normalized), writeDiff(definition, saved.definition)), drift), findings);
|
|
145
|
+
}));
|
|
146
|
+
server.registerTool('process_definition_update', {
|
|
147
|
+
title: 'Süreç tanımını güncelle',
|
|
148
|
+
description: "Taslak sürümü tam gövdeyle günceller (definition = tanım modeli). Hedefli değişiklik için process_definition_patch tercih edin. Yayınlanmış tanımda slug değişmez. Tanım ve her düğüm 'description' taşır (BPM-002). İki dilli 'analysis' tanımda ve her düğümde zorunludur (BPM-023). " + constraintSummary('UpdateProcessDefinitionCommand'),
|
|
149
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, definition: definitionParam, policyOverride: policyOverrideParam },
|
|
150
|
+
annotations: updates
|
|
151
|
+
}, handle(config, async ({ project, environment, id, definition, policyOverride }) => {
|
|
152
|
+
const normalized = normalizeProcessDefinition(definition);
|
|
153
|
+
const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'process', definition, { operation: 'update' }, policyOverride));
|
|
154
|
+
const payload = await platform.tenant('PUT', project, `/bpm/definitions/${id}`, { environment, body: { definition } });
|
|
155
|
+
const saved = dataOf(payload);
|
|
156
|
+
return attachFindings(withWriteDiff(withNormalization(processOutline(saved), normalized), writeDiff(definition, saved.definition)), findings);
|
|
157
|
+
}));
|
|
158
|
+
server.registerTool('process_definition_patch', {
|
|
159
|
+
title: "Süreç tanımına patch uygula",
|
|
160
|
+
description: "Tam gövdeyi bağlama almadan hedefli değişiklik: gateway okunan kaydın 'version'ını baseVersion olarak gönderir, platform seçicileri (/nodes[id=x]) çözer ve taslağa yazar; kayıt bu arada değiştiyse 409 döner ve hiçbir op uygulanmaz. Gateway yazmadan önce yamalanmış tanımı yerelde politika kontrolünden geçirir. Analizi olmayan eski bir tanım her yamada BPM-023'e takılır: eksik 'analysis' alanlarını aynı yamaya ekleyin. Yanıt { applied, newVersion, patchTargets, outline }.",
|
|
161
|
+
inputSchema: {
|
|
162
|
+
project: projectParam,
|
|
163
|
+
environment: environmentParam,
|
|
164
|
+
id: guid,
|
|
165
|
+
operations: patchOperationsParam.optional(),
|
|
166
|
+
ops: patchOperationsParam.optional().describe("'operations' ile aynı alan (eşanlamlı)"),
|
|
167
|
+
policyOverride: policyOverrideParam
|
|
168
|
+
},
|
|
169
|
+
annotations: updates
|
|
170
|
+
}, handle(config, async ({ project, environment, id, operations: givenOperations, ops, policyOverride }) => {
|
|
171
|
+
const operations = microOps('process_definition_patch', ops, givenOperations);
|
|
172
|
+
const current = dataOf(await platform.tenant('GET', project, `/bpm/definitions/${id}`, { environment }));
|
|
173
|
+
const model = structuredClone((current.definition ?? {}));
|
|
174
|
+
const notes = [];
|
|
175
|
+
try {
|
|
176
|
+
const resolved = resolvePatchOperations(model, operations, 'process_definition_get');
|
|
177
|
+
const patched = fastJsonPatch.applyPatch(model, resolved.operations, true, false).newDocument;
|
|
178
|
+
enforcePolicy(evaluatePolicy(config, policyRules, 'process', patched, { operation: 'update' }, policyOverride));
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
if (error instanceof PolicyBlockError)
|
|
182
|
+
throw error;
|
|
183
|
+
notes.push(`yerel politika denetimi atlandı (yol yerelde çözülemedi: ${error instanceof Error ? error.message : String(error)}); platform doğrulaması yine çalışır`);
|
|
184
|
+
}
|
|
185
|
+
const saved = dataOf(await platform.tenant('PATCH', project, `/bpm/definitions/${id}`, {
|
|
186
|
+
environment,
|
|
187
|
+
body: { baseVersion: baseVersionOf(current, 'process_definition_get'), ops: operations }
|
|
188
|
+
}));
|
|
189
|
+
return withNormalization({
|
|
190
|
+
applied: saved.applied ?? operations.length,
|
|
191
|
+
newVersion: saved.newVersion ?? null,
|
|
192
|
+
patchTargets: saved.patchTargets ?? [],
|
|
193
|
+
outline: processOutline((saved.data ?? {}))
|
|
194
|
+
}, notes);
|
|
195
|
+
}));
|
|
196
|
+
server.registerTool('process_definition_delete', {
|
|
197
|
+
title: 'Süreç tanımını sil',
|
|
198
|
+
description: 'Süreç tanımını siler. Açık örneği (running, waiting-timer, suspended, faulted) olan tanım silinmez (422 ProcessOpen); önce process_instance_list ile kontrol edin.',
|
|
199
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid },
|
|
200
|
+
annotations: destructive
|
|
201
|
+
}, handle(config, ({ project, environment, id }) => platform.tenant('DELETE', project, `/bpm/definitions/${id}`, { environment })));
|
|
202
|
+
server.registerTool('process_definition_clone', {
|
|
203
|
+
title: 'Süreç tanımını klonla',
|
|
204
|
+
description: 'Tanımın güncel modelinden yeni slug ile taslak kopya oluşturur.',
|
|
205
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, slug: z.string().min(1).max(100) },
|
|
206
|
+
annotations: writes
|
|
207
|
+
}, handle(config, async ({ project, environment, id, slug }) => processOutline(dataOf(await platform.tenant('POST', project, `/bpm/definitions/${id}/clone`, { environment, body: { slug } })))));
|
|
208
|
+
server.registerTool('process_definition_preview', {
|
|
209
|
+
title: 'Süreç tanımını önizle',
|
|
210
|
+
description: "YAN ETKİSİZ: tanımı platform doğrulayıcısından geçirir (422 kodlarıyla aynı bulgular) ve id verilirse kuru koşu yapar — her insan adımında KİM görev alacak listesi (org ağacından çözülür, satır/bildirim/görev yazılmaz). Kaydedilmemiş gövde için definition verin; kayıtlı tanım için id (+ subjectRowId, vars, initiatorUserId). Yayından önce findings boş ve steps beklenen kişileri göstermeli. Transaction açmaz, geri alınacak yazma yoktur; kurallar platform_guide('bpm') §Simülasyon ve test.",
|
|
211
|
+
inputSchema: {
|
|
212
|
+
project: projectParam,
|
|
213
|
+
environment: environmentParam,
|
|
214
|
+
id: guid.optional(),
|
|
215
|
+
applicationId: guid.optional(),
|
|
216
|
+
definition: definitionParam.optional(),
|
|
217
|
+
subjectRowId: z.string().max(64).optional(),
|
|
218
|
+
initiatorUserId: z.string().max(64).optional(),
|
|
219
|
+
vars: z.record(z.unknown()).optional()
|
|
220
|
+
},
|
|
221
|
+
annotations: readOnly
|
|
222
|
+
}, handle(config, async ({ project, environment, id, applicationId, definition, subjectRowId, initiatorUserId, vars }) => {
|
|
223
|
+
if (!id && !definition)
|
|
224
|
+
throw new Error("process_definition_preview: 'id' ya da 'definition' verin.");
|
|
225
|
+
if (id && definition)
|
|
226
|
+
throw new Error("process_definition_preview: 'id' ve 'definition' birlikte verilmez — doğrulama kaydedilmemiş gövdeyi, kuru koşu kayıtlı modeli okur ve sonuçlar iki ayrı modele ait olur. Önce process_definition_update ile kaydedin, sonra yalnız id ile önizleyin.");
|
|
227
|
+
const normalized = definition ? normalizeProcessDefinition(definition) : [];
|
|
228
|
+
const validation = dataOf(await platform.tenant('POST', project, id ? `/bpm/definitions/${id}/validate` : '/bpm/definitions/validate', {
|
|
229
|
+
environment,
|
|
230
|
+
body: definition ? { applicationId, definition } : {}
|
|
231
|
+
}));
|
|
232
|
+
const simulation = id
|
|
233
|
+
? dataOf(await platform.tenant('POST', project, `/bpm/definitions/${id}/simulate`, { environment, body: { subjectRowId, initiatorUserId, vars } }))
|
|
234
|
+
: undefined;
|
|
235
|
+
return withNormalization({
|
|
236
|
+
findings: validation.findings ?? [],
|
|
237
|
+
isValid: validation.isValid ?? null,
|
|
238
|
+
steps: simulation?.steps ?? null,
|
|
239
|
+
note: id ? undefined : "Kuru koşu (kim yapar) yalnız kayıtlı tanımda çalışır; önce process_definition_create, sonra id ile preview."
|
|
240
|
+
}, normalized);
|
|
241
|
+
}));
|
|
242
|
+
server.registerTool('process_definition_publish', {
|
|
243
|
+
title: 'Süreç sürümünü yayınla',
|
|
244
|
+
description: "Taslak sürümü aktif yapar (önceki aktif 'retiring' olur; açık örnekler kendi sürümünde biter). reason verilirse yayın isteği açılır (POST publish-request): aktif sürüm ya da taslak publishPolicy: four-eyes ve ortamda process-publish süreci kuruluysa sürüm YAYINLANMAZ — { isPublished: false, request { id, state: pending, instanceId, changeCount } } döner, onay görevi tanım sahibine düşer, onaylanınca yayınlanır (ret rejected; taslak sonradan değişirse superseded). Onay gerekmiyorsa istek doğrudan yayınlar (isPublished: true). reason'sız çağrı onay gereken tanımda 403 PublishApprovalRequired alır. Gateway taslak sağlamasında yeşil koşmamış senaryoyu uyarır (BPM-015); requireGreenScenarios: true tanımda platform 422 ScenarioFailed döner — istek açılırken de. Yalnız isteyenden farklı kullanıcının onayı yayınlar. Yayın öncesi platform doğrulaması 422 ile durdurabilir. production hedefi istek gitmeden durur (BPM-001).",
|
|
245
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, reason: z.string().min(1).max(1000).optional().describe('yayın gerekçesi; verilirse dört göz isteği açılır') },
|
|
246
|
+
annotations: writes
|
|
247
|
+
}, handle(config, async ({ project, environment, id, reason }) => {
|
|
248
|
+
assertProcessNotProduction('process_definition_publish', environment, config);
|
|
249
|
+
const findings = config.policyMode === 'off'
|
|
250
|
+
? []
|
|
251
|
+
: await platform.tenant('GET', project, `/bpm/definitions/${id}`, { environment })
|
|
252
|
+
.then(async (current) => staleScenarioFindings(dataOf(current), dataOf(await platform.tenant('GET', project, '/bpm/test-scenarios', { environment, query: { definitionId: id } }))))
|
|
253
|
+
.then(staleFindings => applyPolicyMode(config, staleFindings), () => []);
|
|
254
|
+
if (!reason)
|
|
255
|
+
return attachFindings(processOutline(dataOf(await platform.tenant('POST', project, `/bpm/definitions/${id}/versions`, { environment }))), findings);
|
|
256
|
+
const publishResult = dataOf(await platform.tenant('POST', project, `/bpm/definitions/${id}/publish-request`, { environment, body: { reason } }));
|
|
257
|
+
const publishRequest = (publishResult.request ?? null);
|
|
258
|
+
return attachFindings({
|
|
259
|
+
isPublished: publishResult.isPublished === true,
|
|
260
|
+
request: publishRequest ? { id: publishRequest.id, state: publishRequest.state, instanceId: publishRequest.instanceId ?? null, versionNumber: publishRequest.versionNumber ?? null, changeCount: Array.isArray(publishRequest.changes) ? publishRequest.changes.length : 0 } : null,
|
|
261
|
+
note: publishResult.isPublished === true ? undefined : 'Onay bekliyor: onay görevi tanım sahibine düştü; process_task_list ile izleyin. Onay gelmeden sürüm aktif olmaz.',
|
|
262
|
+
outline: processOutline((publishResult.definition ?? {}))
|
|
263
|
+
}, findings);
|
|
264
|
+
}));
|
|
265
|
+
server.registerTool('process_override_get', {
|
|
266
|
+
title: 'Süreç uyarlamasını getir',
|
|
267
|
+
description: "Uygulama yöneticisinin ortam uyarlamasını getirir: aktif sürüm, overrideRevision, conflictState (conflicted ise conflictDetail — yeni sürüm uyarlanan bölgeyi kaldırdı ya da kilitledi, yeni örnekler varsayılanla başlar), patch, editableScopes (düğüm · derece · editableParams · limits) ve etkin tanımın outline'ı. view:'studio' salt okunur görünümdür (isReadOnly: true); output:'full' temel ve etkin modelin tam gövdesi. Kurallar platform_guide('bpm') §Uyarlama.",
|
|
268
|
+
inputSchema: {
|
|
269
|
+
project: projectParam,
|
|
270
|
+
environment: environmentParam,
|
|
271
|
+
id: guid,
|
|
272
|
+
view: z.enum(['admin', 'studio']).optional().describe('varsayılan admin'),
|
|
273
|
+
output: z.enum(['outline', 'full']).optional().describe('varsayılan outline'),
|
|
274
|
+
asUser: asUserParam,
|
|
275
|
+
asUserId: asUserIdBpmParam
|
|
276
|
+
},
|
|
277
|
+
annotations: readOnly
|
|
278
|
+
}, handle(config, async ({ project, environment, id, view, output, asUser, asUserId }) => {
|
|
279
|
+
const payload = await platform.tenant('GET', project, `/bpm/definitions/${id}/override`, { environment, query: view ? { view } : undefined, headers: await bpmIdentityHeaders(platform, 'process_override_get', project, environment, { asUser, asUserId }) });
|
|
280
|
+
return output === 'full' ? payload : overrideOutline(dataOf(payload));
|
|
281
|
+
}));
|
|
282
|
+
server.registerTool('process_override_update', {
|
|
283
|
+
title: 'Süreç uyarlamasını yaz',
|
|
284
|
+
description: "Uyarlama yazımının tek kapısı: operation save (varsayılan; patch = JSON Patch, seçicili yollar '/nodes[id=bolge]/nodes/-'; mode append mevcut uyarlamanın sonuna ekler, replace tümünü değiştirir) | reset (varsayılana dön) | migrate-open (açık örnekleri güncel uyarlamaya taşı; açık düğümü etkin tanımda olmayan örnek atlanır). Gateway aktif sürüm ve revizyonu okuyup iyimser kilitle gönderir (409 → yeniden okuyun) ve istek gitmeden denetler: kilitli yol BPM-010, bölgenin izin vermediği atama kuralı BPM-011, bölge adım/tür sınırı BPM-012. asUser ZORUNLU (BPM-013); production hedefi durur (BPM-001). Yanıt applied op listesi + güncel uyarlama outline'ı.",
|
|
285
|
+
inputSchema: {
|
|
286
|
+
project: projectParam,
|
|
287
|
+
environment: environmentParam,
|
|
288
|
+
id: guid,
|
|
289
|
+
operation: z.enum(overrideOperations).optional().describe('varsayılan save'),
|
|
290
|
+
patch: patchOperationsParam.optional().describe("save için JSON Patch işlemleri, temel (aktif sürüm) modele göre; düğüm hedeflerken seçici: '/nodes[id=kontrol-bolgesi]/nodes/-'"),
|
|
291
|
+
mode: z.enum(['append', 'replace']).optional().describe('save için; varsayılan append (çatışmalı uyarlamada yalnız verilen işlemler yazılır)'),
|
|
292
|
+
policyOverride: policyOverrideParam,
|
|
293
|
+
asUser: asUserParam,
|
|
294
|
+
asUserId: asUserIdBpmParam
|
|
295
|
+
},
|
|
296
|
+
annotations: writes
|
|
297
|
+
}, handle(config, async ({ project, environment, id, operation, patch, mode, policyOverride, asUser, asUserId }) => {
|
|
298
|
+
assertProcessNotProduction('process_override_update', environment, config);
|
|
299
|
+
if (!asUser && !asUserId) {
|
|
300
|
+
throw new PolicyBlockError([{
|
|
301
|
+
ruleId: 'BPM-013',
|
|
302
|
+
severity: 'block',
|
|
303
|
+
message: "process_override_update kimliksiz gönderilmez: uyarlama uygulama yöneticisi adına yazılır, Studio jetonu 403 alır. İki yol var — development'ta parolasız 'asUserId' (users satır id'si) ya da 'asUser' (access_session_create tokenRefs'teki tok_… değeri).",
|
|
304
|
+
guide: 'ngapps://guides/bpm'
|
|
305
|
+
}]);
|
|
306
|
+
}
|
|
307
|
+
const headers = await bpmIdentityHeaders(platform, 'process_override_update', project, environment, { asUser, asUserId });
|
|
308
|
+
if (operation === 'reset')
|
|
309
|
+
return overrideOutline(dataOf(await platform.tenant('POST', project, `/bpm/definitions/${id}/override/reset`, { environment, headers })));
|
|
310
|
+
if (operation === 'migrate-open')
|
|
311
|
+
return dataOf(await platform.tenant('POST', project, `/bpm/definitions/${id}/override/migrate-open`, { environment, headers }));
|
|
312
|
+
if (!patch)
|
|
313
|
+
throw new Error("process_override_update: operation save için 'patch' zorunlu.");
|
|
314
|
+
const view = dataOf(await platform.tenant('GET', project, `/bpm/definitions/${id}/override`, { environment, headers }));
|
|
315
|
+
const currentOverride = (view.override ?? null);
|
|
316
|
+
const isConflicted = currentOverride?.conflictState === 'conflicted';
|
|
317
|
+
const basePatch = mode === 'replace' || isConflicted ? [] : (currentOverride?.patch ?? []);
|
|
318
|
+
const fullPatch = [...basePatch, ...patch];
|
|
319
|
+
const { findings, notes } = config.policyMode === 'off' ? { findings: [], notes: [] } : overridePatchFindings((view.baseModel ?? {}), fullPatch);
|
|
320
|
+
const policyFindings = enforcePolicy(applyPolicyMode(config, findings, policyOverride));
|
|
321
|
+
if (isConflicted && mode !== 'replace')
|
|
322
|
+
notes.push('mevcut uyarlama çatışmalı (conflicted) olduğu için eklenmedi; yalnız verilen işlemler yazıldı');
|
|
323
|
+
const saved = dataOf(await platform.tenant('PUT', project, `/bpm/definitions/${id}/override`, {
|
|
324
|
+
environment,
|
|
325
|
+
headers,
|
|
326
|
+
body: { baseVersion: view.activeVersionNumber, overrideRevision: view.overrideRevision ?? 0, patch: fullPatch }
|
|
327
|
+
}));
|
|
328
|
+
return attachFindings(withNormalization({ applied: patch.map(entry => `${String(entry.op)} ${String(entry.path)}`), ...overrideOutline(saved) }, notes), policyFindings);
|
|
329
|
+
}));
|
|
330
|
+
server.registerTool('process_step_schema', {
|
|
331
|
+
title: 'Süreç düğüm şeması',
|
|
332
|
+
description: "Düğüm türlerinin alanlarını, zorunlularını ve örneğini döner (start, user-task, approval, service, notify, gateway, timer, wait, send, manual, script, sub-process, region, end). Düğüm yazarken tahmin yerine buradan okuyun.",
|
|
333
|
+
inputSchema: { type: z.string().optional().describe('tek tür; boşsa hepsi') },
|
|
334
|
+
annotations: readOnly
|
|
335
|
+
}, handle(config, async ({ type }) => {
|
|
336
|
+
if (!type)
|
|
337
|
+
return { nodeTypes: processNodeSchemas.map(schema => ({ type: schema.type, purpose: schema.purpose, required: schema.required })) };
|
|
338
|
+
const schema = processNodeSchemas.find(candidate => candidate.type === type);
|
|
339
|
+
if (!schema)
|
|
340
|
+
throw new Error(`Bilinmeyen düğüm türü '${type}'. Türler: ${processNodeSchemas.map(candidate => candidate.type).join(', ')}.`);
|
|
341
|
+
return schema;
|
|
342
|
+
}));
|
|
343
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { guid, projectParam, environmentParam, languageParam, readOnly, writes, updates, destructive } from './common.js';
|
|
2
|
+
import { attachFindings, applyPolicyMode } from '../knowledge/policy.js';
|
|
3
|
+
import { assertProcessNotProduction, dataOf } from './bpm.js';
|
|
4
|
+
import { bpmnExtensionFindings } from '../knowledge/process.js';
|
|
5
|
+
import { processOutline } from '../knowledge/outlines.js';
|
|
6
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
7
|
+
import { normalizeLanguageCode } from '../knowledge/languageCodes.js';
|
|
8
|
+
import { withNormalization } from '../knowledge/normalize.js';
|
|
9
|
+
import { PlatformApiError } from '../platform/errors.js';
|
|
10
|
+
import { handle } from '../platform/responses.js';
|
|
11
|
+
import { dirname, resolve } from 'node:path';
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
const maxInlineBytes = 256_000;
|
|
14
|
+
const maxBpmnBytes = 5_000_000;
|
|
15
|
+
const scenarioActions = ['task', 'clock', 'signal', 'message', 'subProcess'];
|
|
16
|
+
const isoDurationPattern = /^P(?!$)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+(\.\d+)?S)?)?$/;
|
|
17
|
+
const versionParam = z.number().int().min(1).optional();
|
|
18
|
+
const outPathParam = z.string().min(1).optional()
|
|
19
|
+
.describe('verilirse içerik bu yerel yola yazılır ve yanıt yalnız özet döner (256 KB üstü içerik için zorunlu)');
|
|
20
|
+
const scenarioGivenParam = z.object({
|
|
21
|
+
subjectRowId: z.string().max(64),
|
|
22
|
+
initiatorUserId: z.string().max(64),
|
|
23
|
+
vars: z.record(z.unknown())
|
|
24
|
+
}).partial().optional().describe('başlangıç: konu satırı, başlatan kullanıcı, değişkenler (parametre varsayılanlarının üstüne yazılır)');
|
|
25
|
+
const scenarioStepsParam = z.array(z.object({
|
|
26
|
+
task: z.string().max(100),
|
|
27
|
+
as: z.string().max(100),
|
|
28
|
+
action: z.string().max(50),
|
|
29
|
+
outcome: z.string().max(100),
|
|
30
|
+
comment: z.string().max(2000),
|
|
31
|
+
form: z.record(z.unknown()),
|
|
32
|
+
clock: z.string().max(40),
|
|
33
|
+
signal: z.string().max(200),
|
|
34
|
+
message: z.string().max(200),
|
|
35
|
+
correlationKey: z.string().max(200),
|
|
36
|
+
payload: z.record(z.unknown()),
|
|
37
|
+
subProcess: z.string().max(100)
|
|
38
|
+
}).partial()).max(200).optional()
|
|
39
|
+
.describe("adımlar sırayla; her adım TAM OLARAK BİR eylem: { task: '<düğüm>', as: 'user:<id>', action: 'approve' | 'reject' | 'complete' | 'sign' | 'request-info' | 'provide-info' | '<eylem>', form?, comment? } (görev verisi form'dadır; request-info'da comment soru, provide-info'da başlatanın yanıtı) · { clock: 'P3D' } (vadesi gelen zamanlayıcılar ateşlenir) · { signal, payload } ({ signal: 'withdraw', as: 'user:<başlatan>' } withdrawPolicy'ye göre geri çeker) · { message, correlationKey, payload } · { subProcess: '<düğüm>', outcome }. signature TAŞIYAN düğümde 'approve'/'complete' adımı 422 SignatureRequired ile düşer — o adımı 'sign' ile yazın (senaryo parola istemez, defterine TaskSigned yazar; ret ve çekimser imzasız geçer)");
|
|
40
|
+
const scenarioExpectParam = z.object({
|
|
41
|
+
path: z.array(z.string().max(100)).max(500),
|
|
42
|
+
assignees: z.record(z.array(z.string().max(100)).max(50)),
|
|
43
|
+
outcome: z.string().max(100),
|
|
44
|
+
state: z.string().max(40),
|
|
45
|
+
vars: z.record(z.unknown()),
|
|
46
|
+
writeBack: z.record(z.unknown())
|
|
47
|
+
}).partial().optional()
|
|
48
|
+
.describe('beklenenler (yazılmayan denetlenmez): path girilen düğüm dizisi birebir · assignees düğüm → kullanıcılar (alt küme) · outcome · state · vars (nokta yolu) · writeBack (satıra yazılacak değerler; koşuda satıra yazılmaz)');
|
|
49
|
+
export async function writeLocalFile(tool, path, content) {
|
|
50
|
+
const absolute = resolve(path);
|
|
51
|
+
try {
|
|
52
|
+
await mkdir(dirname(absolute), { recursive: true });
|
|
53
|
+
await writeFile(absolute, content, 'utf8');
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw new PlatformApiError(`${tool}: dosya yazılamadı — ${absolute} (${error instanceof Error ? error.message : String(error)}). Yol gateway sürecinin çalıştığı makineye göre çözülür.`);
|
|
57
|
+
}
|
|
58
|
+
return absolute;
|
|
59
|
+
}
|
|
60
|
+
export function assertInline(tool, config, payload) {
|
|
61
|
+
const size = Buffer.byteLength(JSON.stringify(payload), 'utf8');
|
|
62
|
+
if (size > Math.min(maxInlineBytes, config.maxResponseBytes)) {
|
|
63
|
+
throw new PlatformApiError(`${tool}: yanıt ${size} B, satır içi sınır ${Math.min(maxInlineBytes, config.maxResponseBytes)} B (MAX_RESPONSE_BYTES); içerik kırpılmasın diye outPath verin.`, 413);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function languageQuery(language) {
|
|
67
|
+
if (!language)
|
|
68
|
+
return { language: undefined, notes: [] };
|
|
69
|
+
const normalized = normalizeLanguageCode(language, 'language');
|
|
70
|
+
return { language: normalized.code, notes: normalized.note ? [normalized.note] : [] };
|
|
71
|
+
}
|
|
72
|
+
function warnings(config, findings) {
|
|
73
|
+
return config.policyMode === 'off' ? [] : applyPolicyMode(config, findings);
|
|
74
|
+
}
|
|
75
|
+
function assertScenarioSteps(tool, steps) {
|
|
76
|
+
for (const [index, step] of (steps ?? []).entries()) {
|
|
77
|
+
const actions = scenarioActions.filter(action => typeof step[action] === 'string' && step[action].trim() !== '');
|
|
78
|
+
if (actions.length !== 1) {
|
|
79
|
+
throw new PlatformApiError(`${tool}: steps[${index}] tam olarak bir eylem taşımalı (${scenarioActions.join(' | ')}); bulunan: ${actions.length === 0 ? 'yok' : actions.join(', ')} (istek gönderilmedi).`, 400);
|
|
80
|
+
}
|
|
81
|
+
if (typeof step.clock === 'string' && !isoDurationPattern.test(step.clock)) {
|
|
82
|
+
throw new PlatformApiError(`${tool}: steps[${index}].clock ISO 8601 süresi olmalı (ör. P3D, PT4H) (istek gönderilmedi).`, 400);
|
|
83
|
+
}
|
|
84
|
+
if (typeof step.task === 'string' && step.payload !== undefined) {
|
|
85
|
+
throw new PlatformApiError(`${tool}: steps[${index}].payload görev adımında okunmaz — görevin verisi (ör. sipariş no) 'form' alanıyla gider; payload yalnız signal/message adımında kullanılır (istek gönderilmedi).`, 400);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function registerBpmGovernanceTools(server, platform) {
|
|
90
|
+
const config = platform.config;
|
|
91
|
+
server.registerTool('process_definition_diff', {
|
|
92
|
+
title: 'Süreç sürümlerini karşılaştır',
|
|
93
|
+
description: "İki sürüm arasındaki anlamsal farkı okunur satırlarla döner (GET bpm/definitions/{id}/diff): kind (node-added · node-removed · assignee-changed · policy-changed · sla-changed · node-changed · editable-changed · edge-added · edge-removed · edge-condition-changed · param-added · param-removed · param-default-changed · param-type-changed · process-changed), path (/nodes[id=x]/alan), before/after, text ve isOverrideRisk (yöneticinin uyarlamasını bozabilecek değişiklik). to verilmezse taslak (yoksa son sürüm), from verilmezse aktif sürüm. Yayından önce okuyun; hasOverrideRisk true ise process_override_get ile uyarlamayı kontrol edin.",
|
|
94
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, from: versionParam, to: versionParam, language: languageParam },
|
|
95
|
+
annotations: readOnly
|
|
96
|
+
}, handle(config, async ({ project, environment, id, from, to, language }) => {
|
|
97
|
+
const requested = languageQuery(language);
|
|
98
|
+
return withNormalization(await platform.tenant('GET', project, `/bpm/definitions/${id}/diff`, { environment, query: { from, to, language: requested.language } }), requested.notes);
|
|
99
|
+
}));
|
|
100
|
+
server.registerTool('process_documentation_get', {
|
|
101
|
+
title: 'Süreç dokümantasyonunu (SOP) getir',
|
|
102
|
+
description: "Tanımın SOP dokümanını HTML olarak üretir (GET bpm/definitions/{id}/documentation): başlık ve sürüm damgası, sahip ve RACI, kulvarlar (atama kuralına göre), adım tablosu (SLA, politika), parametreler, gömülü SVG diyagram. version verilmezse aktif (yoksa taslak). PDF henüz yok (b-82). 256 KB üstü HTML için outPath verin.",
|
|
103
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, version: versionParam, language: languageParam, outPath: outPathParam },
|
|
104
|
+
annotations: readOnly
|
|
105
|
+
}, handle(config, async ({ project, environment, id, version, language, outPath }) => {
|
|
106
|
+
const requested = languageQuery(language);
|
|
107
|
+
const payload = await platform.tenant('GET', project, `/bpm/definitions/${id}/documentation`, { environment, query: { version, language: requested.language, format: 'html' }, expectBinary: true });
|
|
108
|
+
const html = payload.bytes.toString('utf8').replace(/^\uFEFF/, '');
|
|
109
|
+
if (outPath)
|
|
110
|
+
return withNormalization({ format: 'html', sizeBytes: payload.bytes.byteLength, path: await writeLocalFile('process_documentation_get', outPath, html) }, requested.notes);
|
|
111
|
+
const inline = { format: 'html', sizeBytes: payload.bytes.byteLength, html };
|
|
112
|
+
assertInline('process_documentation_get', config, inline);
|
|
113
|
+
return withNormalization(inline, requested.notes);
|
|
114
|
+
}));
|
|
115
|
+
server.registerTool('process_bpmn_get', {
|
|
116
|
+
title: 'Süreci BPMN 2.0 XML olarak dışa aktar',
|
|
117
|
+
description: "Tanımın bir sürümünü BPMN 2.0 XML'e çevirir (GET bpm/definitions/{id}/bpmn): düğüm ve süreç alanları zeyrek: uzantısında, diyagram (DI) saklıysa korunur, yoksa ızgara yerleşimle üretilir; tanınmayan içe aktarılmış öğeler (şerit, not, veri nesnesi) yerine yazılır. version verilmezse taslak (yoksa aktif). Yanıt { versionNumber, versionState, sizeBytes, xml }; 256 KB üstü için outPath verin. Başka modelleyicide açılabilir; geri almak için process_bpmn_update.",
|
|
118
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, version: versionParam, outPath: outPathParam },
|
|
119
|
+
annotations: readOnly
|
|
120
|
+
}, handle(config, async ({ project, environment, id, version, outPath }) => {
|
|
121
|
+
const response = await platform.tenantWithHeaders('GET', project, `/bpm/definitions/${id}/bpmn`, { environment, query: { version }, expectBinary: true });
|
|
122
|
+
const payload = response.body;
|
|
123
|
+
const xml = payload.bytes.toString('utf8').replace(/^\uFEFF/, '');
|
|
124
|
+
const summary = {
|
|
125
|
+
definitionId: id,
|
|
126
|
+
versionNumber: Number(response.headers['x-definition-version']) || null,
|
|
127
|
+
versionState: response.headers['x-definition-version-state'] ?? null,
|
|
128
|
+
sizeBytes: payload.bytes.byteLength
|
|
129
|
+
};
|
|
130
|
+
if (outPath)
|
|
131
|
+
return { ...summary, path: await writeLocalFile('process_bpmn_get', outPath, xml) };
|
|
132
|
+
const inline = { ...summary, xml };
|
|
133
|
+
assertInline('process_bpmn_get', config, inline);
|
|
134
|
+
return inline;
|
|
135
|
+
}));
|
|
136
|
+
server.registerTool('process_bpmn_update', {
|
|
137
|
+
title: 'Taslağı BPMN 2.0 XML ile değiştir',
|
|
138
|
+
description: "BPMN 2.0 XML'i içe aktarıp tanımın TASLAĞINA yazar (PUT bpm/definitions/{id}/bpmn; yeni tanım için process_definition_create { bpmn }). xml (≤ 256 KB) ya da filePath (≤ 5 MB) — biri. zeyrek: uzantılı XML (process_bpmn_get çıktısı) kayıpsız döner; uzantısız XML'de düğümler öğe türünden türetilir, derece locked, insan adımının ataması yer tutucu 'initiator', SLA boş (BPM-016 uyarısı); workflow'suz serviceTask 422. Diyagram ve tanınmayan öğeler saklanır. DTD/dış varlık içeren XML 400; doğrulama hataları 422 DefinitionInvalid. Yanıt outline.",
|
|
139
|
+
inputSchema: {
|
|
140
|
+
project: projectParam,
|
|
141
|
+
environment: environmentParam,
|
|
142
|
+
id: guid,
|
|
143
|
+
xml: z.string().min(1).max(maxInlineBytes).optional(),
|
|
144
|
+
filePath: z.string().min(1).optional().describe('yerel .bpmn dosyası')
|
|
145
|
+
},
|
|
146
|
+
annotations: updates
|
|
147
|
+
}, handle(config, async ({ project, environment, id, xml, filePath }) => {
|
|
148
|
+
if (Boolean(xml) === Boolean(filePath))
|
|
149
|
+
throw new PlatformApiError("process_bpmn_update: 'xml' ya da 'filePath' — yalnız biri (istek gönderilmedi).", 400);
|
|
150
|
+
let content = xml ?? '';
|
|
151
|
+
if (filePath) {
|
|
152
|
+
try {
|
|
153
|
+
content = await readFile(resolve(filePath), 'utf8');
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
throw new PlatformApiError(`process_bpmn_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);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
content = content.replace(/^\uFEFF/, '');
|
|
160
|
+
if (Buffer.byteLength(content, 'utf8') > maxBpmnBytes)
|
|
161
|
+
throw new PlatformApiError(`process_bpmn_update: XML en çok ${maxBpmnBytes} B (istek gönderilmedi).`, 413);
|
|
162
|
+
const findings = warnings(config, bpmnExtensionFindings(content));
|
|
163
|
+
const saved = dataOf(await platform.tenant('PUT', project, `/bpm/definitions/${id}/bpmn`, { environment, body: { xml: content } }));
|
|
164
|
+
return attachFindings(processOutline(saved), findings);
|
|
165
|
+
}));
|
|
166
|
+
server.registerTool('process_comment_list', {
|
|
167
|
+
title: 'Model yorumlarını listele',
|
|
168
|
+
description: 'Tanıma bırakılmış tasarım yorumlarını listeler (GET bpm/definitions/{id}/comments): nodeId (boşsa genel), versionNumber (yazıldığı taslak), body, authorUserId, postedAt, resolvedBy/At. Çözülmüşler için includeResolved: true. Süreç örneğinin yorumları değildir (onlar process_instance_get).',
|
|
169
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, includeResolved: z.boolean().optional() },
|
|
170
|
+
annotations: readOnly
|
|
171
|
+
}, handle(config, ({ project, environment, id, includeResolved }) => platform.tenant('GET', project, `/bpm/definitions/${id}/comments`, { environment, query: { includeResolved } })));
|
|
172
|
+
server.registerTool('process_comment_create', {
|
|
173
|
+
title: 'Model yorumu yaz',
|
|
174
|
+
description: 'Tanıma ya da bir düğümüne tasarım yorumu bırakır (POST bpm/definitions/{id}/comments); yorum güncel taslak (yoksa son) sürüme sabitlenir. nodeId tanımda olmalı (400). Yetki process.manage.',
|
|
175
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, nodeId: z.string().max(100).optional(), body: z.string().min(1).max(4000) },
|
|
176
|
+
annotations: writes
|
|
177
|
+
}, handle(config, ({ project, environment, id, nodeId, body }) => platform.tenant('POST', project, `/bpm/definitions/${id}/comments`, { environment, body: { nodeId, body } })));
|
|
178
|
+
server.registerTool('process_comment_update', {
|
|
179
|
+
title: 'Model yorumunu çöz ya da yeniden aç',
|
|
180
|
+
description: 'Model yorumunu çözülmüş işaretler (resolved: true) ya da yeniden açar (resolved: false) (POST bpm/definitions/{id}/comments/{commentId}/resolve). Silme bu araçta yok: yorumu yalnız yazan Studio\'dan siler.',
|
|
181
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, commentId: guid, resolved: z.boolean() },
|
|
182
|
+
annotations: updates
|
|
183
|
+
}, handle(config, ({ project, environment, id, commentId, resolved }) => platform.tenant('POST', project, `/bpm/definitions/${id}/comments/${commentId}/resolve`, { environment, body: { resolved } })));
|
|
184
|
+
server.registerTool('process_scenario_list', {
|
|
185
|
+
title: 'Süreç test senaryolarını listele',
|
|
186
|
+
description: "Tanımın test senaryolarını ve son koşularını listeler (GET bpm/test-scenarios?definitionId): given, stubs, steps, expect, lastRunVersionNumber, lastRunChecksum, lastRunPassed, lastRunDivergence. view:'coverage' kayıtlı son koşulardan yol kapsamasını döner (GET bpm/definitions/{id}/coverage): nodePercent, edgePercent, untestedNodeIds, untestedEdgeKeys ('from->to' — hiç test edilmeyen dal); yalnız son koşusu o sürümün sağlamasıyla yapılmış senaryolar sayılır. version verilmezse taslak, yoksa aktif.",
|
|
187
|
+
inputSchema: { project: projectParam, environment: environmentParam, definitionId: guid, view: z.enum(['list', 'coverage']).optional().describe('varsayılan list'), version: versionParam.describe("yalnız view:'coverage'") },
|
|
188
|
+
annotations: readOnly
|
|
189
|
+
}, handle(config, ({ project, environment, definitionId, view, version }) => view === 'coverage'
|
|
190
|
+
? platform.tenant('GET', project, `/bpm/definitions/${definitionId}/coverage`, { environment, query: { version } })
|
|
191
|
+
: platform.tenant('GET', project, '/bpm/test-scenarios', { environment, query: { definitionId } })));
|
|
192
|
+
server.registerTool('process_scenario_create', {
|
|
193
|
+
title: 'Süreç test senaryosu oluştur',
|
|
194
|
+
description: "Test senaryosu yazar. fromInstanceId: gerçek örneğin defterinden kaydeder (POST bpm/instances/{id}/record-scenario) — insan kararları, zaman atlamaları, sinyal/mesaj/alt süreç sonuçları adım; servis çıktıları stub; girilen düğümler, atananlar, sonuç ve durum beklenen olur. definitionId: senaryoyu elle yazar (POST bpm/test-scenarios). Oynatma gerçek motorla yan etkisiz koşar: writeBack satıra yazılmaz, bildirim/webhook gitmez, service düğümü workflow çağırmaz — stubs anahtarı workflowSlug → workflowId → düğüm id sırasıyla aranır, stub yoksa örnek faulted. Koşu process_scenario_execute. Kurallar platform_guide('bpm') §Simülasyon ve test.",
|
|
195
|
+
inputSchema: {
|
|
196
|
+
project: projectParam,
|
|
197
|
+
environment: environmentParam,
|
|
198
|
+
definitionId: guid.optional(),
|
|
199
|
+
fromInstanceId: guid.optional(),
|
|
200
|
+
name: z.string().min(1).max(200),
|
|
201
|
+
description: z.string().max(2000).optional(),
|
|
202
|
+
given: scenarioGivenParam,
|
|
203
|
+
stubs: z.record(z.unknown()).optional().describe("servis düğümü çıktıları: { '<workflowSlug>': { … } }"),
|
|
204
|
+
steps: scenarioStepsParam,
|
|
205
|
+
expect: scenarioExpectParam
|
|
206
|
+
},
|
|
207
|
+
annotations: writes
|
|
208
|
+
}, handle(config, async ({ project, environment, definitionId, fromInstanceId, name, description, given, stubs, steps, expect }) => {
|
|
209
|
+
if (Boolean(definitionId) === Boolean(fromInstanceId))
|
|
210
|
+
throw new PlatformApiError("process_scenario_create: 'definitionId' (elle) ya da 'fromInstanceId' (kayıt) — yalnız biri (istek gönderilmedi).", 400);
|
|
211
|
+
if (fromInstanceId) {
|
|
212
|
+
if (description !== undefined || given || stubs || steps || expect)
|
|
213
|
+
throw new PlatformApiError('process_scenario_create: kayıt (fromInstanceId) yalnız name alır; senaryoyu kayıttan sonra process_scenario_update ile düzenleyin (istek gönderilmedi).', 400);
|
|
214
|
+
return platform.tenant('POST', project, `/bpm/instances/${fromInstanceId}/record-scenario`, { environment, body: { name } });
|
|
215
|
+
}
|
|
216
|
+
assertScenarioSteps('process_scenario_create', steps);
|
|
217
|
+
return platform.tenant('POST', project, '/bpm/test-scenarios', { environment, body: { definitionId, name, description, given, stubs, steps, expect } });
|
|
218
|
+
}));
|
|
219
|
+
server.registerTool('process_scenario_update', {
|
|
220
|
+
title: 'Süreç test senaryosunu güncelle',
|
|
221
|
+
description: 'Senaryoyu günceller (PUT bpm/test-scenarios/{id}); verilmeyen alanlar güncel senaryodan alınır (gateway önce okur). Düzenleme son koşu sonucunu siler — yeşil sayılması için yeniden koşun.',
|
|
222
|
+
inputSchema: {
|
|
223
|
+
project: projectParam,
|
|
224
|
+
environment: environmentParam,
|
|
225
|
+
id: guid,
|
|
226
|
+
name: z.string().min(1).max(200).optional(),
|
|
227
|
+
description: z.string().max(2000).nullable().optional(),
|
|
228
|
+
given: scenarioGivenParam,
|
|
229
|
+
stubs: z.record(z.unknown()).optional(),
|
|
230
|
+
steps: scenarioStepsParam,
|
|
231
|
+
expect: scenarioExpectParam
|
|
232
|
+
},
|
|
233
|
+
annotations: updates
|
|
234
|
+
}, handle(config, async ({ project, environment, id, name, description, given, stubs, steps, expect }) => {
|
|
235
|
+
assertScenarioSteps('process_scenario_update', steps);
|
|
236
|
+
const current = dataOf(await platform.tenant('GET', project, `/bpm/test-scenarios/${id}`, { environment }));
|
|
237
|
+
return platform.tenant('PUT', project, `/bpm/test-scenarios/${id}`, {
|
|
238
|
+
environment,
|
|
239
|
+
body: {
|
|
240
|
+
name: name ?? current.name,
|
|
241
|
+
description: description !== undefined ? description : current.description,
|
|
242
|
+
given: given ?? current.given,
|
|
243
|
+
stubs: stubs ?? current.stubs,
|
|
244
|
+
steps: steps ?? current.steps,
|
|
245
|
+
expect: expect ?? current.expect
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}));
|
|
249
|
+
server.registerTool('process_scenario_delete', {
|
|
250
|
+
title: 'Süreç test senaryosunu sil',
|
|
251
|
+
description: 'Senaryoyu siler (DELETE bpm/test-scenarios/{id}). Tanım requireGreenScenarios: true ise yayın kapısı kalan senaryolara bakar.',
|
|
252
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid },
|
|
253
|
+
annotations: destructive
|
|
254
|
+
}, handle(config, ({ project, environment, id }) => platform.tenant('DELETE', project, `/bpm/test-scenarios/${id}`, { environment })));
|
|
255
|
+
server.registerTool('process_scenario_execute', {
|
|
256
|
+
title: 'Süreç test senaryolarını koş',
|
|
257
|
+
description: "Senaryoları gerçek motorla YAN ETKİSİZ koşar (her senaryo ayrı savepoint; biri istisna atarsa o senaryo error sapması olur, diğerleri koşar) ve son koşu sonucunu kaydeder. Yetki process.manage: definitionId tanımın bütün senaryoları (POST bpm/definitions/{id}/run-scenarios), id tek senaryo (POST bpm/test-scenarios/{id}/run). version verilmezse taslak, yoksa aktif — eski senaryoları yeni taslakta koşmak sürüm regresyonudur. Yanıt { versionNumber, passed, passedCount, failedCount, results[] { name, passed, divergence { kind: step | error | path | assignee | outcome | state | vars | write-back, nodeId, stepIndex, expected, actual, message }, path, state, outcome }, coverage }. Tanımda requireGreenScenarios: true iken taslak sağlamasında yeşil olmayan senaryo yayını 422 ScenarioFailed ile durdurur. production hedefi istek gitmeden durur (BPM-001).",
|
|
258
|
+
inputSchema: { project: projectParam, environment: environmentParam, definitionId: guid.optional(), id: guid.optional().describe('tek senaryo'), version: versionParam },
|
|
259
|
+
annotations: writes
|
|
260
|
+
}, handle(config, async ({ project, environment, definitionId, id, version }) => {
|
|
261
|
+
if (Boolean(definitionId) === Boolean(id))
|
|
262
|
+
throw new PlatformApiError("process_scenario_execute: 'definitionId' (bütün senaryolar) ya da 'id' (tek senaryo) — yalnız biri (istek gönderilmedi).", 400);
|
|
263
|
+
assertProcessNotProduction('process_scenario_execute', environment, config);
|
|
264
|
+
return dataOf(await platform.tenant('POST', project, id ? `/bpm/test-scenarios/${id}/run` : `/bpm/definitions/${definitionId}/run-scenarios`, { environment, query: { version } }));
|
|
265
|
+
}));
|
|
266
|
+
}
|