@appaflytech/zeyrek-ai-mcp 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/.env.example +2 -2
  2. package/.env.production.example +9 -1
  3. package/README.md +14 -38
  4. package/dist/config.js +36 -3
  5. package/dist/knowledge/backendConstraints.js +982 -0
  6. package/dist/knowledge/componentCatalog.js +549 -92
  7. package/dist/knowledge/componentEvents.js +120 -25
  8. package/dist/knowledge/coverage.js +43 -0
  9. package/dist/knowledge/flowGuards.js +30 -0
  10. package/dist/knowledge/functionGuards.js +134 -0
  11. package/dist/knowledge/guideSections.js +49 -0
  12. package/dist/knowledge/hydrate.js +97 -0
  13. package/dist/knowledge/languageCodes.js +36 -0
  14. package/dist/knowledge/languages.js +105 -0
  15. package/dist/knowledge/layoutSummary.js +10 -0
  16. package/dist/knowledge/localization.js +51 -0
  17. package/dist/knowledge/normalize.js +500 -16
  18. package/dist/knowledge/outlines.js +167 -12
  19. package/dist/knowledge/policy.js +14 -5
  20. package/dist/knowledge/propTypes.js +80 -0
  21. package/dist/knowledge/rules.js +961 -44
  22. package/dist/knowledge/schemaGuard.js +199 -0
  23. package/dist/knowledge/styleResolve.js +63 -4
  24. package/dist/observability/trace.js +27 -3
  25. package/dist/platform/csv.js +123 -0
  26. package/dist/platform/errors.js +107 -6
  27. package/dist/platform/http.js +34 -4
  28. package/dist/platform/patch.js +27 -0
  29. package/dist/platform/refs.js +104 -0
  30. package/dist/platform/responses.js +25 -6
  31. package/dist/platform/screenshot.js +544 -16
  32. package/dist/platform/secrets.js +40 -0
  33. package/dist/platform/tokenVault.js +89 -0
  34. package/dist/platform/writeDiff.js +281 -0
  35. package/dist/resources/examples/flow-call-function.json +66 -0
  36. package/dist/resources/examples/function-total.json +70 -0
  37. package/dist/resources/examples/layout-app-shell.json +1 -1
  38. package/dist/resources/examples/microfunction-node-sum.json +56 -0
  39. package/dist/resources/examples/microservice-table-list.json +2 -4
  40. package/dist/resources/examples/page-dynamic-form.json +3 -3
  41. package/dist/resources/examples/page-flex-sections.json +99 -0
  42. package/dist/resources/examples/page-sign-in.json +4 -3
  43. package/dist/resources/guides/component.md +81 -8
  44. package/dist/resources/guides/entity.md +712 -20
  45. package/dist/resources/guides/expressions.md +47 -2
  46. package/dist/resources/guides/external-component.md +9 -0
  47. package/dist/resources/guides/functions.md +262 -0
  48. package/dist/resources/guides/getting-started.md +82 -7
  49. package/dist/resources/guides/layout.md +53 -10
  50. package/dist/resources/guides/merge.md +231 -0
  51. package/dist/resources/guides/microfunction.md +166 -0
  52. package/dist/resources/guides/microservice.md +287 -15
  53. package/dist/resources/guides/middleware.md +89 -1
  54. package/dist/resources/guides/modify-existing.md +23 -0
  55. package/dist/resources/guides/page.md +402 -49
  56. package/dist/resources/guides/translation.md +150 -8
  57. package/dist/resources/guides/view.md +16 -1
  58. package/dist/resources/guides/workflow.md +199 -28
  59. package/dist/resources/knowledge.js +5 -0
  60. package/dist/resources/rules.js +27 -0
  61. package/dist/resources/schema-fingerprint.json +8 -0
  62. package/dist/resources/schemas/AnalyzeImportQuery.json +51 -0
  63. package/dist/resources/schemas/ApplyMergePlanCommand.json +19 -0
  64. package/dist/resources/schemas/CreateBulkRowCommand.json +36 -0
  65. package/dist/resources/schemas/CreateDatasourceCommand.json +15 -6
  66. package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +19 -8
  67. package/dist/resources/schemas/CreateEntityCommand.json +52 -12
  68. package/dist/resources/schemas/CreateEnvironmentCommand.json +31 -0
  69. package/dist/resources/schemas/CreateGlobalFunctionCommand.json +95 -0
  70. package/dist/resources/schemas/CreateImportPlanCommand.json +278 -0
  71. package/dist/resources/schemas/CreateMergePlanCommand.json +34 -0
  72. package/dist/resources/schemas/CreateMicrofunctionCommand.json +117 -0
  73. package/dist/resources/schemas/CreateMicrofunctionRuntimeCommand.json +51 -0
  74. package/dist/resources/schemas/CreateMicroserviceCommand.json +145 -3
  75. package/dist/resources/schemas/CreateQueryCommand.json +56 -3
  76. package/dist/resources/schemas/CreateRowCommand.json +38 -0
  77. package/dist/resources/schemas/CreateViewCommand.json +13 -10
  78. package/dist/resources/schemas/CreateWorkflowCommand.json +106 -14
  79. package/dist/resources/schemas/DataJobDto.json +267 -0
  80. package/dist/resources/schemas/ExportRowsCommand.json +172 -0
  81. package/dist/resources/schemas/Filter.json +3 -1
  82. package/dist/resources/schemas/GetRowsQuery.json +6 -1
  83. package/dist/resources/schemas/GlobalFunctionDto.json +89 -0
  84. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +249 -0
  85. package/dist/resources/schemas/ImportAnalysisDto.json +401 -0
  86. package/dist/resources/schemas/ImportPlanDto.json +260 -0
  87. package/dist/resources/schemas/ImportSampleDto.json +90 -0
  88. package/dist/resources/schemas/InvokeMicrofunctionCommand.json +25 -0
  89. package/dist/resources/schemas/PreviewMicroserviceCommand.json +135 -1
  90. package/dist/resources/schemas/PreviewQueryCommand.json +5 -2
  91. package/dist/resources/schemas/PreviewWorkflowCommand.json +45 -9
  92. package/dist/resources/schemas/ResolveMergeConflictsCommand.json +58 -0
  93. package/dist/resources/schemas/RevertMergePlanCommand.json +19 -0
  94. package/dist/resources/schemas/RunImportCommand.json +62 -0
  95. package/dist/resources/schemas/StepDto.json +34 -3
  96. package/dist/resources/schemas/UpdateBulkRowCommand.json +36 -0
  97. package/dist/resources/schemas/UpdateDatasourceCommand.json +15 -6
  98. package/dist/resources/schemas/UpdateEntityCommand.json +52 -11
  99. package/dist/resources/schemas/UpdateGlobalFunctionCommand.json +66 -0
  100. package/dist/resources/schemas/UpdateMicrofunctionCommand.json +121 -0
  101. package/dist/resources/schemas/UpdateMicrofunctionRuntimeCommand.json +54 -0
  102. package/dist/resources/schemas/UpdateMicroserviceCommand.json +147 -4
  103. package/dist/resources/schemas/UpdateQueryCommand.json +13 -3
  104. package/dist/resources/schemas/UpdateRowCommand.json +40 -0
  105. package/dist/resources/schemas/UpdateViewCommand.json +15 -10
  106. package/dist/resources/schemas/UpdateWorkflowCommand.json +106 -14
  107. package/dist/server.js +17 -1
  108. package/dist/tools/access.js +3 -3
  109. package/dist/tools/activities.js +40 -0
  110. package/dist/tools/applications.js +1 -1
  111. package/dist/tools/automations.js +76 -34
  112. package/dist/tools/common.js +55 -0
  113. package/dist/tools/components.js +16 -7
  114. package/dist/tools/datasources.js +83 -21
  115. package/dist/tools/dependencies.js +1 -1
  116. package/dist/tools/deployments.js +34 -2
  117. package/dist/tools/dtos.js +25 -12
  118. package/dist/tools/entities.js +113 -28
  119. package/dist/tools/environments.js +52 -0
  120. package/dist/tools/externalComponents.js +9 -3
  121. package/dist/tools/files.js +57 -42
  122. package/dist/tools/flows.js +205 -13
  123. package/dist/tools/folders.js +7 -2
  124. package/dist/tools/functions.js +331 -0
  125. package/dist/tools/globalFunctions.js +63 -0
  126. package/dist/tools/importSchemas.js +79 -0
  127. package/dist/tools/imports.js +241 -0
  128. package/dist/tools/layouts.js +103 -29
  129. package/dist/tools/merges.js +113 -0
  130. package/dist/tools/microfunctions.js +155 -0
  131. package/dist/tools/microservices.js +270 -40
  132. package/dist/tools/organizations.js +5 -6
  133. package/dist/tools/pages.js +421 -104
  134. package/dist/tools/platformInfo.js +155 -11
  135. package/dist/tools/projects.js +183 -33
  136. package/dist/tools/queries.js +32 -10
  137. package/dist/tools/revisions.js +94 -0
  138. package/dist/tools/rows.js +484 -50
  139. package/dist/tools/screenWrite.js +68 -0
  140. package/dist/tools/screens.js +28 -0
  141. package/dist/tools/templates.js +32 -5
  142. package/dist/tools/translations.js +24 -8
  143. package/dist/tools/views.js +20 -10
  144. package/dist/tools/workflows.js +56 -21
  145. package/package.json +7 -2
@@ -1,15 +1,37 @@
1
1
  import { fetchHost, flowPath, hostParam, hostUpdateBody, unwrap } from './screens.js';
2
- import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive } from './common.js';
2
+ import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive, microOps } from './common.js';
3
3
  import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
4
- import { normalizeFlowSteps, withNormalization } from '../knowledge/normalize.js';
4
+ import { normalizeFlowSteps, unknownSetterTargetWarnings, withNormalization } from '../knowledge/normalize.js';
5
5
  import { flowBodyWarnings } from '../knowledge/coverage.js';
6
6
  import { PlatformApiError } from '../platform/errors.js';
7
7
  import { handle } from '../platform/responses.js';
8
- import { policyRules } from '../knowledge/rules.js';
8
+ import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
9
+ import { resolvePatchOperations } from '../knowledge/outlines.js';
10
+ import { patchResult, baseVersionOf } from '../platform/patch.js';
11
+ import fastJsonPatch from 'fast-json-patch';
12
+ import { policyRules, flowExpressionSites } from '../knowledge/rules.js';
13
+ import { functionCallRefs } from '../knowledge/functionGuards.js';
9
14
  import { randomUUID } from 'node:crypto';
10
15
  import { z } from 'zod';
11
- const stepsParam = z.array(z.record(z.unknown())).describe("Adım listesi (StepManagementSchema: { stepId, name, type, kind, args, branches }). DİKKAT: branches[].nextStepId bir GUID değil, hedef adımın stepId STRING'idir; dal ADI motorun anahtarıdır ve start/setter/code adımlarında 'always' olmak zorundadır. Tam şablon: ngapps://examples/flow-load-table (ya da flow_get çıktısı); adım tipleri ve setter/parameters şekli platform_guide('page') Kural 1c.");
16
+ /**
17
+ * Akış yazması yanıtında gövdeyi geri döndürmüyor, bu yüzden diff için akışı bir kez okuruz.
18
+ * ÖLÇÜLDÜ 2026-09-05 (Corecruiter koşusu, `S-19`): `code` adımının gövdesi kaydedilmedi,
19
+ * `flow_create` 200 döndü, `flow_get` adımı gösterdi ama `args` içinde kodu taşıyan alan yoktu —
20
+ * teşhis ~10 dakika sürdü ve tek belirti filtrenin hiçbir şey yapmamasıydı. Okuma başarısız
21
+ * olursa yazma yine başarılıdır: diff bir teşhis eklentisidir, yazmanın sonucu değil.
22
+ */
23
+ async function flowWriteDrops(platform, host, project, environment, applicationId, hostId, flowId, sent) {
24
+ try {
25
+ const saved = unwrap(await platform.tenant('GET', project, flowPath(host, applicationId, hostId, flowId), { environment }));
26
+ return writeDiff(sent, saved);
27
+ }
28
+ catch {
29
+ return { dropped: [], normalized: [] };
30
+ }
31
+ }
32
+ const stepsParam = z.array(z.record(z.unknown())).describe("Adım listesi (StepManagementSchema: { stepId, name, type, kind, args, branches }). DİKKAT: branches[].nextStepId bir GUID değil, hedef adımın stepId STRING'idir; dal ADI motorun anahtarıdır ve start/setter/code/function adımlarında 'always' olmak zorundadır. Tam şablon: ngapps://examples/flow-load-table (ya da flow_get çıktısı); adım tipleri ve setter/parameters şekli platform_guide('page') Kural 1c. Kullanıcı fonksiyonu çağırmak için 'function' adımı: { type:'function', args:{ functionId:'<function_list id>', functionName:'<ad>', parameters:[{ name:'<bildirimdeki ad>', value:{ type:'static'|'property'|'storage'|'state'|'code', ref } }] } } → çıktı Steps.<adımAdı>.data (ngapps://examples/flow-call-function, platform_guide('functions')).");
12
33
  const policyOverrideParam = z.boolean().optional();
34
+ const patchOperationsParam = z.array(z.record(z.unknown())).min(1).describe("RFC 6902 JSON Patch işlemleri; yolları flow_get çıktısıyla keşfedin. Dizi öğelerinde indeks yerine seçici kullanılabilir: '/steps[stepId=paint]/args/iteratees[element=tbl_recruiters]/mapping/source' — seçici gövde platforma gitmeden indekse çevrilir, çözülemezse istek hiç gönderilmez.");
13
35
  const flowIdParam = guid.describe("Akış id'si; şemadaki events[].value bu id'dir");
14
36
  function flowSummary(flow) {
15
37
  return {
@@ -22,7 +44,15 @@ function flowSummary(flow) {
22
44
  }
23
45
  async function hostEnvelope(platform, host, project, environment, applicationId, hostId) {
24
46
  const record = await fetchHost(platform, host, project, environment, applicationId, hostId);
25
- return { [host]: hostUpdateBody(host, { ...record, applicationId }) };
47
+ return { record, envelope: { [host]: hostUpdateBody(host, { ...record, applicationId }) } };
48
+ }
49
+ /**
50
+ * FLOW-024'ün arayacağı düğüm kümesi. Ham kaydın TAMAMI gezilmez: kayıt kendi `flows`'unu da
51
+ * taşıyabilir ve oradaki bayat element id'leri kümeyi kirletip aramayı sessizce boşa çıkarır.
52
+ * page/component düğümleri `schema`'da, layout'unkiler `views`'ta durur.
53
+ */
54
+ function hostNodes(record) {
55
+ return record.schema ?? record.views ?? [];
26
56
  }
27
57
  async function requiredParameterWarnings(platform, project, environment, applicationId, steps) {
28
58
  const warnings = [];
@@ -107,6 +137,95 @@ function rowsTargetsByStepName(steps) {
107
137
  }
108
138
  return targets;
109
139
  }
140
+ const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
141
+ function toUiFunctionRecord(entry) {
142
+ return {
143
+ id: String(entry.id ?? ''),
144
+ name: String(entry.name ?? ''),
145
+ scope: String(entry.scope ?? 'application'),
146
+ pageId: typeof entry.pageId === 'string' ? entry.pageId : null,
147
+ isAsync: entry.isAsync === true,
148
+ parameters: (Array.isArray(entry.parameters) ? entry.parameters : [])
149
+ .filter(isRecord)
150
+ .map(parameter => ({ name: String(parameter.name ?? ''), required: parameter.required === true, defaultValue: parameter.defaultValue ?? null }))
151
+ };
152
+ }
153
+ /** Uç okunamazsa (eski backend, yetki) `undefined`: uyarı üretilmez — lint yazmanın sonucu değil, eklentisidir. */
154
+ async function loadUiFunctions(platform, project, environment, applicationId) {
155
+ try {
156
+ const payload = await platform.tenant('GET', project, '/global-functions', { environment, query: { applicationId } });
157
+ return (Array.isArray(payload?.data) ? payload.data : []).filter(isRecord).map(toUiFunctionRecord);
158
+ }
159
+ catch {
160
+ return undefined;
161
+ }
162
+ }
163
+ /**
164
+ * FLOW-024 / FLOW-025 (G-04 · G-05): `function` adımının bildirimle eşleşmesi ve ifadelerdeki
165
+ * `Functions.<ad>(…)` çağrılarının gerçek bir fonksiyona çözülmesi. Küme dinamiktir (uygulama
166
+ * fonksiyonları + barındırıcı SAYFANIN fonksiyonları, K2) — bu yüzden politika kuralı değil,
167
+ * ucu okuyan uyarıdır (`requiredParameterWarnings` deseni). docs/16 §5'in "lint yok" kararı
168
+ * fonksiyon kümesi dinamikleştiği için burada revize edildi (G-13).
169
+ */
170
+ async function functionWarnings(platform, host, project, environment, applicationId, hostId, steps) {
171
+ const functionSteps = steps.filter(step => step.type === 'function');
172
+ const callSites = steps.flatMap(step => flowExpressionSites(step)
173
+ .flatMap(site => functionCallRefs(site.expression).map(ref => ({ step, origin: site.origin, ref }))));
174
+ if (functionSteps.length === 0 && callSites.length === 0)
175
+ return [];
176
+ const functions = await loadUiFunctions(platform, project, environment, applicationId);
177
+ if (functions === undefined)
178
+ return [];
179
+ const byId = new Map(functions.map(entry => [entry.id, entry]));
180
+ const byName = new Map(functions.map(entry => [entry.name, entry]));
181
+ const known = functions.map(entry => entry.name).sort().join(', ') || '(hiç fonksiyon yok)';
182
+ const visible = (entry) => entry.scope !== 'page' || (host === 'page' && entry.pageId === hostId);
183
+ const label = (step) => String(step.stepId ?? step.name ?? '?');
184
+ const warnings = new Set();
185
+ for (const step of functionSteps) {
186
+ const args = (step.args ?? {});
187
+ const id = typeof args.functionId === 'string' ? args.functionId : '';
188
+ const fn = byId.get(id);
189
+ if (fn === undefined) {
190
+ if (id !== '')
191
+ warnings.add(`FLOW-024: '${label(step)}' adımının functionId'si (${id}) uygulamada yok — platform 422 GlobalFunctionNotFound verir. Bilinen fonksiyonlar: ${known}. Gerçek id'yi function_list ile alın.`);
192
+ continue;
193
+ }
194
+ if (typeof args.functionName === 'string' && args.functionName !== '' && args.functionName !== fn.name) {
195
+ warnings.add(`FLOW-024: '${label(step)}' adımının functionName'i '${args.functionName}' ama functionId '${fn.name}' fonksiyonuna ait — Studio özeti ve ad yedeği yanlış fonksiyonu gösterir; functionName: '${fn.name}' yazın.`);
196
+ }
197
+ if (!visible(fn)) {
198
+ warnings.add(`FLOW-024: '${label(step)}' adımı '${fn.name}' SAYFA fonksiyonunu çağırıyor ama fonksiyon başka bir sayfaya (${fn.pageId}) ait — bu ${host} bağlamının runtime paketi onu taşımaz, adım 'Functions.${fn.name} is not a function' ile düşer (K2). Fonksiyonu uygulama kapsamına alın ya da bu sayfada tanımlayın.`);
199
+ }
200
+ const given = (Array.isArray(args.parameters) ? args.parameters : []).filter(isRecord).map(parameter => String(parameter.name ?? ''));
201
+ const declared = fn.parameters.map(parameter => parameter.name);
202
+ const unknown = given.filter(name => name !== '' && !declared.includes(name));
203
+ if (unknown.length > 0) {
204
+ warnings.add(`FLOW-024: '${label(step)}' adımı '${fn.name}' fonksiyonunda olmayan parametre geçiyor: ${unknown.join(', ')} — bildirilenler: ${declared.join(', ') || '(parametresiz)'}. Eşleme ada göredir; tanınmayan parametre sessizce düşer.`);
205
+ }
206
+ const missing = fn.parameters.filter(parameter => parameter.required && !given.includes(parameter.name)).map(parameter => parameter.name);
207
+ if (missing.length > 0) {
208
+ warnings.add(`FLOW-024: '${label(step)}' adımında '${fn.name}' fonksiyonunun zorunlu parametresi eksik: ${missing.join(', ')} — çağrı 'Functions.${fn.name}: "${missing[0]}" is required' ile düşer, Steps.${String(step.name ?? label(step))}.error dolar ve akış hata gövdesiyle 'always' dalından devam eder.`);
209
+ }
210
+ }
211
+ for (const { step, origin, ref } of callSites) {
212
+ const fn = byName.get(ref.name);
213
+ if (fn === undefined) {
214
+ warnings.add(`FLOW-025: '${label(step)}' adımının ${origin} ifadesi Functions.${ref.name}(…) çağırıyor ama uygulamada bu adda fonksiyon yok — runtime 'Functions.${ref.name} is not a function' verir; runCode hatayı yutar, hedef prop yazılmaz. Bilinen fonksiyonlar: ${known}.`);
215
+ continue;
216
+ }
217
+ if (!visible(fn)) {
218
+ warnings.add(`FLOW-025: '${label(step)}' adımının ${origin} ifadesi Functions.${ref.name}(…) çağırıyor ama bu bir SAYFA fonksiyonu ve başka sayfaya (${fn.pageId}) ait — bu ${host} bağlamında tanımsızdır (K2).`);
219
+ }
220
+ if (ref.argCount > fn.parameters.length) {
221
+ warnings.add(`FLOW-025: '${label(step)}' adımının ${origin} ifadesi Functions.${ref.name} için ${ref.argCount} argüman geçiyor, fonksiyon ${fn.parameters.length} parametre bildiriyor (${fn.parameters.map(parameter => parameter.name).join(', ') || 'parametresiz'}) — fazlası yok sayılır (konumsal eşleme, K5).`);
222
+ }
223
+ if (fn.isAsync && !(step.type === 'code' && origin === 'args.value')) {
224
+ warnings.add(`FLOW-025: '${label(step)}' adımının ${origin} ifadesi async fonksiyon Functions.${ref.name} çağırıyor — dönüş bir Promise'dir; setter/parametre/condition ifadesinde değer olarak '[object Promise]' kalır. async fonksiyonu code adımında 'await Functions.${ref.name}(…)' ile ya da function adımıyla çağırın.`);
225
+ }
226
+ }
227
+ return [...warnings];
228
+ }
110
229
  export function registerFlowTools(server, platform) {
111
230
  const config = platform.config;
112
231
  server.registerTool('flow_list', {
@@ -146,7 +265,7 @@ export function registerFlowTools(server, platform) {
146
265
  },
147
266
  annotations: writes
148
267
  }, handle(config, async ({ project, environment, host, applicationId, hostId, name, slug, steps, errorSteps, flowId, policyOverride }) => {
149
- const envelope = await hostEnvelope(platform, host, project, environment, applicationId, hostId);
268
+ const { record: hostRecord, envelope } = await hostEnvelope(platform, host, project, environment, applicationId, hostId);
150
269
  const resolvedFlowId = flowId ?? randomUUID();
151
270
  const flow = {
152
271
  id: resolvedFlowId,
@@ -160,16 +279,21 @@ export function registerFlowTools(server, platform) {
160
279
  const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'flow', flow, { operation: 'create' }, policyOverride));
161
280
  const parameterWarnings = [
162
281
  ...await requiredParameterWarnings(platform, project, environment, applicationId, steps),
163
- ...flowBodyWarnings(steps)
282
+ ...await functionWarnings(platform, host, project, environment, applicationId, hostId, steps),
283
+ ...flowBodyWarnings(steps),
284
+ ...unknownSetterTargetWarnings(steps, errorSteps ?? [], hostNodes(hostRecord))
164
285
  ];
165
286
  await platform.tenant('POST', project, flowPath(host, applicationId, hostId), { environment, body: { flow, ...envelope } });
166
- return attachFindings(withNormalization({
287
+ // Diff YALNIZ çağıranın yazdığı alanları kapsar; `id` ve barındırıcı bağı (`pageId` vb.)
288
+ // gövdeye gateway koyar, kullanıcının düzeltebileceği bir şey değildir.
289
+ const drops = await flowWriteDrops(platform, host, project, environment, applicationId, hostId, resolvedFlowId, { name: flow.name, slug: flow.slug, steps: flow.steps, errorSteps: flow.errorSteps });
290
+ return attachFindings(withWriteDiff(withNormalization({
167
291
  host,
168
292
  hostId,
169
293
  flow: { id: resolvedFlowId, name, slug, steps: steps.length },
170
294
  ...(parameterWarnings.length > 0 ? { parameterWarnings } : {}),
171
295
  hint: "Akışı bir olaya bağlamak için: page_schema_patch ops:[{op:'set_event', target:<elementId>, event:'<component_get events listesindeki ad>', eventValue:<flow id>}]. Olay adları komponente özeldir ve 'on' ÖNEKİ ALMAZ (button → 'click', form → 'submit', table → 'mount'/'select'/'sort'…); uydurma ad 200 döner ama bağ sessizce ölü kalır — gateway PAGE-008 ile yakalar."
172
- }, normalized), findings);
296
+ }, normalized), drops), findings);
173
297
  }));
174
298
  server.registerTool('flow_update', {
175
299
  title: 'Akış güncelle',
@@ -192,7 +316,7 @@ export function registerFlowTools(server, platform) {
192
316
  const current = unwrap(await platform.tenant('GET', project, flowPath(host, applicationId, hostId, flowId), { environment }));
193
317
  if (!current.id)
194
318
  throw new PlatformApiError(`'${flowId}' id'li akış bulunamadı; flow_list ile doğrulayın.`);
195
- const envelope = await hostEnvelope(platform, host, project, environment, applicationId, hostId);
319
+ const { record: hostRecord, envelope } = await hostEnvelope(platform, host, project, environment, applicationId, hostId);
196
320
  const flow = {
197
321
  id: flowId,
198
322
  name: name ?? current.name,
@@ -208,10 +332,78 @@ export function registerFlowTools(server, platform) {
208
332
  : [];
209
333
  const parameterWarnings = [
210
334
  ...await requiredParameterWarnings(platform, project, environment, applicationId, flow.steps),
211
- ...flowBodyWarnings(flow.steps)
335
+ ...await functionWarnings(platform, host, project, environment, applicationId, hostId, flow.steps),
336
+ ...flowBodyWarnings(flow.steps),
337
+ ...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord))
212
338
  ];
213
339
  await platform.tenant('PUT', project, flowPath(host, applicationId, hostId, flowId), { environment, body: { flow, ...envelope } });
214
- return attachFindings(withNormalization({ host, hostId, flow: flowSummary(flow), ...(parameterWarnings.length > 0 ? { parameterWarnings } : {}) }, normalized), findings);
340
+ // Kısmi güncellemede yalnız GÖNDERİLEN alanlar karşılaştırılır; verilmeyen alan için
341
+ // "düştü" demek yanlış alarm olurdu.
342
+ const drops = await flowWriteDrops(platform, host, project, environment, applicationId, hostId, flowId, {
343
+ ...(name === undefined ? {} : { name }),
344
+ ...(slug === undefined ? {} : { slug }),
345
+ ...(steps === undefined ? {} : { steps }),
346
+ ...(errorSteps === undefined ? {} : { errorSteps })
347
+ });
348
+ return attachFindings(withWriteDiff(withNormalization({ host, hostId, flow: flowSummary(flow), ...(parameterWarnings.length > 0 ? { parameterWarnings } : {}) }, normalized), drops), findings);
349
+ }));
350
+ server.registerTool('flow_patch', {
351
+ title: "Akışa patch uygula",
352
+ description: "Tam gövdeyi bağlama almadan hedefli değişiklik: gateway akışı çeker, JSON Patch uygular, politika kontrolünden geçirip geri yazar. flow_update REPLACE olduğu için tek bir ifadeyi düzeltmek 10 adımlı gövdenin tamamını yeniden göndermek demekti (09-05 koşusunda aynı 11 KB'lik gövde beş kez gönderildi). Dizi öğelerinde indeks yerine SEÇİCİ kullanın — adım ve iteratee sırası platformda değişebilir: '/steps[stepId=paint]/args/iteratees[element=tbl_funnel]/mapping/mappings/share/ref' ya da '/steps[stepId=load_summary]/args/parameters[name=company]/value/ref'. Seçici çözülemezse istek HİÇ gönderilmez.",
353
+ inputSchema: {
354
+ project: projectParam,
355
+ environment: environmentParam,
356
+ host: hostParam,
357
+ applicationId: guid,
358
+ hostId: guid,
359
+ flowId: flowIdParam,
360
+ operations: patchOperationsParam.optional(),
361
+ ops: patchOperationsParam.optional().describe("'operations' ile aynı alan (eşanlamlı) — sayfa micro-op tool'larıyla aynı adı kullanabilmeniz için"),
362
+ policyOverride: policyOverrideParam
363
+ },
364
+ annotations: updates
365
+ }, handle(config, async ({ project, environment, host, applicationId, hostId, flowId, operations: givenOperations, ops, policyOverride }) => {
366
+ const operations = microOps('flow_patch', ops, givenOperations);
367
+ const current = unwrap(await platform.tenant('GET', project, flowPath(host, applicationId, hostId, flowId), { environment }));
368
+ if (!current.id)
369
+ throw new PlatformApiError(`'${flowId}' id'li akış bulunamadı; flow_list ile doğrulayın.`);
370
+ const document = structuredClone(current);
371
+ const resolved = resolvePatchOperations(document, operations, 'flow_get');
372
+ const patched = fastJsonPatch.applyPatch(document, resolved.operations, true, false).newDocument;
373
+ const { record: hostRecord } = await hostEnvelope(platform, host, project, environment, applicationId, hostId);
374
+ const flow = {
375
+ ...patched,
376
+ id: flowId,
377
+ steps: patched.steps ?? [],
378
+ errorSteps: patched.errorSteps ?? [],
379
+ ...(host === 'page' ? { pageId: hostId } : host === 'layout' ? { layoutId: hostId } : { componentId: hostId })
380
+ };
381
+ const normalized = [...normalizeFlowSteps(flow.steps), ...normalizeFlowSteps(flow.errorSteps)];
382
+ const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'flow', flow, { operation: 'update' }, policyOverride));
383
+ const parameterWarnings = [
384
+ ...await requiredParameterWarnings(platform, project, environment, applicationId, flow.steps),
385
+ ...await functionWarnings(platform, host, project, environment, applicationId, hostId, flow.steps),
386
+ ...flowBodyWarnings(flow.steps),
387
+ ...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord))
388
+ ];
389
+ // Gövde değil FARK gönderilir: barındırıcı zarfı (envelope) yazma yolunda gerekmez, platform
390
+ // PATCH ucu akışı tek başına yazar. Net diff okunan kayıt ile son hâl arasından hesaplanır.
391
+ const netOps = fastJsonPatch.compare(current, flow);
392
+ const saved = await platform.tenant('PATCH', project, flowPath(host, applicationId, hostId, flowId), {
393
+ environment,
394
+ body: { baseVersion: baseVersionOf(current, 'flow_get'), ops: netOps }
395
+ });
396
+ const written = patchResult(saved, flow);
397
+ const drops = await flowWriteDrops(platform, host, project, environment, applicationId, hostId, flowId, { steps: flow.steps, errorSteps: flow.errorSteps });
398
+ return attachFindings(withWriteDiff(withNormalization({
399
+ host,
400
+ hostId,
401
+ applied: operations.length,
402
+ newVersion: written.newVersion,
403
+ patchTargets: resolved.targets,
404
+ flow: flowSummary(written.data),
405
+ ...(parameterWarnings.length > 0 ? { parameterWarnings } : {})
406
+ }, [...normalized, ...resolved.notes.map(note => `patch yolu seçiciden indekse çözüldü: ${note}`)]), drops), findings);
215
407
  }));
216
408
  server.registerTool('flow_delete', {
217
409
  title: 'Akış sil',
@@ -219,7 +411,7 @@ export function registerFlowTools(server, platform) {
219
411
  inputSchema: { project: projectParam, environment: environmentParam, host: hostParam, applicationId: guid, hostId: guid, flowId: flowIdParam },
220
412
  annotations: destructive
221
413
  }, handle(config, async ({ project, environment, host, applicationId, hostId, flowId }) => {
222
- const envelope = await hostEnvelope(platform, host, project, environment, applicationId, hostId);
414
+ const { envelope } = await hostEnvelope(platform, host, project, environment, applicationId, hostId);
223
415
  await platform.tenant('DELETE', project, flowPath(host, applicationId, hostId, flowId), { environment, body: { id: flowId, ...envelope } });
224
416
  return { deleted: flowId, host, hostId };
225
417
  }));
@@ -1,6 +1,8 @@
1
1
  import { guid, projectParam, environmentParam, definitionParam, pagingParams, readOnly, writes, updates, destructive } from './common.js';
2
2
  import { handle } from '../platform/responses.js';
3
3
  import { z } from 'zod';
4
+ import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
5
+ import { policyRules } from '../knowledge/rules.js';
4
6
  export function registerFolderTools(server, platform) {
5
7
  const config = platform.config;
6
8
  server.registerTool('folder_list', {
@@ -17,10 +19,13 @@ export function registerFolderTools(server, platform) {
17
19
  }, handle(config, ({ project, environment, id, breadcrumb }) => platform.tenant('GET', project, breadcrumb ? `/folders/${id}/breadcrumb` : `/folders/${id}`, { environment })));
18
20
  server.registerTool('folder_create', {
19
21
  title: 'Klasör oluştur',
20
- description: "Yeni klasör oluşturur (definition = CreateFolderCommand: { name, parentId? }). DİKKAT: fiziksel klasör adı ve depolama yolu ASCII'ye katlanır ('Donanım' → 'Donanim') ve aynı üst klasörde çakışırsa sonuna ' (1)' eklenir — kullanıcıya gösterilen displayName ham adı korur. Yanıttaki name/path katlanmış hâldir, dosya yollarını oradan okuyun.",
22
+ description: "Yeni klasör oluşturur (definition = CreateFolderCommand: { name, parentId? }). DİKKAT: fiziksel klasör adı ve depolama yolu ASCII'ye katlanır ('Donanım' → 'Donanim') ve aynı üst klasörde çakışırsa sonuna ' (1)' eklenir — kullanıcıya gösterilen displayName ham adı korur. Yanıttaki name/path katlanmış hâldir, dosya yollarını oradan okuyun. Backend ön kontrolü (FLD-001, block): name zorunlu ve \\|!#~$%&/=?»«@£§€{};'<>, karakterlerini içeremez.",
21
23
  inputSchema: { project: projectParam, environment: environmentParam, definition: definitionParam },
22
24
  annotations: writes
23
- }, handle(config, ({ project, environment, definition }) => platform.tenant('POST', project, '/folders', { environment, body: definition })));
25
+ }, handle(config, async ({ project, environment, definition }) => {
26
+ const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'folder', definition, { operation: 'create' }));
27
+ return attachFindings(await platform.tenant('POST', project, '/folders', { environment, body: definition }), findings);
28
+ }));
24
29
  server.registerTool('folder_update', {
25
30
  title: 'Klasör yetkilerini güncelle',
26
31
  description: "Klasörün YETKİLENDİRMESİNİ günceller — `UpdateFolderCommand`'ın alanları yalnız `middlewareIds` ve `roleIds`'tir. Ad değiştirmek için folder_rename kullanın; **klasör TAŞIMA (parentId değiştirme) platformda desteklenmiyor** — gövdeye parentId/name koyarsanız sessizce yok sayılır ve istek yine 200 döner (etkinlik turu Ç3).",