@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,230 @@
|
|
|
1
|
+
import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive } from './common.js';
|
|
2
|
+
import { identityHeaders } from '../platform/tokenVault.js';
|
|
3
|
+
import { PolicyBlockError } from '../knowledge/policy.js';
|
|
4
|
+
import { PlatformApiError } from '../platform/errors.js';
|
|
5
|
+
import { assertProcessNotProduction } from './bpm.js';
|
|
6
|
+
import { handle } from '../platform/responses.js';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
const workingDays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
|
|
9
|
+
const timeOfDay = z.string().regex(/^\d{2}:\d{2}(:\d{2})?$/).describe("'08:00' ya da '08:00:00'");
|
|
10
|
+
const asUserParam = z.string().min(1).optional()
|
|
11
|
+
.describe("Uygulama kullanıcısının kimliği (ham JWT ya da tok_… referansı). Vekaleti kendisi için açan kişi bu kullanıcıdır; verilmezse gateway'in Studio kullanıcısı adına gider.");
|
|
12
|
+
const holidayDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
13
|
+
const holiday = z.union([
|
|
14
|
+
holidayDate,
|
|
15
|
+
z.object({
|
|
16
|
+
date: holidayDate,
|
|
17
|
+
name: z.string().max(100).optional(),
|
|
18
|
+
workEnd: timeOfDay.optional().describe("yarım gün: iş bu saatte biter ('13:00'); verilmezse gün tam tatildir")
|
|
19
|
+
})
|
|
20
|
+
]);
|
|
21
|
+
const calendarBody = {
|
|
22
|
+
code: z.string().min(1).max(100).describe("kebab-case; 'default' kurum takvimidir"),
|
|
23
|
+
organizationUnitId: guid.optional().describe('Birim takvimi; aynı birimdeki SLA hesapları bu takvimi kullanır'),
|
|
24
|
+
workingDays: z.array(z.enum(workingDays)).min(1),
|
|
25
|
+
workStart: timeOfDay,
|
|
26
|
+
workEnd: timeOfDay,
|
|
27
|
+
holidays: z.array(holiday).max(1000).optional().describe("'2026-10-29' (tam gün) ya da { date: '2026-10-28', name: 'Cumhuriyet Bayramı arifesi', workEnd: '13:00' } (yarım gün); bir tarih yalnız bir kez"),
|
|
28
|
+
timeZone: z.string().min(1).max(100).describe("IANA adı, ör. 'Europe/Istanbul'")
|
|
29
|
+
};
|
|
30
|
+
/** Dates become the holiday objects the backend stores, a half day's end gets its seconds; a date given twice stops the call */
|
|
31
|
+
function holidayBodies(tool, holidays) {
|
|
32
|
+
if (!holidays)
|
|
33
|
+
return undefined;
|
|
34
|
+
const bodies = holidays.map(item => typeof item === 'string'
|
|
35
|
+
? { date: item }
|
|
36
|
+
: { date: item.date, name: item.name, workEnd: item.workEnd ? toSeconds(item.workEnd) : undefined });
|
|
37
|
+
const dates = bodies.map(body => body.date);
|
|
38
|
+
const repeated = dates.find((date, index) => dates.indexOf(date) !== index);
|
|
39
|
+
if (repeated)
|
|
40
|
+
throw new PlatformApiError(`${tool}: ${repeated} tatillerde iki kez var — bir tarih takvimde yalnız bir kez yer alır; birini çıkarın (istek gönderilmedi).`, 400);
|
|
41
|
+
return bodies;
|
|
42
|
+
}
|
|
43
|
+
/** Backend Delegation.Overlaps: same principal, intersecting ranges (open end = forever) and processes (empty = all) */
|
|
44
|
+
function overlapsDelegation(row, fromUserId, startsAt, endsAt, processSlugs) {
|
|
45
|
+
if (row.endedAt || row.fromUserId !== fromUserId)
|
|
46
|
+
return false;
|
|
47
|
+
const rowStart = row.startsAt ? Date.parse(row.startsAt) : Number.NEGATIVE_INFINITY;
|
|
48
|
+
const rowEnd = row.endsAt ? Date.parse(row.endsAt) : Number.POSITIVE_INFINITY;
|
|
49
|
+
const start = Date.parse(startsAt);
|
|
50
|
+
const end = endsAt ? Date.parse(endsAt) : Number.POSITIVE_INFINITY;
|
|
51
|
+
const rowSlugs = row.processSlugs ?? [];
|
|
52
|
+
return rowStart < end && start < rowEnd && (rowSlugs.length === 0 || processSlugs.length === 0 || rowSlugs.some(slug => processSlugs.includes(slug)));
|
|
53
|
+
}
|
|
54
|
+
function toSeconds(value) {
|
|
55
|
+
return /^\d{2}:\d{2}$/.test(value) ? `${value}:00` : value;
|
|
56
|
+
}
|
|
57
|
+
export function registerBpmOperationsTools(server, platform) {
|
|
58
|
+
const config = platform.config;
|
|
59
|
+
server.registerTool('process_calendar_list', {
|
|
60
|
+
title: 'İş takvimlerini listele',
|
|
61
|
+
description: 'SLA ve süre hesaplarında kullanılan iş takvimlerini listeler (çalışma günleri, saatleri, tatiller { date, name, workEnd }, saat dilimi). workEnd olan tatil yarım gündür: o gün iş workEnd\'de biter ve süre o saate kadar sayılır. Tanımsız projede yerleşik Pzt–Cum 08:00–18:00 kullanılır.',
|
|
62
|
+
inputSchema: { project: projectParam, environment: environmentParam },
|
|
63
|
+
annotations: readOnly
|
|
64
|
+
}, handle(config, ({ project, environment }) => platform.tenant('GET', project, '/bpm/calendars', { environment })));
|
|
65
|
+
server.registerTool('process_calendar_create', {
|
|
66
|
+
title: 'İş takvimi oluştur',
|
|
67
|
+
description: "İş takvimi oluşturur. SLA sayacı 'calendar' kodu, sonra örneğin birimi, sonra 'default' kodlu takvimle hesaplanır. Aynı kod 409 döner. Yetki process.manage.",
|
|
68
|
+
inputSchema: { project: projectParam, environment: environmentParam, ...calendarBody },
|
|
69
|
+
annotations: writes
|
|
70
|
+
}, handle(config, async ({ project, environment, workStart, workEnd, holidays, ...body }) => {
|
|
71
|
+
assertProcessNotProduction('process_calendar_create', environment, config);
|
|
72
|
+
return platform.tenant('POST', project, '/bpm/calendars', { environment, body: { ...body, workStart: toSeconds(workStart), workEnd: toSeconds(workEnd), holidays: holidayBodies('process_calendar_create', holidays) } });
|
|
73
|
+
}));
|
|
74
|
+
server.registerTool('process_calendar_update', {
|
|
75
|
+
title: 'İş takvimini güncelle',
|
|
76
|
+
description: "İş takvimini tümüyle değiştirir (tam gövde; kayıtta code, workingDays, workStart, workEnd ve timeZone zorunlu). Yeni takvim yalnız bundan sonra kurulan sayaçlara uygulanır; kurulmuş timer vadeleri değişmez. remove:true yalnız id ile takvimi siler: bir süreç düğümü ya da SLA sayacı takvime işaret ediyorsa 422 ReferencedByProcess döner. 'default' kodlu ya da birime bağlı takvime tanım referans vermez; silinince SLA zinciri (kod → birim → default → yerleşik Pzt–Cum 08–18) sonraki halkaya düşer.",
|
|
77
|
+
inputSchema: {
|
|
78
|
+
project: projectParam,
|
|
79
|
+
environment: environmentParam,
|
|
80
|
+
id: guid,
|
|
81
|
+
remove: z.boolean().optional(),
|
|
82
|
+
...calendarBody,
|
|
83
|
+
code: calendarBody.code.optional(),
|
|
84
|
+
workingDays: calendarBody.workingDays.optional(),
|
|
85
|
+
workStart: timeOfDay.optional(),
|
|
86
|
+
workEnd: timeOfDay.optional(),
|
|
87
|
+
timeZone: calendarBody.timeZone.optional()
|
|
88
|
+
},
|
|
89
|
+
annotations: destructive
|
|
90
|
+
}, handle(config, async ({ project, environment, id, remove, workStart, workEnd, holidays, ...body }) => {
|
|
91
|
+
assertProcessNotProduction('process_calendar_update', environment, config);
|
|
92
|
+
if (remove)
|
|
93
|
+
return platform.tenant('DELETE', project, `/bpm/calendars/${id}`, { environment });
|
|
94
|
+
const missing = [['code', body.code], ['workingDays', body.workingDays], ['workStart', workStart], ['workEnd', workEnd], ['timeZone', body.timeZone]]
|
|
95
|
+
.filter(([, value]) => value === undefined || value === '')
|
|
96
|
+
.map(([field]) => field);
|
|
97
|
+
if (missing.length)
|
|
98
|
+
throw new PlatformApiError(`process_calendar_update: tam gövde için ${missing.join(', ')} zorunlu — önce process_calendar_list ile takvimi okuyun (istek gönderilmedi).`, 400);
|
|
99
|
+
return platform.tenant('PUT', project, `/bpm/calendars/${id}`, { environment, body: { ...body, workStart: toSeconds(workStart), workEnd: toSeconds(workEnd), holidays: holidayBodies('process_calendar_update', holidays) } });
|
|
100
|
+
}));
|
|
101
|
+
server.registerTool('process_delegation_list', {
|
|
102
|
+
title: 'Vekaletleri listele',
|
|
103
|
+
description: 'Kullanıcının verdiği ve aldığı vekaletleri listeler. userId verilmezse asUser (ya da gateway kullanıcısı); başkasınınkini görmek birim yöneticiliği ya da process.manage ister.',
|
|
104
|
+
inputSchema: { project: projectParam, environment: environmentParam, userId: z.string().max(64).optional(), asUser: asUserParam },
|
|
105
|
+
annotations: readOnly
|
|
106
|
+
}, handle(config, ({ project, environment, userId, asUser }) => platform.tenant('GET', project, '/bpm/delegations', { environment, query: userId ? { userId } : undefined, headers: identityHeaders(asUser, undefined, undefined) })));
|
|
107
|
+
server.registerTool('process_delegation_create', {
|
|
108
|
+
title: 'Vekalet aç',
|
|
109
|
+
description: "Tarih aralıklı görev vekaleti açar: aralıkta asıl kişiye düşecek yeni görevler vekile gider (görevde principalUserId asıl kalır), aralık öncesinden açık görevleri vekil 'delegated' sekmesinde görür ve tamamlayabilir. onBehalfOf verilirse başkası adına açılır (birim/üst birim yöneticisi ya da process.manage; kaynak 'manager'). processSlugs boşsa tüm süreçler. Vekil process.manage devralmaz, zincir yok: vekilin kendisi kesişen tarih aralığında ve kesişen süreçlerde (boş liste = tümü) kendi görevlerini devretmişse BPM-008 ile istek gitmez; çağıranın vekilin vekaletlerini okuma yetkisi yoksa ön denetim atlanır ve sunucu 422 DelegationChainNotAllowed döner. Çakışan vekalet 409.",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
project: projectParam,
|
|
112
|
+
environment: environmentParam,
|
|
113
|
+
toUserId: z.string().min(1).max(64),
|
|
114
|
+
onBehalfOf: z.string().max(64).optional().describe('asıl kişi (fromUserId); verilmezse çağıran kişi'),
|
|
115
|
+
startsAt: z.string().datetime({ offset: true }),
|
|
116
|
+
endsAt: z.string().datetime({ offset: true }).optional(),
|
|
117
|
+
processSlugs: z.array(z.string().min(1).max(100)).optional(),
|
|
118
|
+
reason: z.string().max(1000).optional(),
|
|
119
|
+
asUser: asUserParam
|
|
120
|
+
},
|
|
121
|
+
annotations: writes
|
|
122
|
+
}, handle(config, async ({ project, environment, toUserId, onBehalfOf, startsAt, endsAt, processSlugs, reason, asUser }) => {
|
|
123
|
+
assertProcessNotProduction('process_delegation_create', environment, config);
|
|
124
|
+
const headers = identityHeaders(asUser, undefined, undefined);
|
|
125
|
+
let existing;
|
|
126
|
+
try {
|
|
127
|
+
existing = await platform.tenant('GET', project, '/bpm/delegations', { environment, query: { userId: toUserId }, headers });
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
if (!(error instanceof PlatformApiError && error.status === 403))
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
if ((existing?.data ?? []).some(delegation => overlapsDelegation(delegation, toUserId, startsAt, endsAt, processSlugs ?? []))) {
|
|
134
|
+
throw new PolicyBlockError([{
|
|
135
|
+
ruleId: 'BPM-008',
|
|
136
|
+
severity: 'block',
|
|
137
|
+
message: `process_delegation_create: vekil '${toUserId}' aynı tarih aralığında ve süreçlerde kendi görevlerini başkasına devretmiş. Vekalet zincirlenemez — başka bir vekil seçin, aralığı ya da süreçleri değiştirin veya o vekaleti process_delegation_delete ile bitirin.`,
|
|
138
|
+
guide: 'ngapps://guides/bpm'
|
|
139
|
+
}]);
|
|
140
|
+
}
|
|
141
|
+
return platform.tenant('POST', project, '/bpm/delegations', {
|
|
142
|
+
environment,
|
|
143
|
+
body: { fromUserId: onBehalfOf, toUserId, startsAt, endsAt, processSlugs, reason },
|
|
144
|
+
headers
|
|
145
|
+
});
|
|
146
|
+
}));
|
|
147
|
+
server.registerTool('process_delegation_delete', {
|
|
148
|
+
title: 'Vekaleti bitir',
|
|
149
|
+
description: 'Vekaleti şimdi bitirir (kayıt silinmez, endedAt yazılır; geçmiş görevlerde principalUserId kalır). Bitişten sonra yeni görevler asıla gider; vekilin başlattığı görevler vekilde biter.',
|
|
150
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, asUser: asUserParam },
|
|
151
|
+
annotations: destructive
|
|
152
|
+
}, handle(config, async ({ project, environment, id, asUser }) => {
|
|
153
|
+
assertProcessNotProduction('process_delegation_delete', environment, config);
|
|
154
|
+
return platform.tenant('POST', project, `/bpm/delegations/${id}/end`, { environment, headers: identityHeaders(asUser, undefined, undefined) });
|
|
155
|
+
}));
|
|
156
|
+
server.registerTool('process_handover_preview', {
|
|
157
|
+
title: 'Devir önizlemesi',
|
|
158
|
+
description: "YAN ETKİSİZ okuma — ayrılan/uzun süre yok olacak kullanıcının üzerindeki iş: assigned-tasks ve candidate-tasks (süreç başına sayı + önerilen hedef), started-instances, managed-units, held-positions, delegations. process_handover_create kararları bu kalemlerin kind/key değerleriyle verilir. Yetki process.manage.",
|
|
159
|
+
inputSchema: { project: projectParam, environment: environmentParam, userId: z.string().min(1).max(64) },
|
|
160
|
+
annotations: readOnly
|
|
161
|
+
}, handle(config, ({ project, environment, userId }) => platform.tenant('GET', project, `/bpm/handovers/preview/${encodeURIComponent(userId)}`, { environment })));
|
|
162
|
+
server.registerTool('process_handover_create', {
|
|
163
|
+
title: 'Devri uygula',
|
|
164
|
+
description: "Devir kararlarını uygular. decisions[]: { kind: assigned-tasks | candidate-tasks, key: süreç slug'ı, target: manager | user | group | admin-queue, targetUserId (user için) }. force:false iken kararı olmayan grup 422 HandoverIncomplete; force:true iken kararı olmayan assigned-tasks grubu yöneticiye (yoksa admin-queue), candidate-tasks grubu gruba gider (kişi adaylıktan çıkar; aday kalmazsa admin-queue). Açık vekaletler biter; pozisyon devri önizlemedeki held-positions kalemiyle organizasyon ucundan yapılır. Açık görevi olan kullanıcıyı pasifleştirmeden önce bu devir tamamlanmalıdır (OpenTasksExist).",
|
|
165
|
+
inputSchema: {
|
|
166
|
+
project: projectParam,
|
|
167
|
+
environment: environmentParam,
|
|
168
|
+
userId: z.string().min(1).max(64),
|
|
169
|
+
decisions: z.array(z.object({
|
|
170
|
+
kind: z.enum(['assigned-tasks', 'candidate-tasks']),
|
|
171
|
+
key: z.string().min(1).max(100),
|
|
172
|
+
target: z.enum(['manager', 'user', 'group', 'admin-queue']),
|
|
173
|
+
targetUserId: z.string().max(64).optional()
|
|
174
|
+
})).optional(),
|
|
175
|
+
force: z.boolean().optional()
|
|
176
|
+
},
|
|
177
|
+
annotations: writes
|
|
178
|
+
}, handle(config, async ({ project, environment, userId, decisions, force }) => {
|
|
179
|
+
assertProcessNotProduction('process_handover_create', environment, config);
|
|
180
|
+
return platform.tenant('POST', project, '/bpm/handovers', { environment, body: { userId, decisions: decisions ?? [], force: force ?? false } });
|
|
181
|
+
}));
|
|
182
|
+
server.registerTool('process_batch_execute', {
|
|
183
|
+
title: 'Toplu işlem',
|
|
184
|
+
description: "Toplu işlem başlatır ve batchOperationId döner; ilerleme process_batch_get ile izlenir. operation start → slug + items[{subjectRowId, vars}] (en çok 100.000; 500'lük parçalarla arka planda) | reassign → taskIds + targetUserId | extend-due → taskIds + dueAt (bekleyen SLA/hatırlatma timer'ları aynı farkla kayar) | cancel → taskIds + reason (görevlerin örnekleri iptal edilir). Görev işlemleri en çok 500 görev, eşzamanlı. Yetki process.start (start) / process.manage (diğerleri).",
|
|
185
|
+
inputSchema: {
|
|
186
|
+
project: projectParam,
|
|
187
|
+
environment: environmentParam,
|
|
188
|
+
operation: z.enum(['start', 'reassign', 'extend-due', 'cancel']),
|
|
189
|
+
slug: z.string().max(100).optional(),
|
|
190
|
+
applicationId: guid.optional(),
|
|
191
|
+
items: z.array(z.object({ subjectRowId: z.string().max(64).optional(), vars: z.record(z.unknown()).optional() })).optional(),
|
|
192
|
+
taskIds: z.array(guid).max(500).optional(),
|
|
193
|
+
targetUserId: z.string().max(64).optional(),
|
|
194
|
+
dueAt: z.string().datetime({ offset: true }).optional(),
|
|
195
|
+
reason: z.string().max(1000).optional()
|
|
196
|
+
},
|
|
197
|
+
annotations: writes
|
|
198
|
+
}, handle(config, async ({ project, environment, operation, slug, applicationId, items, taskIds, targetUserId, dueAt, reason }) => {
|
|
199
|
+
assertProcessNotProduction('process_batch_execute', environment, config);
|
|
200
|
+
if (operation === 'start') {
|
|
201
|
+
if (!slug || !items?.length)
|
|
202
|
+
throw new PlatformApiError("process_batch_execute: 'start' için slug ve items zorunlu (istek gönderilmedi).", 400);
|
|
203
|
+
return platform.tenant('POST', project, `/bpm/processes/${encodeURIComponent(slug)}/start-batch`, { environment, body: { applicationId, items } });
|
|
204
|
+
}
|
|
205
|
+
if (!taskIds?.length)
|
|
206
|
+
throw new PlatformApiError(`process_batch_execute: '${operation}' için taskIds zorunlu (istek gönderilmedi).`, 400);
|
|
207
|
+
if (operation === 'reassign' && !targetUserId)
|
|
208
|
+
throw new PlatformApiError("process_batch_execute: 'reassign' targetUserId ister (istek gönderilmedi).", 400);
|
|
209
|
+
if (operation === 'extend-due' && !dueAt)
|
|
210
|
+
throw new PlatformApiError("process_batch_execute: 'extend-due' dueAt ister (istek gönderilmedi).", 400);
|
|
211
|
+
if (operation === 'cancel' && !reason?.trim())
|
|
212
|
+
throw new PlatformApiError("process_batch_execute: 'cancel' reason ister (istek gönderilmedi).", 400);
|
|
213
|
+
return platform.tenant('POST', project, '/bpm/tasks/batch', { environment, body: { kind: operation, taskIds, targetUserId, dueAt, reason } });
|
|
214
|
+
}));
|
|
215
|
+
server.registerTool('process_batch_get', {
|
|
216
|
+
title: 'Toplu işlemi getir',
|
|
217
|
+
description: 'Toplu işlemin durumu: state (running | completed), total, done, failed, errors[] (ilk 100 satır hatası, index ile), undoneAt.',
|
|
218
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid },
|
|
219
|
+
annotations: readOnly
|
|
220
|
+
}, handle(config, ({ project, environment, id }) => platform.tenant('GET', project, `/bpm/batch-operations/${id}`, { environment })));
|
|
221
|
+
server.registerTool('process_batch_update', {
|
|
222
|
+
title: 'Toplu işlemi geri al',
|
|
223
|
+
description: "operation undo: reassign işleminde eski atananlar, extend-due işleminde eski terminler (ve timer vadeleri) geri gelir. start ve cancel geri alınamaz → 422 BatchOperationNotUndoable; ikinci geri alma da 422.",
|
|
224
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, operation: z.enum(['undo']) },
|
|
225
|
+
annotations: updates
|
|
226
|
+
}, handle(config, async ({ project, environment, id }) => {
|
|
227
|
+
assertProcessNotProduction('process_batch_update', environment, config);
|
|
228
|
+
return platform.tenant('POST', project, `/bpm/batch-operations/${id}/undo`, { environment });
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { guid, projectParam, environmentParam, readOnly, writes, destructive } from './common.js';
|
|
2
|
+
import { assertInline, writeLocalFile } from './bpmGovernance.js';
|
|
3
|
+
import { checkOcel, checkXes } from '../knowledge/mining.js';
|
|
4
|
+
import { PlatformApiError } from '../platform/errors.js';
|
|
5
|
+
import { assertProcessNotProduction } from './bpm.js';
|
|
6
|
+
import { handle } from '../platform/responses.js';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
const maxReportWindowDays = 366;
|
|
9
|
+
/**
|
|
10
|
+
* Pencere sınırı backend doğrulayıcısıyla aynı hesaplanır: saat dilimsiz tarih-saat UTC sayılır. JS
|
|
11
|
+
* `Date.parse` tarih-yalnız metni UTC, dilimsiz tarih-saati yerel saat okur; gateway Europe/Istanbul'da
|
|
12
|
+
* koşarken '2026-01-01T00:00' – '2027-01-02' 366,125 gün çıkıyor ve backend'in kabul ettiği istek
|
|
13
|
+
* gitmeden duruyordu (Faz 9 incelemesi).
|
|
14
|
+
*/
|
|
15
|
+
const windowTime = (value) => Date.parse(/^\d{4}-\d{2}-\d{2}T[^zZ+]*$/.test(value) && !/[+-]\d{2}:?\d{2}$/.test(value.slice(10)) ? `${value}Z` : value);
|
|
16
|
+
const guidPattern = /^\{?[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}\}?$/i;
|
|
17
|
+
const processSlugPattern = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
18
|
+
export function registerBpmReportingTools(server, platform) {
|
|
19
|
+
const config = platform.config;
|
|
20
|
+
server.registerTool('process_report_get', {
|
|
21
|
+
title: 'Süreç raporunu getir',
|
|
22
|
+
description: "Bir sürecin defterden türetilen raporu (GET bpm/reports/{slug}): startedCount, completedCount, openCount, averageCycleHours (instance-started → instance-completed), slaWithinPercent (terminli ve tamamlanmış görevlerde son termin, uzatma dahil), rejectLoops (ardından yeni görev açılan ret), adminInterventions (atlama, zıplama, göç, yönetici eliyle yeniden atama/iptal/askı), steps[] (düğüm başına görev sayısı, ortalama saat, workedMinutes), units[] (başlatanın birincil birimi), slaBreaches[], longRunningInstanceIds (30 günden eski açık örnekler), isTruncated (5000 örnek tavanı), historyLevel (minimal'de rejectLoops 0 ve slaBreaches boş döner — defter bu satırları yazmaz). Pencere [from, to) en çok 366 gün; arşivlenmiş örnekler rapora girmez. Yetki process.view-all.",
|
|
23
|
+
inputSchema: {
|
|
24
|
+
project: projectParam,
|
|
25
|
+
environment: environmentParam,
|
|
26
|
+
slug: z.string().min(1).max(100),
|
|
27
|
+
from: z.string().min(10).max(40).describe('ISO 8601, ör. 2026-09-01'),
|
|
28
|
+
to: z.string().min(10).max(40).describe('ISO 8601, hariç; from\'dan sonra'),
|
|
29
|
+
applicationId: guid.optional()
|
|
30
|
+
},
|
|
31
|
+
annotations: readOnly
|
|
32
|
+
}, handle(config, ({ project, environment, slug, from, to, applicationId }) => {
|
|
33
|
+
const fromTime = windowTime(from);
|
|
34
|
+
const toTime = windowTime(to);
|
|
35
|
+
if (Number.isNaN(fromTime) || Number.isNaN(toTime))
|
|
36
|
+
throw new PlatformApiError('process_report_get: from ve to ISO 8601 tarih olmalı (istek gönderilmedi).', 400);
|
|
37
|
+
if (toTime <= fromTime)
|
|
38
|
+
throw new PlatformApiError("process_report_get: 'to' 'from'dan sonra olmalı (istek gönderilmedi).", 400);
|
|
39
|
+
if ((toTime - fromTime) / 86_400_000 > maxReportWindowDays)
|
|
40
|
+
throw new PlatformApiError(`process_report_get: pencere en çok ${maxReportWindowDays} gün; aralığı bölün (istek gönderilmedi).`, 400);
|
|
41
|
+
return platform.tenant('GET', project, `/bpm/reports/${encodeURIComponent(slug)}`, { environment, query: { from, to, applicationId } });
|
|
42
|
+
}));
|
|
43
|
+
server.registerTool('process_export_get', {
|
|
44
|
+
title: 'Süreç madenciliği günlüğünü dışa aktar',
|
|
45
|
+
description: "Bir sürecin defterini süreç madenciliği biçiminde indirir (GET bpm/export/{format}): xes — IEEE 1849-2016 XES (iz = örnek, olay = defter satırı; concept:name düğüm, lifecycle:transition, time:timestamp, org:resource eylemci, vekalette asıl sahip zeyrek:principal); ocel — OCEL 2.0 JSON, dosya .json (nesneler process-instance, task, subject, document; olaylar nesne ilişkileriyle). Onay kararı ve imza XES'te ayrı etkinliktir ('kontrol:decided', 'kontrol:signed'), planlama (schedule) olayında org:resource yok. isTruncated true ise dosya 5000 örnekle kırpılmıştır (X-Mining-Truncated) — pencereyi bölün. Pencere rapor gibi: from–to arasında başlayan örnekler, en çok 366 gün, 5000 örnek tavanı; yetki process.view-all. Gateway dosyayı YAPISAL olarak yeniden denetler (XES: log/xes.version, iz ve olay concept:name, tarih; OCEL: dört dizi, bildirilmiş türler, var olan nesneye ilişki) ve { validated, validation: 'structural', problems } döner — resmî XSD/JSON şema doğrulaması değildir. ProM, Celonis ya da PM4Py için outPath verin; satır içi yanıt en çok 256 KB, aşarsa 413.",
|
|
46
|
+
inputSchema: {
|
|
47
|
+
project: projectParam,
|
|
48
|
+
environment: environmentParam,
|
|
49
|
+
format: z.enum(['xes', 'ocel']),
|
|
50
|
+
definition: z.string().min(1).max(100).describe('süreç slug'),
|
|
51
|
+
from: z.string().min(10).max(40).describe('ISO 8601, ör. 2026-09-01'),
|
|
52
|
+
to: z.string().min(10).max(40).describe("ISO 8601, hariç; from'dan sonra"),
|
|
53
|
+
applicationId: guid.optional(),
|
|
54
|
+
outPath: z.string().min(1).optional().describe('verilirse dosya bu yerel yola yazılır ve yanıt yalnız özet döner')
|
|
55
|
+
},
|
|
56
|
+
annotations: readOnly
|
|
57
|
+
}, handle(config, async ({ project, environment, format, definition, from, to, applicationId, outPath }) => {
|
|
58
|
+
const fromTime = windowTime(from);
|
|
59
|
+
const toTime = windowTime(to);
|
|
60
|
+
if (Number.isNaN(fromTime) || Number.isNaN(toTime))
|
|
61
|
+
throw new PlatformApiError('process_export_get: from ve to ISO 8601 tarih olmalı (istek gönderilmedi).', 400);
|
|
62
|
+
if (toTime <= fromTime)
|
|
63
|
+
throw new PlatformApiError("process_export_get: 'to' 'from'dan sonra olmalı (istek gönderilmedi).", 400);
|
|
64
|
+
if ((toTime - fromTime) / 86_400_000 > maxReportWindowDays)
|
|
65
|
+
throw new PlatformApiError(`process_export_get: pencere en çok ${maxReportWindowDays} gün; aralığı bölün (istek gönderilmedi).`, 400);
|
|
66
|
+
const response = await platform.tenantWithHeaders('GET', project, `/bpm/export/${format}`, { environment, query: { definition, from, to, applicationId }, expectBinary: true });
|
|
67
|
+
const payload = response.body;
|
|
68
|
+
const content = payload.bytes.toString('utf8').replace(/^\uFEFF/, '');
|
|
69
|
+
const fileName = /filename\*?=(?:UTF-8'')?"?([^";]+)"?/i.exec(response.headers['content-disposition'] ?? '')?.[1] ?? `${definition}.${format === 'xes' ? 'xes' : 'json'}`;
|
|
70
|
+
const summary = {
|
|
71
|
+
format,
|
|
72
|
+
fileName,
|
|
73
|
+
sizeBytes: payload.bytes.byteLength,
|
|
74
|
+
isTruncated: response.headers['x-mining-truncated'] === 'true',
|
|
75
|
+
...(format === 'xes' ? checkXes(content) : checkOcel(content))
|
|
76
|
+
};
|
|
77
|
+
if (outPath)
|
|
78
|
+
return { ...summary, path: await writeLocalFile('process_export_get', outPath, content) };
|
|
79
|
+
const inline = { ...summary, content };
|
|
80
|
+
assertInline('process_export_get', config, inline);
|
|
81
|
+
return inline;
|
|
82
|
+
}));
|
|
83
|
+
server.registerTool('process_analytics_get', {
|
|
84
|
+
title: 'Süreç analitiğini getir',
|
|
85
|
+
description: "Defterden süreç analitiği (GET bpm/analytics/{slug}/{view}): variants — yol varyantları (art arda aynı düğüm tek adım), örnek sayısı, pay % (paydada yolu olan örnekler; yolu olmayan örnek — history: minimal ya da etkinlik satırı yok — listeye ve paya girmez), ortalama çevrim saati, en çok beş örnek kimliği; heatmap — insan görevi düğümü başına ziyaret, tamamlanan, ortalama ve medyan saat (servis/karar düğümü yok); conformance — tanım dışı geçişler (instance-jumped hedefiyle, node-skipped, instance-migrated), eylemci ve gerekçe; prediction — açık örnek başına kalan süre: aynı yol önekini paylaşan tamamlanmış örneklerin medyanı eksi son adımdan beri geçen süre (sampleSize 0 ise remainingHours alanı yanıtta yok; model eğitimi yok). variants/heatmap/conformance yalnız pencerede başlayan örnekleri, prediction ayrıca pencereden önce başlayıp hâlâ açık olanları kapsar. Yalnız istenen liste gelir, diğer listeler yanıtta yok (gateway boş değerleri budar); isTruncated 5000 örnek tavanı; historyLevel minimal'de yol eksik. Pencere en çok 366 gün; yetki process.view-all.",
|
|
86
|
+
inputSchema: {
|
|
87
|
+
project: projectParam,
|
|
88
|
+
environment: environmentParam,
|
|
89
|
+
definition: z.string().min(1).max(100).describe('süreç slug'),
|
|
90
|
+
view: z.enum(['variants', 'heatmap', 'conformance', 'prediction']),
|
|
91
|
+
from: z.string().min(10).max(40).describe('ISO 8601, ör. 2026-09-01'),
|
|
92
|
+
to: z.string().min(10).max(40).describe("ISO 8601, hariç; from'dan sonra"),
|
|
93
|
+
applicationId: guid.optional()
|
|
94
|
+
},
|
|
95
|
+
annotations: readOnly
|
|
96
|
+
}, handle(config, ({ project, environment, definition, view, from, to, applicationId }) => {
|
|
97
|
+
const fromTime = windowTime(from);
|
|
98
|
+
const toTime = windowTime(to);
|
|
99
|
+
if (Number.isNaN(fromTime) || Number.isNaN(toTime))
|
|
100
|
+
throw new PlatformApiError('process_analytics_get: from ve to ISO 8601 tarih olmalı (istek gönderilmedi).', 400);
|
|
101
|
+
if (toTime <= fromTime)
|
|
102
|
+
throw new PlatformApiError("process_analytics_get: 'to' 'from'dan sonra olmalı (istek gönderilmedi).", 400);
|
|
103
|
+
if ((toTime - fromTime) / 86_400_000 > maxReportWindowDays)
|
|
104
|
+
throw new PlatformApiError(`process_analytics_get: pencere en çok ${maxReportWindowDays} gün; aralığı bölün (istek gönderilmedi).`, 400);
|
|
105
|
+
return platform.tenant('GET', project, `/bpm/analytics/${encodeURIComponent(definition)}/${view}`, { environment, query: { from, to, applicationId } });
|
|
106
|
+
}));
|
|
107
|
+
server.registerTool('process_legal_hold_list', {
|
|
108
|
+
title: 'Yasal bekletmeleri listele',
|
|
109
|
+
description: 'Yasal bekletmeleri (legal hold) en yeniden eskiye listeler: scope (process | instance | user), scopeKey, reason, placedBy/At, releasedBy/At, isActive. Etkin hold kapsadığı kapanmış örneğin arşive taşınmasını ve arşivden silinmesini durdurur. Yetki process.manage.',
|
|
110
|
+
inputSchema: { project: projectParam, environment: environmentParam },
|
|
111
|
+
annotations: readOnly
|
|
112
|
+
}, handle(config, ({ project, environment }) => platform.tenant('GET', project, '/bpm/legal-holds', { environment })));
|
|
113
|
+
server.registerTool('process_legal_hold_create', {
|
|
114
|
+
title: 'Yasal bekletme koy',
|
|
115
|
+
description: "Yasal bekletme koyar (POST bpm/legal-holds). scope process: scopeKey süreç slug'ı; instance: örnek kimliği (defterine legal-hold-placed yazılır); user: kullanıcı kimliği (başlatan, atanan, tamamlayan ya da vekâlet veren olduğu örnekler). Aynı kapsam ve anahtarda etkin hold varsa yenisi açılmaz, o döner (200). Olmayan süreç ya da örnek 404; süreç slug'ı küçük harfe çevrilir. reason zorunlu (dava/denetim numarası). production hedefi istek gitmeden durur (BPM-001) — production'daki bekletme insan işidir. Yetki process.manage.",
|
|
116
|
+
inputSchema: {
|
|
117
|
+
project: projectParam,
|
|
118
|
+
environment: environmentParam,
|
|
119
|
+
scope: z.enum(['process', 'instance', 'user']),
|
|
120
|
+
scopeKey: z.string().min(1).max(200),
|
|
121
|
+
reason: z.string().min(1).max(1000)
|
|
122
|
+
},
|
|
123
|
+
annotations: writes
|
|
124
|
+
}, handle(config, async ({ project, environment, scope, scopeKey, reason }) => {
|
|
125
|
+
assertProcessNotProduction('process_legal_hold_create', environment, config);
|
|
126
|
+
const normalizedKey = scope === 'process' ? scopeKey.trim().toLowerCase() : scopeKey.trim();
|
|
127
|
+
if (scope === 'instance' && !guidPattern.test(normalizedKey))
|
|
128
|
+
throw new PlatformApiError("process_legal_hold_create: scope 'instance' için scopeKey örnek kimliği (GUID) olmalı; process_instance_list ile bulun (istek gönderilmedi).", 400);
|
|
129
|
+
if (scope === 'process' && !processSlugPattern.test(normalizedKey))
|
|
130
|
+
throw new PlatformApiError("process_legal_hold_create: scope 'process' için scopeKey süreç slug'ı olmalı (küçük harf, rakam, tire); process_definition_list ile bulun (istek gönderilmedi).", 400);
|
|
131
|
+
return platform.tenant('POST', project, '/bpm/legal-holds', { environment, body: { scope, scopeKey: normalizedKey, reason } });
|
|
132
|
+
}));
|
|
133
|
+
server.registerTool('process_legal_hold_delete', {
|
|
134
|
+
title: 'Yasal bekletmeyi kaldır',
|
|
135
|
+
description: 'Yasal bekletmeyi kaldırır (DELETE bpm/legal-holds/{id}); kayıt silinmez, releasedAt dolar ve saklama işi kapsadığı örnekleri bir sonraki koşuda arşive taşır. Kaldırılmış hold ikinci kez kaldırılamaz (400). production hedefi istek gitmeden durur (BPM-001). Yetki process.manage.',
|
|
136
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid },
|
|
137
|
+
annotations: destructive
|
|
138
|
+
}, handle(config, ({ project, environment, id }) => {
|
|
139
|
+
assertProcessNotProduction('process_legal_hold_delete', environment, config);
|
|
140
|
+
return platform.tenant('DELETE', project, `/bpm/legal-holds/${id}`, { environment });
|
|
141
|
+
}));
|
|
142
|
+
server.registerTool('process_health_get', {
|
|
143
|
+
title: 'Süreç motoru sağlığı',
|
|
144
|
+
description: "Proje ve ortam için süreç motoru sağlığı (GET bpm/health): status healthy | degraded | unhealthy, timerLagSeconds (vadesi geçmiş en eski timer; 60 sn üstü degraded, 600 sn üstü unhealthy), notificationLagSeconds (gönderilmemiş en eski bildirim; 300 sn üstü degraded), faultedInstanceCount (10 üstü degraded). unhealthy'de platform 503 döner; araç gövdeyi yine verir. Yetki process.view-all.",
|
|
145
|
+
inputSchema: { project: projectParam, environment: environmentParam },
|
|
146
|
+
annotations: readOnly
|
|
147
|
+
}, handle(config, async ({ project, environment }) => {
|
|
148
|
+
const response = await platform.tenantRaw('GET', project, '/bpm/health', { environment });
|
|
149
|
+
const body = response.body;
|
|
150
|
+
if (response.httpStatus === 200 || (response.httpStatus === 503 && typeof body?.data?.status === 'string'))
|
|
151
|
+
return response.body;
|
|
152
|
+
throw new PlatformApiError(`process_health_get: HTTP ${response.httpStatus}${body?.message ? ` — ${body.message}` : ''}`, response.httpStatus);
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive } from './common.js';
|
|
2
|
+
import { PlatformApiError } from '../platform/errors.js';
|
|
3
|
+
import { currentOrganizationId } from './access.js';
|
|
4
|
+
import { assertProcessNotProduction } from './bpm.js';
|
|
5
|
+
import { handle } from '../platform/responses.js';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
const partnerFields = {
|
|
8
|
+
code: z.string().min(1).max(100).describe('ilişki kodu; tanımda assignee partner:<kod>'),
|
|
9
|
+
partnerOrganizationId: guid.describe('karşı organizasyon'),
|
|
10
|
+
direction: z.enum(['one-way', 'two-way']).describe('one-way: yalnız bu organizasyon partnere görev atar; two-way: iki yön'),
|
|
11
|
+
processSlugs: z.array(z.string().min(1).max(100)).min(1).describe('ilişkinin izin verdiği süreç slugları'),
|
|
12
|
+
allowedFields: z.array(z.string().min(1).max(100)).optional().describe('partnerin görebileceği iş satırı alanları')
|
|
13
|
+
};
|
|
14
|
+
export function registerBpmRoutingTools(server, platform) {
|
|
15
|
+
const config = platform.config;
|
|
16
|
+
server.registerTool('process_authority_limit_list', {
|
|
17
|
+
title: 'Yetki limitlerini listele',
|
|
18
|
+
description: "chain-until-authority atamasında kullanılan yetki limitleri (subjectKind user | position, subjectId, category, limit, currency). Zincir, limiti tutarı karşılayan ilk yöneticide durur; pozisyon limiti kişi limitini ezer.",
|
|
19
|
+
inputSchema: { project: projectParam, environment: environmentParam },
|
|
20
|
+
annotations: readOnly
|
|
21
|
+
}, handle(config, ({ project, environment }) => platform.tenant('GET', project, '/bpm/authority-limits', { environment })));
|
|
22
|
+
server.registerTool('process_authority_limit_update', {
|
|
23
|
+
title: 'Yetki limitini kaydet',
|
|
24
|
+
description: "Yetki limitini (subjectKind, subjectId, category) üçlüsüne göre ekler ya da günceller; kayıtta subjectKind, subjectId, category ve limit zorunludur. remove:true yalnız id ile siler (id yoksa istek gitmez). Pozisyon öznesi pozisyon kimliği ya da kodudur (P-SATIS). Tanımdaki kullanım: assignee { rule: 'chain', arg: 'until-authority', category, amount: 'vars.tutar' }.",
|
|
25
|
+
inputSchema: {
|
|
26
|
+
project: projectParam,
|
|
27
|
+
environment: environmentParam,
|
|
28
|
+
id: guid.optional().describe('remove:true için'),
|
|
29
|
+
remove: z.boolean().optional(),
|
|
30
|
+
subjectKind: z.enum(['user', 'position']).optional(),
|
|
31
|
+
subjectId: z.string().max(100).optional(),
|
|
32
|
+
category: z.string().max(100).optional(),
|
|
33
|
+
limit: z.number().nonnegative().optional(),
|
|
34
|
+
currency: z.string().length(3).optional()
|
|
35
|
+
},
|
|
36
|
+
annotations: destructive
|
|
37
|
+
}, handle(config, async ({ project, environment, id, remove, ...body }) => {
|
|
38
|
+
assertProcessNotProduction('process_authority_limit_update', environment, config);
|
|
39
|
+
if (remove) {
|
|
40
|
+
if (!id)
|
|
41
|
+
throw new PlatformApiError('process_authority_limit_update: remove:true id ister; önce process_authority_limit_list ile kimliği bulun (istek gönderilmedi).', 400);
|
|
42
|
+
return platform.tenant('DELETE', project, `/bpm/authority-limits/${id}`, { environment });
|
|
43
|
+
}
|
|
44
|
+
const missing = ['subjectKind', 'subjectId', 'category', 'limit'].filter(field => body[field] === undefined || body[field] === '');
|
|
45
|
+
if (missing.length)
|
|
46
|
+
throw new PlatformApiError(`process_authority_limit_update: kayıt için ${missing.join(', ')} zorunlu (istek gönderilmedi).`, 400);
|
|
47
|
+
return platform.tenant('PUT', project, '/bpm/authority-limits', { environment, body });
|
|
48
|
+
}));
|
|
49
|
+
server.registerTool('process_pool_list', {
|
|
50
|
+
title: 'Atama havuzlarını listele',
|
|
51
|
+
description: "pool:<kod> atamasının havuzları: code, name (yoksa null), üyeler (user | role | unit | position), strategy (round-robin | least-loaded | skill-match | manual), requiredSkills, lastAssignedUserId ve usedBy [{ definitionId, slug, name, locations: ['v1/<düğüm>'] }] — havuzu kullanan süreç tanımları. usedBy boş değilse havuz silinemez (422 ReferencedByProcess). least-loaded sıralaması açık görev / haftalık kapasite (bpm/workloads weeklyLimit, saat; ağırlık) oranıdır; maxOpenTasks dolu ve açık görevi o sayıya ulaşmış kişi manual dışı stratejilerde seçilmez.",
|
|
52
|
+
inputSchema: { project: projectParam, environment: environmentParam },
|
|
53
|
+
annotations: readOnly
|
|
54
|
+
}, handle(config, ({ project, environment }) => platform.tenant('GET', project, '/bpm/pools', { environment })));
|
|
55
|
+
server.registerTool('process_pool_update', {
|
|
56
|
+
title: 'Atama havuzunu kaydet',
|
|
57
|
+
description: "id verilmezse yeni havuz oluşturur (aynı kod 409), verilirse adı/üyeleri/stratejiyi/gerekli becerileri değiştirir; kayıtta code, members ve strategy zorunludur. Tam gövde: güncellemede verilmeyen name ve requiredSkills boşalır, code değişmez. remove:true yalnız id ile havuzu siler (id yoksa istek gitmez); bir süreç tanımı havuzu kullanıyorsa 422 ReferencedByProcess döner ve mesaj süreçleri sayar — önce process_pool_list usedBy'a bakın. Kaydetme yanıtında usedBy null'dır, kullanım yalnız listede hesaplanır. skill-match tanımda assignee.skills ifadeleriyle görev başına beceri de ister.",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
project: projectParam,
|
|
60
|
+
environment: environmentParam,
|
|
61
|
+
id: guid.optional().describe('güncelleme ve remove:true için'),
|
|
62
|
+
remove: z.boolean().optional(),
|
|
63
|
+
code: z.string().min(1).max(100).optional(),
|
|
64
|
+
name: z.string().max(200).optional().describe('ekranda görünen ad; boşsa kod gösterilir'),
|
|
65
|
+
members: z.array(z.object({ kind: z.enum(['user', 'role', 'unit', 'position']), id: z.string().min(1).max(100) })).optional(),
|
|
66
|
+
strategy: z.enum(['round-robin', 'least-loaded', 'skill-match', 'manual']).optional(),
|
|
67
|
+
requiredSkills: z.array(z.string().min(1).max(100)).optional()
|
|
68
|
+
},
|
|
69
|
+
annotations: destructive
|
|
70
|
+
}, handle(config, async ({ project, environment, id, remove, ...body }) => {
|
|
71
|
+
assertProcessNotProduction('process_pool_update', environment, config);
|
|
72
|
+
if (remove) {
|
|
73
|
+
if (!id)
|
|
74
|
+
throw new PlatformApiError('process_pool_update: remove:true id ister; önce process_pool_list ile kimliği bulun (istek gönderilmedi).', 400);
|
|
75
|
+
return platform.tenant('DELETE', project, `/bpm/pools/${id}`, { environment });
|
|
76
|
+
}
|
|
77
|
+
const missing = ['code', 'members', 'strategy'].filter(field => body[field] === undefined || body[field] === '');
|
|
78
|
+
if (missing.length)
|
|
79
|
+
throw new PlatformApiError(`process_pool_update: kayıt için ${missing.join(', ')} zorunlu (istek gönderilmedi).`, 400);
|
|
80
|
+
return id
|
|
81
|
+
? platform.tenant('PUT', project, `/bpm/pools/${id}`, { environment, body })
|
|
82
|
+
: platform.tenant('POST', project, '/bpm/pools', { environment, body });
|
|
83
|
+
}));
|
|
84
|
+
server.registerTool('process_webhook_subscription_list', {
|
|
85
|
+
title: 'Webhook aboneliklerini listele',
|
|
86
|
+
description: 'Giden CloudEvents webhook abonelikleri (url, eventFilters, state active | dead, consecutiveFailures, lastError). Üç ardışık teslim hatasında abonelik dead olur.',
|
|
87
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid.optional() },
|
|
88
|
+
annotations: readOnly
|
|
89
|
+
}, handle(config, ({ project, environment, applicationId }) => platform.tenant('GET', project, '/bpm/webhook-subscriptions', { environment, query: applicationId ? { applicationId } : undefined })));
|
|
90
|
+
server.registerTool('process_webhook_subscription_create', {
|
|
91
|
+
title: 'Webhook aboneliği oluştur',
|
|
92
|
+
description: "HTTPS uca imzalı CloudEvents teslimi (X-Zeyrek-Signature, HMAC-SHA256). eventFilters: '*', 'instance.*', 'task.completed', 'sla.breached' gibi. secret en az 16 karakter; yanıtlarda geri dönmez.",
|
|
93
|
+
inputSchema: {
|
|
94
|
+
project: projectParam,
|
|
95
|
+
environment: environmentParam,
|
|
96
|
+
applicationId: guid.optional(),
|
|
97
|
+
url: z.string().url().max(2000).refine(url => url.startsWith('https://'), { message: 'url https:// ile başlamalı (sunucu yalnız HTTPS kabul eder)' }),
|
|
98
|
+
secret: z.string().min(16).max(256),
|
|
99
|
+
eventFilters: z.array(z.string().min(1).max(100)).optional()
|
|
100
|
+
},
|
|
101
|
+
annotations: writes
|
|
102
|
+
}, handle(config, async ({ project, environment, ...body }) => {
|
|
103
|
+
assertProcessNotProduction('process_webhook_subscription_create', environment, config);
|
|
104
|
+
return platform.tenant('POST', project, '/bpm/webhook-subscriptions', { environment, body });
|
|
105
|
+
}));
|
|
106
|
+
server.registerTool('process_webhook_subscription_delete', {
|
|
107
|
+
title: 'Webhook aboneliğini sil',
|
|
108
|
+
description: 'Aboneliği siler; kuyruktaki teslimler abonelik bulunamadığı için düşer.',
|
|
109
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid },
|
|
110
|
+
annotations: destructive
|
|
111
|
+
}, handle(config, async ({ project, environment, id }) => {
|
|
112
|
+
assertProcessNotProduction('process_webhook_subscription_delete', environment, config);
|
|
113
|
+
return platform.tenant('DELETE', project, `/bpm/webhook-subscriptions/${id}`, { environment });
|
|
114
|
+
}));
|
|
115
|
+
server.registerTool('process_partner_list', {
|
|
116
|
+
title: 'B2B partner ilişkilerini listele',
|
|
117
|
+
description: "Organizasyonun taraf olduğu partner ilişkilerini listeler (GET organizations/{organizationId}/partners, Organization.API, organization.read): code, partnerOrganizationId, direction, processSlugs, allowedFields, state (proposed | active | revoked). Süreçte assignee 'partner:<kod>' yalnız active ve süreci izinli ilişkiyle yayınlanır (422 PartnerUnknown).",
|
|
118
|
+
inputSchema: { organizationId: guid.optional().describe("Verilmezse gateway ORGANIZATION ayarındaki slug'la bulur") },
|
|
119
|
+
annotations: readOnly
|
|
120
|
+
}, handle(config, async ({ organizationId }) => platform.org('GET', `/organizations/${organizationId ?? await currentOrganizationId(platform, 'process_partner_list')}/partners`)));
|
|
121
|
+
server.registerTool('process_partner_create', {
|
|
122
|
+
title: 'B2B partner ilişkisi öner',
|
|
123
|
+
description: "Karşı organizasyona partner ilişkisi önerir (POST organizations/{organizationId}/partners, organization.write): state proposed; karşı taraf process_partner_update { operation: accept } ile kabul edene kadar görev atanamaz. Aynı iki organizasyon arasında açık ilişki ya da aynı kod varsa 409.",
|
|
124
|
+
inputSchema: { organizationId: guid.optional().describe("Verilmezse gateway ORGANIZATION ayarındaki slug'la bulur"), ...partnerFields },
|
|
125
|
+
annotations: writes
|
|
126
|
+
}, handle(config, async ({ organizationId, ...body }) => platform.org('POST', `/organizations/${organizationId ?? await currentOrganizationId(platform, 'process_partner_create')}/partners`, { body })));
|
|
127
|
+
server.registerTool('process_partner_update', {
|
|
128
|
+
title: 'B2B partner ilişkisini güncelle',
|
|
129
|
+
description: "operation update (yalnız öneren taraf; TAM kayıt yazar: code, partnerOrganizationId, direction, processSlugs ZORUNLU, verilmeyen allowedFields BOŞALIR, partnerOrganizationId değiştirilemez (400) — önce process_partner_list ile güncel kaydı okuyun; kapsam genişlerse ilişki yeniden proposed olur) | accept (yalnız karşı taraf; proposed → active) | revoke (iki taraf da; revoked, açık partner görevleri kalır). organizationId çağıranın organizasyonudur (Organization.API, organization.write).",
|
|
130
|
+
inputSchema: {
|
|
131
|
+
organizationId: guid.optional().describe("Verilmezse gateway ORGANIZATION ayarındaki slug'la bulur"),
|
|
132
|
+
id: guid,
|
|
133
|
+
operation: z.enum(['update', 'accept', 'revoke']),
|
|
134
|
+
code: partnerFields.code.optional(),
|
|
135
|
+
partnerOrganizationId: partnerFields.partnerOrganizationId.optional(),
|
|
136
|
+
direction: partnerFields.direction.optional(),
|
|
137
|
+
processSlugs: partnerFields.processSlugs.optional(),
|
|
138
|
+
allowedFields: partnerFields.allowedFields
|
|
139
|
+
},
|
|
140
|
+
annotations: updates
|
|
141
|
+
}, handle(config, async ({ organizationId, id, operation, ...fields }) => {
|
|
142
|
+
const resolvedId = organizationId ?? await currentOrganizationId(platform, 'process_partner_update');
|
|
143
|
+
if (operation !== 'update')
|
|
144
|
+
return platform.org('POST', `/organizations/${resolvedId}/partners/${id}/${operation}`);
|
|
145
|
+
if (!fields.code || !fields.partnerOrganizationId || !fields.direction || !fields.processSlugs) {
|
|
146
|
+
throw new PlatformApiError("process_partner_update: operation 'update' code, partnerOrganizationId, direction ve processSlugs ister (tam kayıt yazılır; istek gönderilmedi).", 400);
|
|
147
|
+
}
|
|
148
|
+
return platform.org('PUT', `/organizations/${resolvedId}/partners/${id}`, { body: fields });
|
|
149
|
+
}));
|
|
150
|
+
}
|