@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.
- package/.env.example +2 -2
- package/.env.production.example +9 -1
- package/README.md +14 -38
- package/dist/config.js +36 -3
- package/dist/knowledge/backendConstraints.js +982 -0
- package/dist/knowledge/componentCatalog.js +549 -92
- package/dist/knowledge/componentEvents.js +120 -25
- package/dist/knowledge/coverage.js +43 -0
- package/dist/knowledge/flowGuards.js +30 -0
- package/dist/knowledge/functionGuards.js +134 -0
- package/dist/knowledge/guideSections.js +49 -0
- package/dist/knowledge/hydrate.js +97 -0
- package/dist/knowledge/languageCodes.js +36 -0
- package/dist/knowledge/languages.js +105 -0
- package/dist/knowledge/layoutSummary.js +10 -0
- package/dist/knowledge/localization.js +51 -0
- package/dist/knowledge/normalize.js +500 -16
- package/dist/knowledge/outlines.js +167 -12
- package/dist/knowledge/policy.js +14 -5
- package/dist/knowledge/propTypes.js +80 -0
- package/dist/knowledge/rules.js +961 -44
- package/dist/knowledge/schemaGuard.js +199 -0
- package/dist/knowledge/styleResolve.js +63 -4
- package/dist/observability/trace.js +27 -3
- package/dist/platform/csv.js +123 -0
- package/dist/platform/errors.js +107 -6
- package/dist/platform/http.js +34 -4
- package/dist/platform/patch.js +27 -0
- package/dist/platform/refs.js +104 -0
- package/dist/platform/responses.js +25 -6
- package/dist/platform/screenshot.js +544 -16
- package/dist/platform/secrets.js +40 -0
- package/dist/platform/tokenVault.js +89 -0
- package/dist/platform/writeDiff.js +281 -0
- package/dist/resources/examples/flow-call-function.json +66 -0
- package/dist/resources/examples/function-total.json +70 -0
- package/dist/resources/examples/layout-app-shell.json +1 -1
- package/dist/resources/examples/microfunction-node-sum.json +56 -0
- package/dist/resources/examples/microservice-table-list.json +2 -4
- package/dist/resources/examples/page-dynamic-form.json +3 -3
- package/dist/resources/examples/page-flex-sections.json +99 -0
- package/dist/resources/examples/page-sign-in.json +4 -3
- package/dist/resources/guides/component.md +81 -8
- package/dist/resources/guides/entity.md +712 -20
- package/dist/resources/guides/expressions.md +47 -2
- package/dist/resources/guides/external-component.md +9 -0
- package/dist/resources/guides/functions.md +262 -0
- package/dist/resources/guides/getting-started.md +82 -7
- package/dist/resources/guides/layout.md +53 -10
- package/dist/resources/guides/merge.md +231 -0
- package/dist/resources/guides/microfunction.md +166 -0
- package/dist/resources/guides/microservice.md +287 -15
- package/dist/resources/guides/middleware.md +89 -1
- package/dist/resources/guides/modify-existing.md +23 -0
- package/dist/resources/guides/page.md +402 -49
- package/dist/resources/guides/translation.md +150 -8
- package/dist/resources/guides/view.md +16 -1
- package/dist/resources/guides/workflow.md +199 -28
- package/dist/resources/knowledge.js +5 -0
- package/dist/resources/rules.js +27 -0
- package/dist/resources/schema-fingerprint.json +8 -0
- package/dist/resources/schemas/AnalyzeImportQuery.json +51 -0
- package/dist/resources/schemas/ApplyMergePlanCommand.json +19 -0
- package/dist/resources/schemas/CreateBulkRowCommand.json +36 -0
- package/dist/resources/schemas/CreateDatasourceCommand.json +15 -6
- package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +19 -8
- package/dist/resources/schemas/CreateEntityCommand.json +52 -12
- package/dist/resources/schemas/CreateEnvironmentCommand.json +31 -0
- package/dist/resources/schemas/CreateGlobalFunctionCommand.json +95 -0
- package/dist/resources/schemas/CreateImportPlanCommand.json +278 -0
- package/dist/resources/schemas/CreateMergePlanCommand.json +34 -0
- package/dist/resources/schemas/CreateMicrofunctionCommand.json +117 -0
- package/dist/resources/schemas/CreateMicrofunctionRuntimeCommand.json +51 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +145 -3
- package/dist/resources/schemas/CreateQueryCommand.json +56 -3
- package/dist/resources/schemas/CreateRowCommand.json +38 -0
- package/dist/resources/schemas/CreateViewCommand.json +13 -10
- package/dist/resources/schemas/CreateWorkflowCommand.json +106 -14
- package/dist/resources/schemas/DataJobDto.json +267 -0
- package/dist/resources/schemas/ExportRowsCommand.json +172 -0
- package/dist/resources/schemas/Filter.json +3 -1
- package/dist/resources/schemas/GetRowsQuery.json +6 -1
- package/dist/resources/schemas/GlobalFunctionDto.json +89 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +249 -0
- package/dist/resources/schemas/ImportAnalysisDto.json +401 -0
- package/dist/resources/schemas/ImportPlanDto.json +260 -0
- package/dist/resources/schemas/ImportSampleDto.json +90 -0
- package/dist/resources/schemas/InvokeMicrofunctionCommand.json +25 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +135 -1
- package/dist/resources/schemas/PreviewQueryCommand.json +5 -2
- package/dist/resources/schemas/PreviewWorkflowCommand.json +45 -9
- package/dist/resources/schemas/ResolveMergeConflictsCommand.json +58 -0
- package/dist/resources/schemas/RevertMergePlanCommand.json +19 -0
- package/dist/resources/schemas/RunImportCommand.json +62 -0
- package/dist/resources/schemas/StepDto.json +34 -3
- package/dist/resources/schemas/UpdateBulkRowCommand.json +36 -0
- package/dist/resources/schemas/UpdateDatasourceCommand.json +15 -6
- package/dist/resources/schemas/UpdateEntityCommand.json +52 -11
- package/dist/resources/schemas/UpdateGlobalFunctionCommand.json +66 -0
- package/dist/resources/schemas/UpdateMicrofunctionCommand.json +121 -0
- package/dist/resources/schemas/UpdateMicrofunctionRuntimeCommand.json +54 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +147 -4
- package/dist/resources/schemas/UpdateQueryCommand.json +13 -3
- package/dist/resources/schemas/UpdateRowCommand.json +40 -0
- package/dist/resources/schemas/UpdateViewCommand.json +15 -10
- package/dist/resources/schemas/UpdateWorkflowCommand.json +106 -14
- package/dist/server.js +17 -1
- package/dist/tools/access.js +3 -3
- package/dist/tools/activities.js +40 -0
- package/dist/tools/applications.js +1 -1
- package/dist/tools/automations.js +76 -34
- package/dist/tools/common.js +55 -0
- package/dist/tools/components.js +16 -7
- package/dist/tools/datasources.js +83 -21
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +34 -2
- package/dist/tools/dtos.js +25 -12
- package/dist/tools/entities.js +113 -28
- package/dist/tools/environments.js +52 -0
- package/dist/tools/externalComponents.js +9 -3
- package/dist/tools/files.js +57 -42
- package/dist/tools/flows.js +205 -13
- package/dist/tools/folders.js +7 -2
- package/dist/tools/functions.js +331 -0
- package/dist/tools/globalFunctions.js +63 -0
- package/dist/tools/importSchemas.js +79 -0
- package/dist/tools/imports.js +241 -0
- package/dist/tools/layouts.js +103 -29
- package/dist/tools/merges.js +113 -0
- package/dist/tools/microfunctions.js +155 -0
- package/dist/tools/microservices.js +270 -40
- package/dist/tools/organizations.js +5 -6
- package/dist/tools/pages.js +421 -104
- package/dist/tools/platformInfo.js +155 -11
- package/dist/tools/projects.js +183 -33
- package/dist/tools/queries.js +32 -10
- package/dist/tools/revisions.js +94 -0
- package/dist/tools/rows.js +484 -50
- package/dist/tools/screenWrite.js +68 -0
- package/dist/tools/screens.js +28 -0
- package/dist/tools/templates.js +32 -5
- package/dist/tools/translations.js +24 -8
- package/dist/tools/views.js +20 -10
- package/dist/tools/workflows.js +56 -21
- package/package.json +7 -2
package/dist/platform/http.js
CHANGED
|
@@ -5,6 +5,17 @@ import { TokenProvider } from './token.js';
|
|
|
5
5
|
import { randomUUID } from 'node:crypto';
|
|
6
6
|
import { request } from 'undici';
|
|
7
7
|
export const requestAuthStorage = new AsyncLocalStorage();
|
|
8
|
+
const hiddenResponseHeaders = new Set(['set-cookie', 'authorization', 'proxy-authorization']);
|
|
9
|
+
/**
|
|
10
|
+
* Sayfalı tablo akışları `Total-Count` header'ına bağlı ama execute ailesi yalnız gövdeyi
|
|
11
|
+
* döndürüyordu; sözleşme hiçbir tool'dan doğrulanamıyordu (İK koşusu M-8). Gizli header'lar
|
|
12
|
+
* her koşulda elenir.
|
|
13
|
+
*/
|
|
14
|
+
export function responseHeadersForTool(raw) {
|
|
15
|
+
return Object.fromEntries(Object.entries(raw)
|
|
16
|
+
.map(([name, value]) => [name.toLowerCase(), Array.isArray(value) ? value.join(', ') : value])
|
|
17
|
+
.filter((entry) => typeof entry[1] === 'string' && !hiddenResponseHeaders.has(entry[0])));
|
|
18
|
+
}
|
|
8
19
|
function buildMultipart(parts, boundary) {
|
|
9
20
|
const chunks = [];
|
|
10
21
|
for (const part of parts) {
|
|
@@ -70,6 +81,18 @@ export class Platform {
|
|
|
70
81
|
});
|
|
71
82
|
return outcome.body;
|
|
72
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* `tenant` gibi davranır (4xx/5xx atar) ama yanıt başlıklarını da verir. `tenantRaw` bu iş için
|
|
86
|
+
* kullanılamaz: o hata durumunda atmaz, yani satır yazma yolunda 400'ü başarı gibi döndürürdü.
|
|
87
|
+
* Satır araçları başlığa bakmak zorunda — `X-Unmapped-Members` platformun `localizations`
|
|
88
|
+
* alanını tanımadığını söyleyen tek işaret (sözleşme D6, gateway eski platformda companion'a düşer).
|
|
89
|
+
*/
|
|
90
|
+
async tenantWithHeaders(method, project, path, options = {}) {
|
|
91
|
+
return this.send('tenant', method, `/${this.resolveProject(project)}${path}`, {
|
|
92
|
+
...options,
|
|
93
|
+
environment: this.resolveEnvironment(options.environment)
|
|
94
|
+
});
|
|
95
|
+
}
|
|
73
96
|
async tenantRaw(method, project, path, options = {}) {
|
|
74
97
|
return this.send('tenant', method, `/${this.resolveProject(project)}${path}`, {
|
|
75
98
|
...options,
|
|
@@ -95,12 +118,17 @@ export class Platform {
|
|
|
95
118
|
const url = new URL(`${baseUrl}${path}`);
|
|
96
119
|
appendQuery(url, options.query);
|
|
97
120
|
const callerHeaders = Object.fromEntries(Object.entries(options.headers ?? {}).map(([name, value]) => [name.toLowerCase(), value]));
|
|
121
|
+
const authorization = callerHeaders.authorization ?? (options.anonymous ? undefined : await this.authorizationHeader());
|
|
98
122
|
const headers = {
|
|
99
123
|
host: `${this.config.organization}.${this.config.hostSuffix}`,
|
|
100
124
|
accept: 'application/json',
|
|
101
|
-
...(
|
|
125
|
+
...(authorization === undefined ? {} : { authorization }),
|
|
102
126
|
...(options.environment ? { 'x-environment': options.environment } : {}),
|
|
103
|
-
...callerHeaders
|
|
127
|
+
...callerHeaders,
|
|
128
|
+
'x-client': 'mcp',
|
|
129
|
+
...(options.mirrorAuthorizationCookie && authorization !== undefined && callerHeaders.cookie === undefined
|
|
130
|
+
? { cookie: `NG_AccessToken=${authorization.replace(/^Bearer /, '')}` }
|
|
131
|
+
: {})
|
|
104
132
|
};
|
|
105
133
|
let body;
|
|
106
134
|
if (options.multipart) {
|
|
@@ -127,6 +155,7 @@ export class Platform {
|
|
|
127
155
|
let statusCode;
|
|
128
156
|
let payload;
|
|
129
157
|
let responseBytes = 0;
|
|
158
|
+
let responseHeaders = {};
|
|
130
159
|
try {
|
|
131
160
|
const response = await request(url, {
|
|
132
161
|
method: method,
|
|
@@ -135,6 +164,7 @@ export class Platform {
|
|
|
135
164
|
signal: AbortSignal.timeout(this.config.requestTimeoutMs)
|
|
136
165
|
});
|
|
137
166
|
statusCode = response.statusCode;
|
|
167
|
+
responseHeaders = responseHeadersForTool(response.headers);
|
|
138
168
|
if (options.expectBinary && statusCode < 400) {
|
|
139
169
|
const bytes = Buffer.from(await response.body.arrayBuffer());
|
|
140
170
|
responseBytes = bytes.byteLength;
|
|
@@ -164,9 +194,9 @@ export class Platform {
|
|
|
164
194
|
return this.send(api, method, path, options, raw, true);
|
|
165
195
|
}
|
|
166
196
|
if (raw)
|
|
167
|
-
return { httpStatus: statusCode, body: payload ?? null };
|
|
197
|
+
return { httpStatus: statusCode, body: payload ?? null, headers: responseHeaders };
|
|
168
198
|
if (statusCode >= 400)
|
|
169
199
|
throw mapPlatformError(statusCode, payload, context);
|
|
170
|
-
return { httpStatus: statusCode, body: payload ?? { success: true } };
|
|
200
|
+
return { httpStatus: statusCode, body: payload ?? { success: true }, headers: responseHeaders };
|
|
171
201
|
}
|
|
172
202
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function patchResult(response, fallback) {
|
|
2
|
+
const envelope = response && typeof response === 'object'
|
|
3
|
+
? (response.data ?? response)
|
|
4
|
+
: {};
|
|
5
|
+
const written = envelope.data && typeof envelope.data === 'object'
|
|
6
|
+
? envelope.data
|
|
7
|
+
: fallback;
|
|
8
|
+
return {
|
|
9
|
+
// Sayı olarak gelen bir newVersion JSON.parse sırasında ZATEN yuvarlanmıştır
|
|
10
|
+
// (6.4e16 > MAX_SAFE_INTEGER): geri gönderilirse her patch 409 verir, boş bırakmak dürüst.
|
|
11
|
+
newVersion: typeof envelope.newVersion === 'string' ? envelope.newVersion : '',
|
|
12
|
+
applied: typeof envelope.applied === 'number' ? envelope.applied : 0,
|
|
13
|
+
data: written
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* `baseVersion` PATCH gövdesinde ZORUNLU. Okunan kayıtta `version` yoksa platform 422 döndürür;
|
|
18
|
+
* ajanın hatayı gateway'e değil eksik alana bağlayabilmesi için burada erken ve açık patlıyoruz.
|
|
19
|
+
*/
|
|
20
|
+
export function baseVersionOf(definition, readTool) {
|
|
21
|
+
const version = definition.version;
|
|
22
|
+
if (typeof version === 'string' && version.length > 0)
|
|
23
|
+
return version;
|
|
24
|
+
if (typeof version === 'number')
|
|
25
|
+
return String(version);
|
|
26
|
+
throw new Error(`Okunan kayıtta 'version' alanı yok; PATCH 'baseVersion' olmadan gönderilemez. ${readTool} çıktısında 'version' görünmüyorsa platform sürümü bu ucu desteklemiyordur.`);
|
|
27
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { PlatformApiError } from './errors.js';
|
|
2
|
+
const guidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3
|
+
export function isGuid(value) {
|
|
4
|
+
return guidPattern.test(value);
|
|
5
|
+
}
|
|
6
|
+
export class RefResolveError extends PlatformApiError {
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Slug → id çözümü çağrı başına yapılır ve çağrılar arasında TAŞINMAZ: gateway stateless, N
|
|
10
|
+
* replika ile paylaşılan bir önbellek tutarsız olur. Belirsiz slug (aynı ad iki datasource'ta)
|
|
11
|
+
* sessizce "ilkini al" ile geçilmez — bu planın var oluş nedeni sessiz yanlışı azaltmaktı;
|
|
12
|
+
* adaylar listelenip `datasource/entity` biçimi istenir.
|
|
13
|
+
*/
|
|
14
|
+
export class RefResolver {
|
|
15
|
+
platform;
|
|
16
|
+
project;
|
|
17
|
+
environment;
|
|
18
|
+
resolved = [];
|
|
19
|
+
tree;
|
|
20
|
+
fieldsByEntity = new Map();
|
|
21
|
+
constructor(platform, project, environment) {
|
|
22
|
+
this.platform = platform;
|
|
23
|
+
this.project = project;
|
|
24
|
+
this.environment = environment;
|
|
25
|
+
}
|
|
26
|
+
async applicationId(ref) {
|
|
27
|
+
if (isGuid(ref))
|
|
28
|
+
return ref;
|
|
29
|
+
const projectPayload = await this.platform.org('GET', `/p/${this.platform.resolveProject(this.project)}`);
|
|
30
|
+
const applicationsPayload = await this.platform.org('GET', '/applications', { query: { projectId: String(projectPayload.data?.id ?? ''), pageLength: 100 } });
|
|
31
|
+
const applications = applicationsPayload.data ?? [];
|
|
32
|
+
const found = applications.find(application => application.slug === ref);
|
|
33
|
+
if (!found) {
|
|
34
|
+
throw new RefResolveError(`'${ref}' diye bir application yok. Mevcut slug'lar: ${applications.map(application => application.slug).join(', ') || '(yok)'}`);
|
|
35
|
+
}
|
|
36
|
+
this.resolved.push(`applicationId '${ref}' → ${found.id}`);
|
|
37
|
+
return found.id;
|
|
38
|
+
}
|
|
39
|
+
async entity(ref) {
|
|
40
|
+
const tree = await this.loadTree();
|
|
41
|
+
const flat = tree
|
|
42
|
+
.filter((datasource) => Boolean(datasource) && typeof datasource === 'object')
|
|
43
|
+
.flatMap(datasource => (Array.isArray(datasource.entities) ? datasource.entities : [])
|
|
44
|
+
.filter((entity) => Boolean(entity) && typeof entity === 'object' && typeof entity.id === 'string')
|
|
45
|
+
.map(entity => ({ datasource, entity })));
|
|
46
|
+
if (isGuid(ref)) {
|
|
47
|
+
const byId = flat.find(({ entity }) => entity.id.toLowerCase() === ref.toLowerCase());
|
|
48
|
+
if (!byId)
|
|
49
|
+
throw new RefResolveError(`'${ref}' id'li entity ağaçta yok.`);
|
|
50
|
+
return { entityId: byId.entity.id, datasourceId: byId.datasource.id, slug: byId.entity.slug };
|
|
51
|
+
}
|
|
52
|
+
const separator = ref.indexOf('/');
|
|
53
|
+
const [datasourcePart, entityPart] = separator === -1 ? [undefined, ref] : [ref.slice(0, separator), ref.slice(separator + 1)];
|
|
54
|
+
const matches = flat.filter(({ datasource, entity }) => entity.slug === entityPart && (datasourcePart === undefined || datasource.slug === datasourcePart || datasource.name === datasourcePart));
|
|
55
|
+
if (matches.length === 0) {
|
|
56
|
+
throw new RefResolveError(`'${ref}' diye bir entity yok. Mevcut slug'lar: ${flat.map(({ entity }) => entity.slug).join(', ') || '(yok)'}`);
|
|
57
|
+
}
|
|
58
|
+
if (matches.length > 1) {
|
|
59
|
+
throw new RefResolveError(`'${ref}' birden çok datasource'ta var: ${matches.map(({ datasource, entity }) => `${datasource.slug}/${entity.slug}`).join(', ')}. 'datasource/entity' biçimiyle verin.`);
|
|
60
|
+
}
|
|
61
|
+
const [{ datasource, entity }] = matches;
|
|
62
|
+
this.resolved.push(`entityId '${ref}' → ${entity.id}`);
|
|
63
|
+
return { entityId: entity.id, datasourceId: datasource.id, slug: entity.slug };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Entity'nin ham kaydı (alan listesi DIŞINDAKİ kök alanlar için: `localizationEntityId`,
|
|
67
|
+
* `localizationParentId`, `type`). `entityFields` ile aynı ucu çağırır ve aynı önbelleği
|
|
68
|
+
* kullanır — iki tur atmaz.
|
|
69
|
+
*/
|
|
70
|
+
async entityRecord(ref) {
|
|
71
|
+
const cached = this.recordByEntity.get(ref.entityId);
|
|
72
|
+
if (cached)
|
|
73
|
+
return cached;
|
|
74
|
+
const payload = await this.platform.tenant('GET', this.project, `/entities/${ref.datasourceId}/${ref.entityId}`, { environment: this.environment });
|
|
75
|
+
const record = payload.data ?? {};
|
|
76
|
+
this.recordByEntity.set(ref.entityId, record);
|
|
77
|
+
return record;
|
|
78
|
+
}
|
|
79
|
+
async entityFields(ref) {
|
|
80
|
+
const cached = this.fieldsByEntity.get(ref.entityId);
|
|
81
|
+
if (cached)
|
|
82
|
+
return cached;
|
|
83
|
+
const payload = await this.platform.tenant('GET', this.project, `/entities/${ref.datasourceId}/${ref.entityId}`, { environment: this.environment });
|
|
84
|
+
const fields = (payload.data?.fields ?? [])
|
|
85
|
+
.filter(field => typeof field?.id === 'string' && field.id !== '' && typeof field.slug === 'string' && field.slug !== '')
|
|
86
|
+
.map(field => ({
|
|
87
|
+
id: String(field.id),
|
|
88
|
+
slug: String(field.slug),
|
|
89
|
+
relationType: String(field.relationType ?? 'none'),
|
|
90
|
+
relationId: field.relationId ?? null,
|
|
91
|
+
primaryKey: field.primaryKey === true
|
|
92
|
+
}));
|
|
93
|
+
this.fieldsByEntity.set(ref.entityId, fields);
|
|
94
|
+
return fields;
|
|
95
|
+
}
|
|
96
|
+
recordByEntity = new Map();
|
|
97
|
+
async loadTree() {
|
|
98
|
+
if (this.tree)
|
|
99
|
+
return this.tree;
|
|
100
|
+
const payload = await this.platform.tenant('GET', this.project, '/datasources/tree', { environment: this.environment });
|
|
101
|
+
this.tree = Array.isArray(payload) ? payload : (payload?.data ?? []);
|
|
102
|
+
return this.tree;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { logError, logStep } from '../observability/trace.js';
|
|
2
2
|
import { PlatformApiError } from './errors.js';
|
|
3
|
+
import { PolicyBlockError } from '../knowledge/policy.js';
|
|
4
|
+
import { maskSecrets } from './secrets.js';
|
|
3
5
|
export function prune(value) {
|
|
4
6
|
if (Array.isArray(value))
|
|
5
7
|
return value.map(prune);
|
|
@@ -125,13 +127,20 @@ function asStructured(payload) {
|
|
|
125
127
|
return payload;
|
|
126
128
|
return { data: payload };
|
|
127
129
|
}
|
|
128
|
-
export function toolResult(payload, config) {
|
|
130
|
+
export function toolResult(payload, config, expectFailure) {
|
|
129
131
|
const pruned = prune(payload);
|
|
130
|
-
const
|
|
132
|
+
const { payload: safe, masked, tokenRefs } = maskSecrets(pruned);
|
|
133
|
+
const runtimeErrors = runtimeErrorSummary(safe);
|
|
131
134
|
if (runtimeErrors.length > 0) {
|
|
132
|
-
|
|
135
|
+
// Kasıtlı negatif test (expectFailure:true) bir kusur değil bir KANIT: çağıran beklediğini
|
|
136
|
+
// söylediyse SONUÇ satırı 'başarılı' yazılır ve ölçüm aracının plansız hata oranını şişirmez
|
|
137
|
+
// (pages.ts'teki page_screenshot expectFailure deseniyle aynı; artık her handle()'lı tool'da geçerli).
|
|
138
|
+
logStep('akış hatası ✕', `${runtimeErrors.length} adım hata ile bitti — platform HTTP 200 döndürdüğü için çağrı başarılı görünüyordu${expectFailure === true ? ' (expectFailure:true — kasıtlı negatif test)' : ''}`, runtimeErrors);
|
|
133
139
|
}
|
|
134
|
-
|
|
140
|
+
if (masked.length > 0) {
|
|
141
|
+
logStep('maskeleme', `${masked.length} gizli alan yanıttan çıkarıldı — değer *** ile değiştirildi, yolu gatewayMasked'de`, masked);
|
|
142
|
+
}
|
|
143
|
+
const { payload: bounded, truncated } = shrink(safe, config.maxResponseBytes);
|
|
135
144
|
const structured = asStructured(bounded);
|
|
136
145
|
if (truncated) {
|
|
137
146
|
logStep('kırpma ✂', `yanıt ${config.maxResponseBytes} bayt sınırını aştı — en uzun liste yarıya indirilerek küçültüldü (MAX_RESPONSE_BYTES)`);
|
|
@@ -139,6 +148,9 @@ export function toolResult(payload, config) {
|
|
|
139
148
|
const enriched = {
|
|
140
149
|
...structured,
|
|
141
150
|
...(runtimeErrors.length > 0 ? { runtimeErrors } : {}),
|
|
151
|
+
...(runtimeErrors.length > 0 && expectFailure === true ? { expectedFailure: true } : {}),
|
|
152
|
+
...(masked.length > 0 ? { gatewayMasked: masked } : {}),
|
|
153
|
+
...(Object.keys(tokenRefs).length > 0 ? { tokenRefs, tokenRefNote: "Maskelenen JWT'ler gateway içinde tutuldu; bu referansları page_screenshot / page_runtime_get 'asUser' alanına verebilirsiniz (15 dk geçerli). Jetonun kendisi yanıta ve günlüğe hiç girmez." } : {}),
|
|
142
154
|
...(truncated ? { truncated: true, truncationNote: 'Liste, yanıt boyutu sınırı nedeniyle kırpıldı; sayfalama ya da daha dar filtre kullanın.' } : {})
|
|
143
155
|
};
|
|
144
156
|
return {
|
|
@@ -148,7 +160,8 @@ export function toolResult(payload, config) {
|
|
|
148
160
|
}
|
|
149
161
|
export function toolError(error) {
|
|
150
162
|
logError(error);
|
|
151
|
-
|
|
163
|
+
// Politika durdurması bilinçli bir karardır, "beklenmeyen" değil (33/S-5).
|
|
164
|
+
const message = error instanceof PlatformApiError || error instanceof PolicyBlockError
|
|
152
165
|
? error.message
|
|
153
166
|
: error instanceof Error
|
|
154
167
|
? `Beklenmeyen gateway hatası: ${error.message}`
|
|
@@ -161,7 +174,13 @@ export function toolError(error) {
|
|
|
161
174
|
export function handle(config, run) {
|
|
162
175
|
return async (args) => {
|
|
163
176
|
try {
|
|
164
|
-
|
|
177
|
+
// Tool'un kendi input şeması `expectFailure: z.boolean().optional()` taşıyorsa (ör.
|
|
178
|
+
// microservice_execute, automation_preview) burada otomatik okunur ve toolResult'a geçirilir
|
|
179
|
+
// — kasıtlı negatif testler (yanlış parola, yetkisiz erişim, iş kuralı ihlali) ölçüm
|
|
180
|
+
// aracının plansız hata oranını şişirmez. Şemasında bu alan olmayan tool'larda args'ta
|
|
181
|
+
// anahtar hiç bulunmaz, davranış değişmez.
|
|
182
|
+
const expectFailure = args?.expectFailure === true;
|
|
183
|
+
return toolResult(await run(args), config, expectFailure);
|
|
165
184
|
}
|
|
166
185
|
catch (error) {
|
|
167
186
|
return toolError(error);
|