@appaflytech/zeyrek-ai-mcp 0.0.23 → 0.0.24
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/dist/config.js +9 -0
- package/dist/index.js +2 -0
- package/dist/knowledge/componentCatalog.js +1 -1
- package/dist/knowledge/mobileComponentCatalog.js +231 -58
- package/dist/knowledge/mobileComponentEvents.js +118 -3
- package/dist/knowledge/normalize.js +16 -6
- package/dist/knowledge/platform/lint.js +110 -3
- package/dist/knowledge/platform/mobileRules.js +64 -8
- package/dist/knowledge/rules.js +56 -7
- package/dist/knowledge/styleResolve.js +49 -3
- package/dist/knowledge/styleResolveNative.js +104 -4
- package/dist/observability/trace.js +1 -1
- package/dist/platform/argumentAliases.js +128 -0
- package/dist/platform/bodyFile.js +103 -0
- package/dist/platform/capture/capture.js +29 -5
- package/dist/platform/capture/console-log.js +80 -0
- package/dist/platform/capture/index.js +1 -0
- package/dist/platform/diagnostics.js +10 -4
- package/dist/platform/http.js +23 -7
- package/dist/platform/responses.js +6 -2
- package/dist/platform/schemaReadMemory.js +60 -0
- package/dist/platform/secrets.js +9 -0
- package/dist/platform/strictArguments.js +68 -12
- package/dist/platform/tokenVault.js +187 -4
- package/dist/resources/guides/expressions.md +1 -1
- package/dist/resources/guides/microservice.md +9 -2
- package/dist/resources/guides/middleware.md +5 -1
- package/dist/resources/guides/mobile.md +150 -14
- package/dist/resources/guides/page.md +146 -39
- package/dist/resources/guides/theme.md +4 -1
- package/dist/resources/guides/workflow.md +7 -1
- package/dist/resources/schema-fingerprint.json +2 -2
- package/dist/server.js +1 -1
- package/dist/tools/datasources.js +1 -1
- package/dist/tools/designSchemaPatch.js +4 -3
- package/dist/tools/externalComponents.js +1 -1
- package/dist/tools/flows.js +70 -4
- package/dist/tools/layouts.js +5 -5
- package/dist/tools/microservices.js +26 -10
- package/dist/tools/pages.js +192 -36
- package/dist/tools/platformInfo.js +4 -1
- package/dist/tools/rows.js +7 -1
- package/dist/tools/schemaOps.js +1 -1
- package/dist/tools/screenWrite.js +4 -3
- package/dist/tools/screens.js +81 -0
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -84,7 +84,7 @@ const moduleRegistrations = {
|
|
|
84
84
|
mobile: registerMobileTools,
|
|
85
85
|
};
|
|
86
86
|
export function createGatewayServer(platform) {
|
|
87
|
-
const server = rejectUnknownArguments(instrumentServer(new McpServer({ name: "ngapps", version: "0.1.0" }), platform.config));
|
|
87
|
+
const server = rejectUnknownArguments(instrumentServer(new McpServer({ name: "ngapps", version: "0.1.0" }), platform.config), platform);
|
|
88
88
|
for (const moduleName of platform.config.modules) {
|
|
89
89
|
moduleRegistrations[moduleName](server, platform);
|
|
90
90
|
}
|
|
@@ -118,7 +118,7 @@ export function registerDatasourceTools(server, platform) {
|
|
|
118
118
|
}, handle(config, ({ project, environment }) => platform.tenant('GET', project, '/datasources/resources', { environment })));
|
|
119
119
|
server.registerTool('datasource_configuration_options', {
|
|
120
120
|
title: 'Yapılandırma seçenekleri',
|
|
121
|
-
description: "Datasource'un alan tipi kataloğunu döner: commonType başına dbType listesi, varsayılan tip, tipin kabul ettiği seçenek anahtarları (length/precision/collation/...) ve dönüştürülebilir tipler; ayrıca ilişki kuralları (onUpdate/onDelete) ve identity generation tipleri. VARSAYILAN YANIT ÖZETTİR (~5 KB): collation DEĞER listesi (816 kayıt, tam yanıtın %79'u) dışarıdadır — tipin collation kabul edip etmediği zaten options'ta yazar. Collation değerleri gerçekten gerekiyorsa includeCollations: true ile tam yanıtı isteyin (~45 KB).",
|
|
121
|
+
description: "Datasource'un alan tipi kataloğunu döner: commonType başına dbType listesi, varsayılan tip, tipin kabul ettiği seçenek anahtarları (length/precision/collation/...) ve dönüştürülebilir tipler; ayrıca ilişki kuralları (onUpdate/onDelete) ve identity generation tipleri. İlişki alanına yazarken kuralı SAYI olarak verin (`None` 0 · `Cascade` 1 · `SetNull` 2 · `SetDefault` 3 — `Rule` sırası): her backend sürümünde çalışır. Katalog adı listeler; `onDelete` adı (`Cascade`) 2026-09-27'den sonraki backend'de de kabul edilir, `onUpdate` adı hâlâ reddedilir (500). VARSAYILAN YANIT ÖZETTİR (~5 KB): collation DEĞER listesi (816 kayıt, tam yanıtın %79'u) dışarıdadır — tipin collation kabul edip etmediği zaten options'ta yazar. Collation değerleri gerçekten gerekiyorsa includeCollations: true ile tam yanıtı isteyin (~45 KB).",
|
|
122
122
|
inputSchema: {
|
|
123
123
|
project: projectParam,
|
|
124
124
|
environment: environmentParam,
|
|
@@ -87,7 +87,8 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
87
87
|
if (deletedFlows.length > 0 && allowFlowDeletion !== true)
|
|
88
88
|
throw new PlatformApiError(orphanedFlowMessage(deletedFlows), 400);
|
|
89
89
|
const added = opRecords.filter(entry => entry.op === 'add_component').map(entry => entry.component);
|
|
90
|
-
|
|
90
|
+
const resolved = await resolveProfile(platform, applicationId);
|
|
91
|
+
assertScreenWriteGuards(spec.kind, { schema: added }, resolved.profile.platform);
|
|
91
92
|
const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, added);
|
|
92
93
|
const flowWarnings = opRecords.some(entry => entry.op === 'remove_component')
|
|
93
94
|
? [...orphanedFlowWarnings(nodes, events, opRecords), ...staleSetterWarnings(Array.isArray(record.flows) ? record.flows : [], opRecords, nodes)]
|
|
@@ -102,12 +103,12 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
102
103
|
|| (entry.op === 'replace_prop' && FLEX_LAYOUT_PROP_NAMES.has(String(entry.prop ?? ''))));
|
|
103
104
|
const nested = simulatable ? nestedRepeaterNodes(simulated) : [];
|
|
104
105
|
if (nested.length > 0)
|
|
105
|
-
throw new PlatformApiError(nestedRepeaterMessage(nested), 400);
|
|
106
|
-
const resolved = await resolveProfile(platform, applicationId);
|
|
106
|
+
throw new PlatformApiError(nestedRepeaterMessage(nested, resolved.profile.platform), 400);
|
|
107
107
|
const reshaped = simulatable || opRecords.some(entry => entry.op === 'remove_component');
|
|
108
108
|
const profileWarnings = await screenProfileWarnings(platform, resolved, project, environment, applicationId, {
|
|
109
109
|
kind: spec.kind,
|
|
110
110
|
ops: [...opRecords, ...propTypeOps],
|
|
111
|
+
savedNodes: nodes,
|
|
111
112
|
steps: await boundFlowSteps(platform, resolved, project, environment, host, applicationId, id, opRecords)
|
|
112
113
|
});
|
|
113
114
|
const layoutWarnings = reshaped
|
|
@@ -21,7 +21,7 @@ async function assertExternalComponentsAllowed(platform, applicationId, tool) {
|
|
|
21
21
|
throw new PlatformApiError(`${tool}: ${findingLine({
|
|
22
22
|
code: 'MOB-006',
|
|
23
23
|
severity: 'warn',
|
|
24
|
-
message: "harici bileşen mobil uygulamada desteklenmiyor — protokol v1 tarayıcıda koşan .mjs custom element'tir, native çizer onu yükleyemez (mobil protokol v2 Faz 3). Çağrı platforma gönderilmedi; mobil ekranı katalog bileşenleriyle kurun (component_list)."
|
|
24
|
+
message: "harici bileşen mobil uygulamada desteklenmiyor — protokol v1 tarayıcıda koşan .mjs custom element'tir, native çizer onu yükleyemez (mobil protokol v2 Faz 3). Çağrı platforma gönderilmedi; mobil ekranı katalog bileşenleriyle kurun (component_list). Katalogda karşılığı olmayan küçük görünüm için geçici yol 'html' bileşenidir: içerik korumalı belgede tema değişkenleriyle çizilir, fireEvent/ngApps.emit ile akışa bağlanır (component_get('html'))."
|
|
25
25
|
})}`, 400);
|
|
26
26
|
}
|
|
27
27
|
/** Platformun köprü protokolü; scaffold ve publish bu sürüme göre üretir/doğrular (backend ExternalComponentRules.CurrentProtocolVersion) */
|
package/dist/tools/flows.js
CHANGED
|
@@ -275,7 +275,7 @@ async function functionWarnings(platform, host, project, environment, applicatio
|
|
|
275
275
|
}
|
|
276
276
|
const missing = fn.parameters.filter(parameter => parameter.required && !given.includes(parameter.name)).map(parameter => parameter.name);
|
|
277
277
|
if (missing.length > 0) {
|
|
278
|
-
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
|
|
278
|
+
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.message dolar ve .data null'dur; errorSteps yoksa akış 'always' dalından devam eder.`);
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
for (const { step, origin, ref } of callSites) {
|
|
@@ -296,6 +296,60 @@ async function functionWarnings(platform, host, project, environment, applicatio
|
|
|
296
296
|
}
|
|
297
297
|
return [...warnings];
|
|
298
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* FLOW-028 (g-02, ADR 0004 — ZM-07). `microservice.ts` `.finally` üç yoldan birini dener: adımın
|
|
301
|
+
* kendi `error` dalı bağlıysa oradan sürer, yoksa flow `errorSteps`'i (`engine.onError`) devralır,
|
|
302
|
+
* ikisi de yoksa `always` koşar ve storage artık `{ data: null, headers: null, error }`'dır — hata
|
|
303
|
+
* gövdesi ARTIK `data`'da DEĞİL. İkisi de bağlı değilken bu adımın `.data`'sını okuyan bir
|
|
304
|
+
* setter/condition varsa hata anında NULL üzerinden koşar ve yazar hiç fark etmez. Aynı yürüyüş
|
|
305
|
+
* FLOW-024/025'in kullandığı `flowExpressionSites` taramasıdır; setter'ın STORAGE (kod olmayan)
|
|
306
|
+
* referansı ayrıca kendi düz alanından okunur (FLOW-008/009 deseni, `rowsTargetsByStepName`).
|
|
307
|
+
*/
|
|
308
|
+
function microserviceAlwaysDataWarnings(steps, errorSteps) {
|
|
309
|
+
const mainSteps = (Array.isArray(steps) ? steps : []).filter(isRecord);
|
|
310
|
+
const hasErrorFlow = (Array.isArray(errorSteps) ? errorSteps : []).filter(isRecord).some(step => String(step.type ?? '') !== 'start');
|
|
311
|
+
const label = (step) => String(step.stepId ?? step.name ?? '?');
|
|
312
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
313
|
+
const warnings = new Set();
|
|
314
|
+
for (const step of mainSteps) {
|
|
315
|
+
if (String(step.type ?? '') !== 'microservice')
|
|
316
|
+
continue;
|
|
317
|
+
const name = String(step.name ?? step.stepId ?? '');
|
|
318
|
+
if (name === '')
|
|
319
|
+
continue;
|
|
320
|
+
const hasErrorBranch = (Array.isArray(step.branches) ? step.branches : [])
|
|
321
|
+
.filter(isRecord)
|
|
322
|
+
.some(branch => String(branch.name ?? '') === 'error' && Boolean(branch.nextStepId));
|
|
323
|
+
if (hasErrorBranch || hasErrorFlow)
|
|
324
|
+
continue;
|
|
325
|
+
const pattern = new RegExp(`Steps\\s*(?:\\.\\s*${escapeRegExp(name)}\\b|\\[\\s*['"]${escapeRegExp(name)}['"]\\s*\\])\\s*\\??\\.\\s*data\\b`);
|
|
326
|
+
const message = (other) => `FLOW-028: '${label(other)}' adımı 'always' dalında '${name}.data' okuyor ama '${name}' adımının ne bağlı bir 'error' dalı ne akışın 'errorSteps'i var — uç hata dönerse bu adımın 'data'sı NULL olur (eskiden hata gövdesiydi, ADR 0004) ve '${label(other)}' boş/null veriyle koşar. '${name}' adımına 'error' dalı bağlayın ya da flow'a errorSteps verin.`;
|
|
327
|
+
for (const other of mainSteps) {
|
|
328
|
+
if (other === step)
|
|
329
|
+
continue;
|
|
330
|
+
if (String(other.type ?? '') === 'setter') {
|
|
331
|
+
const iteratees = Array.isArray(other.args?.iteratees)
|
|
332
|
+
? other.args.iteratees.filter(isRecord)
|
|
333
|
+
: [];
|
|
334
|
+
for (const iteratee of iteratees) {
|
|
335
|
+
const mapping = (iteratee.mapping ?? {});
|
|
336
|
+
const value = (iteratee.value ?? {});
|
|
337
|
+
const reference = typeof mapping.source === 'string' && mapping.source.length > 0
|
|
338
|
+
? mapping.source
|
|
339
|
+
: typeof value.ref === 'string' ? value.ref : '';
|
|
340
|
+
if (value.type === 'storage' && (reference === `${name}.data` || reference.startsWith(`${name}.data.`) || reference.startsWith(`${name}.data[`))) {
|
|
341
|
+
warnings.add(message(other));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
for (const site of flowExpressionSites(other)) {
|
|
346
|
+
if (pattern.test(site.expression))
|
|
347
|
+
warnings.add(message(other));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return [...warnings];
|
|
352
|
+
}
|
|
299
353
|
export function registerFlowTools(server, platform) {
|
|
300
354
|
const config = platform.config;
|
|
301
355
|
server.registerTool('flow_list', {
|
|
@@ -369,7 +423,8 @@ export function registerFlowTools(server, platform) {
|
|
|
369
423
|
...await requiredParameterWarnings(platform, project, environment, applicationId, steps),
|
|
370
424
|
...await functionWarnings(platform, host, project, environment, applicationId, hostId, steps),
|
|
371
425
|
...flowBodyWarnings(steps),
|
|
372
|
-
...unknownSetterTargetWarnings(steps, errorSteps ?? [], hostNodes(hostRecord))
|
|
426
|
+
...unknownSetterTargetWarnings(steps, errorSteps ?? [], hostNodes(hostRecord)),
|
|
427
|
+
...microserviceAlwaysDataWarnings(steps, errorSteps ?? [])
|
|
373
428
|
];
|
|
374
429
|
// Gövde yalnız akıştır (designer-partial-update K1): barındırıcı zarfı platformda hâlâ isteğe bağlı
|
|
375
430
|
// ama gönderilirse okunan tam kaydı yeniden yazar, açık Studio designer'ının kayıtlı tabanını
|
|
@@ -438,7 +493,8 @@ export function registerFlowTools(server, platform) {
|
|
|
438
493
|
...await requiredParameterWarnings(platform, project, environment, applicationId, flow.steps),
|
|
439
494
|
...await functionWarnings(platform, host, project, environment, applicationId, hostId, flow.steps),
|
|
440
495
|
...flowBodyWarnings(flow.steps),
|
|
441
|
-
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord))
|
|
496
|
+
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord)),
|
|
497
|
+
...microserviceAlwaysDataWarnings(flow.steps, flow.errorSteps)
|
|
442
498
|
];
|
|
443
499
|
await platform.tenant('PUT', project, flowPath(host, applicationId, hostId, flowId), { environment, body: { flow } });
|
|
444
500
|
// Kısmi güncellemede yalnız GÖNDERİLEN alanlar karşılaştırılır; verilmeyen alan için
|
|
@@ -490,7 +546,8 @@ export function registerFlowTools(server, platform) {
|
|
|
490
546
|
...await requiredParameterWarnings(platform, project, environment, applicationId, flow.steps),
|
|
491
547
|
...await functionWarnings(platform, host, project, environment, applicationId, hostId, flow.steps),
|
|
492
548
|
...flowBodyWarnings(flow.steps),
|
|
493
|
-
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord))
|
|
549
|
+
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord)),
|
|
550
|
+
...microserviceAlwaysDataWarnings(flow.steps, flow.errorSteps)
|
|
494
551
|
];
|
|
495
552
|
// Gövde değil FARK gönderilir; platform PATCH ucu akışı tek başına yazar. Net diff okunan kayıt ile son hâl arasından hesaplanır.
|
|
496
553
|
const netOps = fastJsonPatch.compare(current, flow);
|
|
@@ -521,4 +578,13 @@ export function registerFlowTools(server, platform) {
|
|
|
521
578
|
await platform.tenant('DELETE', project, flowPath(host, applicationId, hostId, flowId), { environment, body: { id: flowId } });
|
|
522
579
|
return { deleted: flowId, host, hostId };
|
|
523
580
|
}));
|
|
581
|
+
server.registerTool('flow_restore', {
|
|
582
|
+
title: 'Silinen akışı geri yükle',
|
|
583
|
+
description: "Silinmiş bir akışı revizyon defterindeki son içeriğiyle (ad, slug, adımlar, barındırıcı) AYNI id ile yeniden kurar. Sayfa kaydı bağı kalmayan akışı kalıcı siler (FLOW-002, allowFlowDeletion) — Studio'nun eski sekmesi boş şema kaydettiğinde de (ZM-27). Akışı olaya YENİDEN BAĞLAMAZ: geri geldikten sonra set_event ile bağlayın. SINIR: adımları sayfanın bir komponentini hedefliyorsa (tetikleyen element, iterate/parametre komponenti) o komponent de şemada olmalıdır, yoksa geri yükleme reddedilir — sayfa boşaldıysa (ZM-27) page_restore kullanın: sayfayı ve akışlarını birlikte geri kurar. Akış hâlâ varsa 409, saklama süresi dolup revizyonu budanmışsa 404 döner. Silinen akışı revision_list(module: 'flow') satırlarında operation: delete olarak bulun; recordId akış id'sidir.",
|
|
584
|
+
inputSchema: { project: projectParam, environment: environmentParam, flowId: flowIdParam },
|
|
585
|
+
annotations: writes
|
|
586
|
+
}, handle(config, async ({ project, environment, flowId }) => {
|
|
587
|
+
const restored = unwrap(await platform.tenant('POST', project, `/flows/${flowId}/restore`, { environment }));
|
|
588
|
+
return { restored: flowId, flow: restored };
|
|
589
|
+
}));
|
|
524
590
|
}
|
package/dist/tools/layouts.js
CHANGED
|
@@ -99,8 +99,8 @@ export function registerLayoutTools(server, platform) {
|
|
|
99
99
|
},
|
|
100
100
|
annotations: writes
|
|
101
101
|
}, handle(config, async ({ project, environment, applicationId, name, views, events, parameters }) => {
|
|
102
|
-
assertScreenWriteGuards('layout', { schema: views, events });
|
|
103
102
|
const resolved = await resolveProfile(platform, applicationId);
|
|
103
|
+
assertScreenWriteGuards('layout', { schema: views, events }, resolved.profile.platform);
|
|
104
104
|
const fixed = normalizeComponentDefaults(views);
|
|
105
105
|
const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, views);
|
|
106
106
|
const gaps = [
|
|
@@ -138,13 +138,13 @@ export function registerLayoutTools(server, platform) {
|
|
|
138
138
|
annotations: updates
|
|
139
139
|
}, handle(config, async ({ project, environment, applicationId, id, ...changes }) => {
|
|
140
140
|
const overrides = Object.fromEntries(Object.entries(changes).filter(([, value]) => value !== undefined));
|
|
141
|
-
|
|
141
|
+
const resolved = overrides.views === undefined ? undefined : await resolveProfile(platform, applicationId);
|
|
142
|
+
assertScreenWriteGuards('layout', { schema: overrides.views, events: overrides.events }, resolved?.profile.platform);
|
|
142
143
|
const current = await fetchHost(platform, 'layout', project, environment, applicationId, id);
|
|
143
144
|
const fixed = normalizeComponentDefaults(overrides.views);
|
|
144
145
|
const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, overrides.views);
|
|
145
146
|
const body = layoutUpdateBody({ ...current, applicationId }, overrides);
|
|
146
147
|
await platform.tenant('PUT', project, `/layouts/${applicationId}/${id}`, { environment, body });
|
|
147
|
-
const resolved = overrides.views === undefined ? undefined : await resolveProfile(platform, applicationId);
|
|
148
148
|
const gaps = overrides.views === undefined || !resolved
|
|
149
149
|
? []
|
|
150
150
|
: [
|
|
@@ -206,7 +206,8 @@ export function registerLayoutTools(server, platform) {
|
|
|
206
206
|
const overrides = Object.fromEntries(['name', 'views', 'events', 'parameters']
|
|
207
207
|
.filter(key => patched[key] !== undefined)
|
|
208
208
|
.map(key => [key, patched[key]]));
|
|
209
|
-
|
|
209
|
+
const profile = await resolveProfile(platform, applicationId);
|
|
210
|
+
assertScreenWriteGuards('layout', { schema: overrides.views, events: overrides.events }, profile.profile.platform);
|
|
210
211
|
const fixed = normalizeComponentDefaults(overrides.views);
|
|
211
212
|
const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, overrides.views);
|
|
212
213
|
const body = layoutUpdateBody({ ...current, applicationId }, overrides);
|
|
@@ -219,7 +220,6 @@ export function registerLayoutTools(server, platform) {
|
|
|
219
220
|
body: { baseVersion: baseVersionOf(document, 'layout_get'), ops: netOps }
|
|
220
221
|
});
|
|
221
222
|
const written = patchResult(saved, finalDocument);
|
|
222
|
-
const profile = await resolveProfile(platform, applicationId);
|
|
223
223
|
const gaps = [
|
|
224
224
|
...(profile.profile.layout.blocksMeaningful ? verticalGapWarnings(body.views, 'LAY-003 (uyarı)') : []),
|
|
225
225
|
...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'),
|
|
@@ -6,13 +6,14 @@ import { normalizeMicroserviceDefinition, normalizeMicroservicePatchOps, withNor
|
|
|
6
6
|
import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
|
|
7
7
|
import { microserviceOutline, workflowOutline, resolvePart, resolvePatchOperations } from '../knowledge/outlines.js';
|
|
8
8
|
import { patchResult, baseVersionOf } from '../platform/patch.js';
|
|
9
|
-
import { policyRules } from '../knowledge/rules.js';
|
|
9
|
+
import { filterOperatorError, policyRules } from '../knowledge/rules.js';
|
|
10
10
|
import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
|
|
11
|
-
import { identityHeaders } from '../platform/tokenVault.js';
|
|
11
|
+
import { identityHeaders, resolveIdentity } from '../platform/tokenVault.js';
|
|
12
12
|
import { PlatformApiError } from '../platform/errors.js';
|
|
13
13
|
import { existingMatch, ifExistsParam, listAll, platformRename, withOutcome, withRename } from '../platform/ifExists.js';
|
|
14
14
|
import { outlineWrite, recordWriteViewParam } from '../platform/writeView.js';
|
|
15
15
|
import { handle } from '../platform/responses.js';
|
|
16
|
+
import { headersForMode } from '../platform/http.js';
|
|
16
17
|
import fastJsonPatch from 'fast-json-patch';
|
|
17
18
|
import { z } from 'zod';
|
|
18
19
|
import { constraintSummary } from '../knowledge/backendConstraints.js';
|
|
@@ -68,7 +69,7 @@ async function queryScopeFindings(platform, project, environment, definition) {
|
|
|
68
69
|
}
|
|
69
70
|
const patchOperationsParam = z.array(z.record(z.unknown())).min(1)
|
|
70
71
|
.describe("RFC 6902 JSON Patch işlemleri; path'leri view:'outline'/'part' ile keşfedin. Dizi öğelerinde indeks yerine seçici kullanılabilir: '/sourceParameters/joins[alias=customers]/path' — seçici gövde platforma gitmeden indekse çevrilir, çözülemezse istek hiç gönderilmez.");
|
|
71
|
-
const asUserParam = z.string().min(1).optional().describe("Uygulama üyesinin kimliği: istek gateway'in servis jetonu yerine bu kimlikle gider. Jeton HEM Authorization başlığında HEM NG_AccessToken çerezinde taşınır, böylece jetonu ÇEREZDEN okuyan middleware de geçer — uca middleware bağlandığı anda bu alan olmadan çağrı 401 döner (09-05 koşusu S-21).
|
|
72
|
+
const asUserParam = z.string().min(1).optional().describe("Uygulama üyesinin kimliği: istek gateway'in servis jetonu yerine bu kimlikle gider. Jeton HEM Authorization başlığında HEM NG_AccessToken çerezinde taşınır, böylece jetonu ÇEREZDEN okuyan middleware de geçer — uca middleware bağlandığı anda bu alan olmadan çağrı 401 döner (09-05 koşusu S-21). ÜÇ ŞEKİL kabul edilir: (1) TERCİH EDİLEN `test:<ad>` — gateway kök .env'deki MCP_TEST_USER_<AD> satırıyla giriş ucunu kendisi çağırır, jetonu kasadan yeniden kullanır (satırı insan yazar), (2) `tok_…` referansı (giriş ucunu çağırdığınızda yanıtın tokenRefs alanında döner; ömür MCP_TOKEN_TTL_MINUTES, MCP_STATE_DIR varsa gateway yeniden başlayınca da çözülür), (3) ham JWT. anonymous ile birlikte verilemez.");
|
|
72
73
|
const cookiesParam = z.record(z.string()).optional().describe("İsteğe eklenecek çerezler (ad → değer): uygulamanın KENDİ oturum çerezi buraya verilir (ör. { CORE_Token: '<jwt>' }). asUser verildiğinde gateway NG_AccessToken çerezini zaten kendisi kurar; bu alan yalnız uygulama başka bir çerez adı okuyorsa gerekir. anonymous ile birlikte verilemez.");
|
|
73
74
|
const executeInput = {
|
|
74
75
|
project: projectParam,
|
|
@@ -189,6 +190,13 @@ function unboundQueryParameters(value, into = []) {
|
|
|
189
190
|
unboundQueryParameters(entry, into);
|
|
190
191
|
return into;
|
|
191
192
|
}
|
|
193
|
+
/** Çağrının dinamik filtresi (`filters`, sorguda ya da gövdede) bilinen işleçlerle mi — değilse istek gitmez (g-03). */
|
|
194
|
+
function assertFilterOperators(tool, bound) {
|
|
195
|
+
const bodyFilters = bound.body && !Array.isArray(bound.body) ? bound.body.filters : undefined;
|
|
196
|
+
const error = filterOperatorError(tool, bound.query?.filters) ?? filterOperatorError(tool, bodyFilters);
|
|
197
|
+
if (error)
|
|
198
|
+
throw new PlatformApiError(error, 400);
|
|
199
|
+
}
|
|
192
200
|
function executePath(prefix, application, microservice, routeParams) {
|
|
193
201
|
const routeSuffix = routeParams && routeParams.length > 0
|
|
194
202
|
? `/${routeParams.map(encodeURIComponent).join('/')}`
|
|
@@ -406,14 +414,20 @@ export function registerMicroserviceTools(server, platform) {
|
|
|
406
414
|
}, handle(config, ({ project, environment, id }) => platform.tenant('POST', project, `/microservices/${id}/clone`, { environment })));
|
|
407
415
|
server.registerTool('microservice_execute', {
|
|
408
416
|
title: 'Microservice çalıştır',
|
|
409
|
-
description: "Kayıtlı microservice'i gerçek runtime yolundan çalıştırır (POST /{project}/m/{application}/{microservice SLUG'ı}). Dönen httpStatus microservice'in kendi sonucudur (hata da geçerli çıktıdır); beklenmedik sonuçta microservice_debug ile adım adım inceleyin. Anonim erişim testi için anonymous: true. Yanıt 'headers'
|
|
410
|
-
inputSchema:
|
|
417
|
+
description: "Kayıtlı microservice'i gerçek runtime yolundan çalıştırır (POST /{project}/m/{application}/{microservice SLUG'ı}). Dönen httpStatus microservice'in kendi sonucudur (hata da geçerli çıktıdır); beklenmedik sonuçta microservice_debug ile adım adım inceleyin. Anonim erişim testi için anonymous: true. Yanıt 'headers' alanı VARSAYILAN OLARAK yalnız iş başlıklarını taşır (küçük harf): content-type, content-length, content-disposition, location, total-count (sayfalı tablo akışlarının okuduğu sözleşme), x-request-id, traceparent. Ucun response adımında kendi yazdığı başlıkları ya da sunucu başlıklarını görmek için includeHeaders:'all'; hiç başlık istemiyorsanız 'none'. set-cookie/authorization her kipte elenir.",
|
|
418
|
+
inputSchema: {
|
|
419
|
+
...executeInput,
|
|
420
|
+
includeHeaders: z.enum(['none', 'essential', 'all']).optional().describe("Yanıttaki 'headers' alanı. essential (varsayılan): content-type, content-length, content-disposition, location, total-count, x-request-id, traceparent. all: platformun döndüğü bütün başlıklar (gizliler hariç) — ucun kendi yazdığı başlığı doğrulamak için. none: başlık yok")
|
|
421
|
+
},
|
|
411
422
|
annotations: writes
|
|
412
|
-
}, handle(config, async ({ project, environment, application, microservice, method, routeParams, query, body, parameters, headers, anonymous, asUser, cookies, language }) => {
|
|
423
|
+
}, handle(config, async ({ project, environment, application, microservice, method, routeParams, query, body, parameters, headers, anonymous, asUser, cookies, language, includeHeaders }) => {
|
|
413
424
|
const notes = [];
|
|
414
|
-
const
|
|
425
|
+
const member = anonymous === true ? asUser : await resolveIdentity(asUser, platform, project, environment);
|
|
426
|
+
const requestHeaders = { ...runIdentity(anonymous, member, cookies, headers), ...languageHeader(language, notes) };
|
|
415
427
|
const bound = bindCallParameters(method, parameters, query, body, notes);
|
|
416
|
-
|
|
428
|
+
assertFilterOperators('microservice_execute', bound);
|
|
429
|
+
const raw = await platform.tenantRaw(method, project, executePath('m', application, microservice, routeParams), { environment, query: bound.query, body: bound.body, headers: requestHeaders, anonymous, mirrorAuthorizationCookie: true });
|
|
430
|
+
return withNormalization({ ...raw, headers: headersForMode(raw.headers, includeHeaders ?? 'essential') }, notes);
|
|
417
431
|
}));
|
|
418
432
|
server.registerTool('microservice_debug', {
|
|
419
433
|
title: 'Microservice debug',
|
|
@@ -422,8 +436,10 @@ export function registerMicroserviceTools(server, platform) {
|
|
|
422
436
|
annotations: writes
|
|
423
437
|
}, handle(config, async ({ project, environment, application, microservice, method, routeParams, query, body, parameters, headers, anonymous, asUser, cookies, language }) => {
|
|
424
438
|
const notes = [];
|
|
425
|
-
const
|
|
439
|
+
const member = anonymous === true ? asUser : await resolveIdentity(asUser, platform, project, environment);
|
|
440
|
+
const requestHeaders = { ...runIdentity(anonymous, member, cookies, headers), ...languageHeader(language, notes) };
|
|
426
441
|
const bound = bindCallParameters(method, parameters, query, body, notes);
|
|
442
|
+
assertFilterOperators('microservice_debug', bound);
|
|
427
443
|
const payload = await platform.tenantRaw(method, project, executePath('debug', application, microservice, routeParams), { environment, query: bound.query, body: bound.body, headers: requestHeaders, anonymous, mirrorAuthorizationCookie: true });
|
|
428
444
|
const unbound = unboundQueryParameters(payload.body);
|
|
429
445
|
return withNormalization({
|
|
@@ -474,7 +490,7 @@ export function registerMicroserviceTools(server, platform) {
|
|
|
474
490
|
const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'microservice', definition, { operation: 'preview' }, policyOverride));
|
|
475
491
|
// Önizleme de ucun middleware'lerini koşturur: kimlik verilmezse korumalı uç 401 döner ve kuru
|
|
476
492
|
// koşu aracı tam gerektiği anda kullanılamaz hâle gelir (09-05 koşusu S-21).
|
|
477
|
-
const payload = await platform.tenantRaw('POST', project, `/microservices/${resolvedApplicationId}/preview`, { environment, body: definition, headers: { ...identityHeaders(asUser, cookies, undefined), ...languageHeader(language, normalized) }, mirrorAuthorizationCookie: true });
|
|
493
|
+
const payload = await platform.tenantRaw('POST', project, `/microservices/${resolvedApplicationId}/preview`, { environment, body: definition, headers: { ...identityHeaders(await resolveIdentity(asUser, platform, project, environment), cookies, undefined), ...languageHeader(language, normalized) }, mirrorAuthorizationCookie: true });
|
|
478
494
|
return attachFindings(withResolved(withNormalization(payload, normalized), [...resolver.resolved, ...hydrated]), findings);
|
|
479
495
|
}));
|
|
480
496
|
server.registerTool('microservice_job_history', {
|