@appaflytech/zeyrek-ai-mcp 0.0.43 → 0.0.45
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/dist/knowledge/backendConstraints.js +86 -2
- package/dist/knowledge/componentCatalog.js +63 -26
- package/dist/knowledge/componentEvents.js +6 -0
- package/dist/knowledge/connectDataBind.js +43 -6
- package/dist/knowledge/connectDataDefinition.js +74 -3
- package/dist/knowledge/connectDataRules.js +14 -0
- package/dist/knowledge/mobileComponentCatalog.js +66 -7
- package/dist/knowledge/mobileStore.js +61 -2
- package/dist/knowledge/platform/mobile.js +1 -1
- package/dist/knowledge/platform/mobileRules.js +2 -1
- package/dist/knowledge/platform/mobileTabs.js +31 -2
- package/dist/knowledge/platform/web.js +1 -1
- package/dist/knowledge/rules.js +195 -4
- package/dist/knowledge/textHooks.js +434 -0
- package/dist/platform/capture/capture.js +1 -1
- package/dist/platform/capture/readiness.js +4 -0
- package/dist/platform/errors.js +22 -2
- package/dist/prompts/mobilePublish.js +21 -0
- package/dist/resources/examples/flow-biometric-confirm.json +199 -0
- package/dist/resources/examples/microfunction-node-dependencies.json +19 -0
- package/dist/resources/guides/access.md +3 -3
- package/dist/resources/guides/component.md +20 -4
- package/dist/resources/guides/deployment.md +98 -0
- package/dist/resources/guides/entity.md +1 -1
- package/dist/resources/guides/expressions.md +5 -1
- package/dist/resources/guides/external-component.md +6 -4
- package/dist/resources/guides/merge.md +81 -4
- package/dist/resources/guides/microfunction.md +104 -9
- package/dist/resources/guides/microservice.md +34 -5
- package/dist/resources/guides/mobile-connect-data.md +70 -0
- package/dist/resources/guides/mobile-flow.md +64 -0
- package/dist/resources/guides/mobile-publish.md +161 -16
- package/dist/resources/guides/mobile-steps.md +17 -0
- package/dist/resources/guides/mobile.md +41 -23
- package/dist/resources/guides/page.md +11 -5
- package/dist/resources/guides/translation.md +95 -0
- package/dist/resources/knowledge.js +3 -1
- package/dist/resources/prompts/mobile-publish-prepare.md +89 -0
- package/dist/resources/schema-fingerprint.json +4 -4
- package/dist/resources/schemas/ApplyMicrofunctionArchiveChangesCommand.json +76 -0
- package/dist/resources/schemas/ConnectDataDescribeDto.json +44 -0
- package/dist/resources/schemas/ConnectDataResultDto.json +10 -0
- package/dist/resources/schemas/CreateMicrofunctionCommand.json +8 -0
- package/dist/resources/schemas/InvokeMicrofunctionCommand.json +0 -4
- package/dist/resources/schemas/MobileStorePreflightScope.json +13 -0
- package/dist/resources/schemas/StartMobileBuildCommand.json +35 -1
- package/dist/resources/schemas/UpdateMicrofunctionCommand.json +8 -0
- package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +75 -0
- package/dist/server.js +2 -0
- package/dist/tools/common.js +6 -0
- package/dist/tools/components.js +19 -6
- package/dist/tools/connectData.js +9 -4
- package/dist/tools/deployments.js +1 -1
- package/dist/tools/designSchemaPatch.js +16 -4
- package/dist/tools/environments.js +3 -3
- package/dist/tools/files.js +1 -1
- package/dist/tools/flows.js +29 -3
- package/dist/tools/layouts.js +32 -12
- package/dist/tools/merges.js +8 -8
- package/dist/tools/microfunctions.js +53 -3
- package/dist/tools/mobileStore.js +83 -21
- package/dist/tools/pages.js +59 -24
- package/dist/tools/platformInfo.js +4 -2
- package/dist/tools/textHookGuard.js +116 -0
- package/package.json +2 -2
|
@@ -18,6 +18,21 @@
|
|
|
18
18
|
},
|
|
19
19
|
"runnerScope": {
|
|
20
20
|
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileBuilds.MobileRunnerScope"
|
|
21
|
+
},
|
|
22
|
+
"packageType": {
|
|
23
|
+
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileBuilds.MobilePackageType"
|
|
24
|
+
},
|
|
25
|
+
"releaseTrack": {
|
|
26
|
+
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileBuilds.MobilePlayTrack"
|
|
27
|
+
},
|
|
28
|
+
"runnerId": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"format": "uuid",
|
|
31
|
+
"nullable": true
|
|
32
|
+
},
|
|
33
|
+
"environment": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"nullable": true
|
|
21
36
|
}
|
|
22
37
|
},
|
|
23
38
|
"additionalProperties": false
|
|
@@ -36,7 +51,10 @@
|
|
|
36
51
|
"enum": [
|
|
37
52
|
"build",
|
|
38
53
|
"testflight",
|
|
39
|
-
"internal"
|
|
54
|
+
"internal",
|
|
55
|
+
"app-store-review",
|
|
56
|
+
"app-store-release",
|
|
57
|
+
"play-promote"
|
|
40
58
|
],
|
|
41
59
|
"type": "string",
|
|
42
60
|
"format": ""
|
|
@@ -48,6 +66,22 @@
|
|
|
48
66
|
],
|
|
49
67
|
"type": "string",
|
|
50
68
|
"format": ""
|
|
69
|
+
},
|
|
70
|
+
"NgApps.Domain.Contexts.Organization.MobileBuilds.MobilePackageType": {
|
|
71
|
+
"enum": [
|
|
72
|
+
"aab",
|
|
73
|
+
"apk"
|
|
74
|
+
],
|
|
75
|
+
"type": "string",
|
|
76
|
+
"format": ""
|
|
77
|
+
},
|
|
78
|
+
"NgApps.Domain.Contexts.Organization.MobileBuilds.MobilePlayTrack": {
|
|
79
|
+
"enum": [
|
|
80
|
+
"closed",
|
|
81
|
+
"production"
|
|
82
|
+
],
|
|
83
|
+
"type": "string",
|
|
84
|
+
"format": ""
|
|
51
85
|
}
|
|
52
86
|
}
|
|
53
87
|
}
|
|
@@ -98,6 +98,13 @@
|
|
|
98
98
|
},
|
|
99
99
|
"nullable": true
|
|
100
100
|
},
|
|
101
|
+
"notificationReminder": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"nullable": true
|
|
104
|
+
},
|
|
105
|
+
"releaseTargets": {
|
|
106
|
+
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreReleaseTargetsModel"
|
|
107
|
+
},
|
|
101
108
|
"listing": {
|
|
102
109
|
"type": "object",
|
|
103
110
|
"additionalProperties": {
|
|
@@ -120,6 +127,9 @@
|
|
|
120
127
|
"ads": {
|
|
121
128
|
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAdsModel"
|
|
122
129
|
},
|
|
130
|
+
"appLock": {
|
|
131
|
+
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAppLockModel"
|
|
132
|
+
},
|
|
123
133
|
"expectedUpdatedAt": {
|
|
124
134
|
"type": "string",
|
|
125
135
|
"format": "date-time",
|
|
@@ -130,6 +140,18 @@
|
|
|
130
140
|
},
|
|
131
141
|
"components": {
|
|
132
142
|
"schemas": {
|
|
143
|
+
"NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreReleaseTargetsModel": {
|
|
144
|
+
"type": "object",
|
|
145
|
+
"properties": {
|
|
146
|
+
"ios": {
|
|
147
|
+
"type": "boolean"
|
|
148
|
+
},
|
|
149
|
+
"android": {
|
|
150
|
+
"type": "boolean"
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
"additionalProperties": false
|
|
154
|
+
},
|
|
133
155
|
"NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreListingModel": {
|
|
134
156
|
"type": "object",
|
|
135
157
|
"properties": {
|
|
@@ -210,6 +232,29 @@
|
|
|
210
232
|
"copyright": {
|
|
211
233
|
"type": "string",
|
|
212
234
|
"nullable": true
|
|
235
|
+
},
|
|
236
|
+
"releaseType": {
|
|
237
|
+
"type": "string",
|
|
238
|
+
"nullable": true
|
|
239
|
+
},
|
|
240
|
+
"scheduledAt": {
|
|
241
|
+
"type": "string",
|
|
242
|
+
"format": "date-time",
|
|
243
|
+
"nullable": true
|
|
244
|
+
},
|
|
245
|
+
"phasedRelease": {
|
|
246
|
+
"type": "boolean"
|
|
247
|
+
},
|
|
248
|
+
"pricing": {
|
|
249
|
+
"type": "string",
|
|
250
|
+
"nullable": true
|
|
251
|
+
},
|
|
252
|
+
"territories": {
|
|
253
|
+
"type": "array",
|
|
254
|
+
"items": {
|
|
255
|
+
"type": "string"
|
|
256
|
+
},
|
|
257
|
+
"nullable": true
|
|
213
258
|
}
|
|
214
259
|
},
|
|
215
260
|
"additionalProperties": false
|
|
@@ -228,6 +273,23 @@
|
|
|
228
273
|
"releaseStatus": {
|
|
229
274
|
"type": "string",
|
|
230
275
|
"nullable": true
|
|
276
|
+
},
|
|
277
|
+
"contactPhone": {
|
|
278
|
+
"type": "string",
|
|
279
|
+
"nullable": true
|
|
280
|
+
},
|
|
281
|
+
"contactWebsite": {
|
|
282
|
+
"type": "string",
|
|
283
|
+
"nullable": true
|
|
284
|
+
},
|
|
285
|
+
"promoVideoUrl": {
|
|
286
|
+
"type": "string",
|
|
287
|
+
"nullable": true
|
|
288
|
+
},
|
|
289
|
+
"rolloutPercent": {
|
|
290
|
+
"type": "integer",
|
|
291
|
+
"format": "int32",
|
|
292
|
+
"nullable": true
|
|
231
293
|
}
|
|
232
294
|
},
|
|
233
295
|
"additionalProperties": false
|
|
@@ -292,6 +354,19 @@
|
|
|
292
354
|
},
|
|
293
355
|
"additionalProperties": false
|
|
294
356
|
},
|
|
357
|
+
"NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAppLockModel": {
|
|
358
|
+
"type": "object",
|
|
359
|
+
"properties": {
|
|
360
|
+
"enabled": {
|
|
361
|
+
"type": "boolean"
|
|
362
|
+
},
|
|
363
|
+
"timeoutSeconds": {
|
|
364
|
+
"type": "integer",
|
|
365
|
+
"format": "int32"
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
"additionalProperties": false
|
|
369
|
+
},
|
|
295
370
|
"NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAppleCategoriesModel": {
|
|
296
371
|
"type": "object",
|
|
297
372
|
"properties": {
|
package/dist/server.js
CHANGED
|
@@ -17,6 +17,7 @@ import { registerAccessTools } from "./tools/access.js";
|
|
|
17
17
|
import { registerTranslationTools } from "./tools/translations.js";
|
|
18
18
|
import { registerFlowTools } from "./tools/flows.js";
|
|
19
19
|
import { registerKnowledgeResources } from "./resources/knowledge.js";
|
|
20
|
+
import { registerMobilePublishPrompt } from "./prompts/mobilePublish.js";
|
|
20
21
|
import { registerSchemaResources } from "./resources/schemas.js";
|
|
21
22
|
import { registerRuleResources } from "./resources/rules.js";
|
|
22
23
|
import { instrumentServer } from "./observability/trace.js";
|
|
@@ -109,6 +110,7 @@ const moduleRegistrations = {
|
|
|
109
110
|
mobile: (server, platform) => {
|
|
110
111
|
registerMobileTools(server, platform);
|
|
111
112
|
registerMobileStoreTools(server, platform);
|
|
113
|
+
registerMobilePublishPrompt(server);
|
|
112
114
|
},
|
|
113
115
|
};
|
|
114
116
|
export function createGatewayServer(platform) {
|
package/dist/tools/common.js
CHANGED
|
@@ -67,6 +67,12 @@ export const pagingParams = {
|
|
|
67
67
|
* ayrılırsa gateway kabul ettiği bir adı görüntüleyemez hâle gelir.
|
|
68
68
|
*/
|
|
69
69
|
export const branchNamePattern = /^(?=.{3,31}$)[a-z][a-z0-9_-]*(\/[a-z0-9][a-z0-9_-]*)*$/;
|
|
70
|
+
/**
|
|
71
|
+
* Dal ve merge yazan sekiz tool'un açıklamasındaki ortak ön kontrol cümlesi (MRG-018; backend
|
|
72
|
+
* `CheckBranchWriteAllowedBehavior`, ADR 0022). Abonelik `project_get` yanıtında görünür, ajan
|
|
73
|
+
* isteği göndermeden eleyebilsin diye açıklamada durur.
|
|
74
|
+
*/
|
|
75
|
+
export const templateSubscriptionLock = "Şablondan kurulup güncellemeleri alan projede (project_get → templateMetadata.installedTemplateReceiveUpdates: true) 400 döner (MRG-018): o projede değişiklik yalnız şablondan gelir.";
|
|
70
76
|
const reservedEnvironmentNames = new Set(['development', 'production', 'public', 'information_schema', 'dev', 'view', 'api', 'files', 'hubs']);
|
|
71
77
|
/** Sabit ortamlar, url sözcükleri ve postgres şemaları: dal adı olamazlar */
|
|
72
78
|
export function isReservedEnvironmentName(name) {
|
package/dist/tools/components.js
CHANGED
|
@@ -12,6 +12,8 @@ import { applicationTypeField, resolveProfile } from '../knowledge/platform/reso
|
|
|
12
12
|
import { mobileComponentNotes } from '../knowledge/platform/mobileRules.js';
|
|
13
13
|
import { randomUUID } from 'node:crypto';
|
|
14
14
|
import { z } from 'zod';
|
|
15
|
+
import { enforceTextHooks, textHookOverrideParam } from './textHookGuard.js';
|
|
16
|
+
import { attachFindings } from '../knowledge/policy.js';
|
|
15
17
|
const typeParam = z.enum(['web', 'mobile']).optional().describe('Hedef platform (ComponentType); verilmezse uygulamanın tipi — mobil uygulamada mobile');
|
|
16
18
|
const kindParam = z.enum(['component', 'widget']).optional()
|
|
17
19
|
.describe("component: sayfaya konan tekil eleman. widget: kendi şeması olan kapsayıcı (container/modal/drawer)");
|
|
@@ -320,14 +322,16 @@ export function registerComponentTools(server, platform) {
|
|
|
320
322
|
isDroppable: z.boolean().optional(),
|
|
321
323
|
isSticky: z.boolean().optional(),
|
|
322
324
|
isDeletable: z.boolean().optional(),
|
|
323
|
-
isDefaultAutoHeight: z.boolean().optional()
|
|
325
|
+
isDefaultAutoHeight: z.boolean().optional(),
|
|
326
|
+
policyOverride: textHookOverrideParam
|
|
324
327
|
},
|
|
325
328
|
annotations: writes
|
|
326
|
-
}, handle(config, async ({ project, environment, applicationId, ...body }) => {
|
|
329
|
+
}, handle(config, async ({ project, environment, applicationId, policyOverride, ...body }) => {
|
|
327
330
|
if (body.kind === 'widget' && !body.widgetType) {
|
|
328
331
|
throw new PlatformApiError("kind:'widget' için widgetType (container | modal | drawer) zorunludur.");
|
|
329
332
|
}
|
|
330
333
|
assertNoRetiredNodes(body.schema);
|
|
334
|
+
const textFindings = await enforceTextHooks(config, { platform, project, environment, applicationId, after: body.schema ?? [], where: `widget '${body.name}'` }, policyOverride);
|
|
331
335
|
const wrapped = body.kind === 'widget' && body.widgetType && (body.schema ?? []).length > 0
|
|
332
336
|
? wrapWidgetRoot(body.schema ?? [], body.widgetType, await listComponents(platform, project, environment, applicationId).catch(() => []))
|
|
333
337
|
: { schema: body.schema, notes: [] };
|
|
@@ -348,7 +352,7 @@ export function registerComponentTools(server, platform) {
|
|
|
348
352
|
schema: [],
|
|
349
353
|
...Object.fromEntries(Object.entries(body).filter(([, value]) => value !== undefined))
|
|
350
354
|
}
|
|
351
|
-
}).then(response => withNormalization(response, [...wrapped.notes, ...componentDefaultsNote(fixed), ...blockSizeNote(catalog.clamped), ...heightTypeNote(catalog.heights), ...sizeStringNote(catalog.sizes)]));
|
|
355
|
+
}).then(response => attachFindings(withNormalization(response, [...wrapped.notes, ...componentDefaultsNote(fixed), ...blockSizeNote(catalog.clamped), ...heightTypeNote(catalog.heights), ...sizeStringNote(catalog.sizes)]), textFindings));
|
|
352
356
|
}));
|
|
353
357
|
server.registerTool('component_update', {
|
|
354
358
|
title: 'Komponent güncelle',
|
|
@@ -370,10 +374,11 @@ export function registerComponentTools(server, platform) {
|
|
|
370
374
|
isDroppable: z.boolean().optional(),
|
|
371
375
|
isSticky: z.boolean().optional(),
|
|
372
376
|
isDeletable: z.boolean().optional(),
|
|
373
|
-
isDefaultAutoHeight: z.boolean().optional()
|
|
377
|
+
isDefaultAutoHeight: z.boolean().optional(),
|
|
378
|
+
policyOverride: textHookOverrideParam
|
|
374
379
|
},
|
|
375
380
|
annotations: updates
|
|
376
|
-
}, handle(config, async ({ project, environment, applicationId, slug, ...changes }) => {
|
|
381
|
+
}, handle(config, async ({ project, environment, applicationId, slug, policyOverride, ...changes }) => {
|
|
377
382
|
// PAGE-037: UpdateComponentCommand de UpsertRange'ten geçer, blocks'suz widget düğümü kayıtta
|
|
378
383
|
// 500 verir. component_create bu kapıdan geçmez — CreateComponentCommand revizyon kapsamı
|
|
379
384
|
// açmıyor ve platform orada blocks'suz düğümü blocks: [] gibi kaydediyor (2026-10-04, kod okuması).
|
|
@@ -387,6 +392,14 @@ export function registerComponentTools(server, platform) {
|
|
|
387
392
|
? await fetchHost(platform, 'component', project, environment, applicationId, String(current.id))
|
|
388
393
|
: current;
|
|
389
394
|
const overrides = Object.fromEntries(Object.entries(changes).filter(([, value]) => value !== undefined));
|
|
395
|
+
const textFindings = changes.schema === undefined
|
|
396
|
+
? []
|
|
397
|
+
: await enforceTextHooks(config, {
|
|
398
|
+
platform, project, environment, applicationId,
|
|
399
|
+
after: changes.schema,
|
|
400
|
+
before: async () => detailed.schema,
|
|
401
|
+
where: `widget '${String(detailed.name ?? slug)}'`
|
|
402
|
+
}, policyOverride);
|
|
390
403
|
const widgetType = changes.widgetType ?? detailed.widgetType;
|
|
391
404
|
const wrapped = detailed.kind === 'widget' && typeof widgetType === 'string' && changes.schema !== undefined && changes.schema.length > 0
|
|
392
405
|
? wrapWidgetRoot(changes.schema, widgetType, components)
|
|
@@ -400,7 +413,7 @@ export function registerComponentTools(server, platform) {
|
|
|
400
413
|
const sizes = overrides.schema === undefined ? [] : normalizeSizeStrings(overrides.schema);
|
|
401
414
|
const body = componentUpdateBody({ ...detailed, applicationId }, overrides);
|
|
402
415
|
await platform.tenant('PUT', project, `/components/${applicationId}/${slug}`, { environment, body });
|
|
403
|
-
return withNormalization({ slug, changed: Object.keys(overrides), preserved: { schemaNodes: body.schema.length } }, [...wrapped.notes, ...componentDefaultsNote(fixed), ...blockSizeNote(clamped), ...heightTypeNote(heights), ...sizeStringNote(sizes)]);
|
|
416
|
+
return attachFindings(withNormalization({ slug, changed: Object.keys(overrides), preserved: { schemaNodes: body.schema.length } }, [...wrapped.notes, ...componentDefaultsNote(fixed), ...blockSizeNote(clamped), ...heightTypeNote(heights), ...sizeStringNote(sizes)]), textFindings);
|
|
404
417
|
}));
|
|
405
418
|
server.registerTool('component_delete', {
|
|
406
419
|
title: 'Komponent sil',
|
|
@@ -8,7 +8,8 @@ import { impersonationHeaders } from '../platform/impersonation.js';
|
|
|
8
8
|
import { handle } from '../platform/responses.js';
|
|
9
9
|
import { PlatformApiError } from '../platform/errors.js';
|
|
10
10
|
import { applyPolicyMode, enforcePolicy, attachFindings } from '../knowledge/policy.js';
|
|
11
|
-
import { connectDataOpIssues, CONNECT_DATA_OPERATORS, CONNECT_DATA_SOURCE_KINDS, lookupSchema, selectedPaths } from '../knowledge/connectDataDefinition.js';
|
|
11
|
+
import { connectDataOpIssues, CONNECT_DATA_OPERATORS, CONNECT_DATA_SOURCE_KINDS, groupingSchema, lookupSchema, mobileBehaviorSchema, mobileCardSchema, selectedPaths } from '../knowledge/connectDataDefinition.js';
|
|
12
|
+
import { resolveProfile } from '../knowledge/platform/resolve.js';
|
|
12
13
|
import { CONNECT_DATA_EXECUTE_OPS, CONNECT_DATA_WRITE_OPS, CONNECT_DATA_WRITE_ROW_LIMIT, enrichRowErrors, executeFindings } from '../knowledge/connectDataExecute.js';
|
|
13
14
|
import { bindOps, buildDefinition, enumOptionItems, capabilityUnderRecordedNames, carryFieldDetails, anonymousWriteFindings, lookupReadabilityFindings, userEyeFindings, sameSourceFindings, editFindings, CONNECT_DATA_PREVIEW_LIMIT, fieldListItemProps, inputFromExisting, readAllPageSize, invertRenames, filterFindings, findSchemaNode, readPropValue, renameBindInput, renameColumnItems, renameDefinitionPaths, renamesFromDrift, resolveConnectable, mergeOptionItems, microserviceReferenceId, stampExpandIds, stateReferenceFindings, stateReferenceNames, unboundStateNames, previewStateFindings } from '../knowledge/connectDataBind.js';
|
|
14
15
|
import { parseVariableEntries } from '../knowledge/normalize.js';
|
|
@@ -261,7 +262,7 @@ export function registerConnectDataTools(server, platform) {
|
|
|
261
262
|
}));
|
|
262
263
|
server.registerTool('connect_data_bind', {
|
|
263
264
|
title: 'Prop\'u veri kaynağına bağla',
|
|
264
|
-
description: "Tek çağrıda bağlar: bileşenin bağlanabilir prop kaydını (component_get.connectData) ve kaynağın kabiliyetini okur, tanımı kurar ve CD-001…004/006/007/008/009/010/011/013/014/015/016/017/018 bulgularını istek gitmeden üretir (CD-018: dosya alanı edit.editable'a konamaz, platform kaydı 422 connect-data-editable-file ile reddederdi; kolonların isFilterable'ı yalnız filterable listesindeki alanlarda açılır); bileşen bir layout'taysa pageId yerine layoutId, widget içindeyse componentId verilir (yazma o kaydın şema ucuna gider, sürüm koşulu yoktur, CD-014 koşmaz); block bulgusu policyOverride ile de yazılmaz: CD-004/CD-013 şekil kuralları op gövdesinde yine reddedilir; set_connect_data (+ kolon listesi, + alan adı prop'ları, + pano kolonu isteyen bileşende rolün enum değerlerinden seçenek listesi) tek atomik PATCH olarak yazılır, sonra şema yeniden okunup prop'un connectData olarak kaydedildiği doğrulanır. Roller verilmezse yalnız ZORUNLU roller önerilir (value ← birincil anahtar, label ← görünen alan) — isteğe bağlı roller (caption, color, isDisabled…) açıkça verilmedikçe eşlenmez. Liste bileşeninde (fieldsFrom) kolonlar bileşenin kendi kolon şemasından yalnız KÖK alanlardan üretilir (expand yolu satıra okunur, kolon olmaz). VAR OLAN BAĞLANTIDA aynı kaynakla çağrılırsa verilmeyen parçalar (alanlar, expand, roller, filtre, sıralama, sayfa boyu, filterable/searchable, edit) ve alanların lookup ayarları kayıtlı tanımdan taşınır; alan listesi değişmediyse ve columns verilmediyse kolon listesi yeniden yazılmaz, değiştiyse kayıtlı listeyle birleştirilir (kalan kolonların sırası ve ayarı korunur, çıkan alan düşer, yeni alan sona eklenir). Kaynakta yeniden adlanan alan id ile eşleşir: girdide kayıtlı ya da güncel ad verilebilir, tanım güncel adlarla kurulur ve sayfanın gördüğü kayıtlı adla yazılır (rebind.renamed). Expand alt alanlarına hedef alan id'si yazılır; kayıtlı lookup ayarları kimlikleriyle taşınır. Seçim bileşeninde (connectData.props[prop].lookup) entity kaynağına YENİ bağlamada kök lookup açık yazılır ({ fields: [], mode: 'modal' } — alanın yanında ↗, pencerede türetilmiş kolonlar); yeniden bağlamada kayıtlı kök lookup korunur, kök lookup'sız eski bağlantı kapalı kalır; lookup girdisi ayarlar, null kapatır. Block bulgusu varsa hiçbir şey yazılmaz; dryRun: true yalnız tanımı, op'ları ve bulguları döner.",
|
|
265
|
+
description: "Tek çağrıda bağlar: bileşenin bağlanabilir prop kaydını (component_get.connectData) ve kaynağın kabiliyetini okur, tanımı kurar ve CD-001…004/006/007/008/009/010/011/013/014/015/016/017/018/019/020 bulgularını istek gitmeden üretir (CD-018: dosya alanı edit.editable'a konamaz, platform kaydı 422 connect-data-editable-file ile reddederdi; CD-019/020: gruplu liste grouping ister, başka bileşen almaz; kolonların isFilterable'ı yalnız filterable listesindeki alanlarda açılır); bileşen bir layout'taysa pageId yerine layoutId, widget içindeyse componentId verilir (yazma o kaydın şema ucuna gider, sürüm koşulu yoktur, CD-014 koşmaz); block bulgusu policyOverride ile de yazılmaz: CD-004/CD-013 şekil kuralları op gövdesinde yine reddedilir; set_connect_data (+ kolon listesi, + alan adı prop'ları, + pano kolonu isteyen bileşende rolün enum değerlerinden seçenek listesi) tek atomik PATCH olarak yazılır, sonra şema yeniden okunup prop'un connectData olarak kaydedildiği doğrulanır. Roller verilmezse yalnız ZORUNLU roller önerilir (value ← birincil anahtar, label ← görünen alan) — isteğe bağlı roller (caption, color, isDisabled…) açıkça verilmedikçe eşlenmez. Liste bileşeninde (fieldsFrom) kolonlar bileşenin kendi kolon şemasından yalnız KÖK alanlardan üretilir (expand yolu satıra okunur, kolon olmaz). VAR OLAN BAĞLANTIDA aynı kaynakla çağrılırsa verilmeyen parçalar (alanlar, expand, roller, filtre, sıralama, sayfa boyu, filterable/searchable, edit) ve alanların lookup ayarları kayıtlı tanımdan taşınır; alan listesi değişmediyse ve columns verilmediyse kolon listesi yeniden yazılmaz, değiştiyse kayıtlı listeyle birleştirilir (kalan kolonların sırası ve ayarı korunur, çıkan alan düşer, yeni alan sona eklenir). Kaynakta yeniden adlanan alan id ile eşleşir: girdide kayıtlı ya da güncel ad verilebilir, tanım güncel adlarla kurulur ve sayfanın gördüğü kayıtlı adla yazılır (rebind.renamed). Expand alt alanlarına hedef alan id'si yazılır; kayıtlı lookup ayarları kimlikleriyle taşınır. Seçim bileşeninde (connectData.props[prop].lookup) entity kaynağına YENİ bağlamada kök lookup açık yazılır ({ fields: [], mode: 'modal' } — alanın yanında ↗, pencerede türetilmiş kolonlar); yeniden bağlamada kayıtlı kök lookup korunur, kök lookup'sız eski bağlantı kapalı kalır; lookup girdisi ayarlar, null kapatır. MOBİL uygulamada telefon varsayılanları yazılır: kök lookup'a mobileMode 'sheet' ve mobileRow.title ← label alanı, liste/seçim bağlantısına behavior.mobile { infiniteScroll: true }. Block bulgusu varsa hiçbir şey yazılmaz; dryRun: true yalnız tanımı, op'ları ve bulguları döner.",
|
|
265
266
|
inputSchema: {
|
|
266
267
|
project: projectParam,
|
|
267
268
|
environment: environmentParam,
|
|
@@ -295,7 +296,10 @@ export function registerConnectDataTools(server, platform) {
|
|
|
295
296
|
refresh: z.object({ afterWrite: z.enum(['reload', 'merge']).optional(), poll: z.number().int().min(1).nullable().optional() }).strict().optional(),
|
|
296
297
|
columns: z.union([z.literal('auto'), z.array(z.record(z.unknown()))]).optional().describe("Liste bileşeninde (fieldsFrom) kolon listesi: 'auto' bileşenin kolon şemasından ve kabiliyetten üretir; dizi verilirse öğe değerleri her seviyede static Prop'a sarılarak yazılır (sarmal değerlere dokunulmaz). Var olan bağlantıda verilmezse ve alanlar değişmediyse kolonlar korunur"),
|
|
297
298
|
fixDrift: z.boolean().optional().describe('true: kaynaktan silinmiş alanları tanımdan düşürür (CD-008)'),
|
|
298
|
-
lookup: lookupSchema.nullable().optional().describe("Seçim bileşeninin KENDİ kaynağı üzerindeki lookup penceresi (tanımın kök lookup'ı; alan lookup'ıyla aynı biçim: { fields: [{ slug }], behavior: { filterable, searchable }, sort, page, mode: 'modal' | 'drawer' }; fields boşsa platform türetir). Verilmezse yeni bağlamada açık yazılır, yeniden bağlamada kayıtlı ayar korunur; null kapatır. Yalnız entity kaynağında (CD-013)."),
|
|
299
|
+
lookup: lookupSchema.nullable().optional().describe("Seçim bileşeninin KENDİ kaynağı üzerindeki lookup penceresi (tanımın kök lookup'ı; alan lookup'ıyla aynı biçim: { fields: [{ slug }], behavior: { filterable, searchable }, sort, page, mode: 'modal' | 'drawer' }; fields boşsa platform türetir). Verilmezse yeni bağlamada açık yazılır, yeniden bağlamada kayıtlı ayar korunur; null kapatır. Yalnız entity kaynağında (CD-013). Telefonda: mobileMode 'sheet' (alt sayfa) | 'fullscreen', mobileRow { title, subtitle?, trailing? } lookup alanlarından (dışındaysa CD-004); MOBİL uygulamada verilmezse mobileMode 'sheet' ve mobileRow.title = label alanı yazılır."),
|
|
300
|
+
mobile: mobileBehaviorSchema.nullable().optional().describe("Telefon davranışı (behavior.mobile): seçim bileşeninde search · infiniteScroll · pullToRefresh · recentSelections, datatable'da filterChips · swipeDelete · infiniteScroll · pullToRefresh. MOBİL uygulamada liste/seçim bağlantısında verilmezse { infiniteScroll: true } (sayfalama tanımın page.mode'uyla); yeniden bağlamada kayıtlı ayar korunur; null kaldırır."),
|
|
301
|
+
mobileCard: mobileCardSchema.nullable().optional().describe("Datatable kart görünümü telefonda (behavior.mobileCard): { title, fields?: [...], trailing? } seçili yollardan (dışındaysa CD-004). Verilmezse kayıtlı ayar korunur, yoksa telefonda ilk metin alan başlıktır; null kaldırır."),
|
|
302
|
+
grouping: groupingSchema.nullable().optional().describe("Gruplu listenin bölümlemesi (kök ADR 0019): { by: '<seçili alan>', sectionOrder: 'asc' | 'desc' | 'asis', dateBucket?: 'day' | 'week' | 'month' (yalnız date/datetime alan), otherSectionTitle?, stickyHeaders?, showCounts? }. Satırları bölümleyen bileşende (connectData.props.<prop>.wizardSteps 'grouping' — mobilde grouped-list) ZORUNLU (yoksa CD-019 block), başka bileşende yazılmaz (CD-020 block). Yalnız entity kaynağında (CD-013); sunucu sırayı gruplama alanıyla başlatır (azalanda boşlar sonda), date alanında kovayı satıra __section yazar (datetime cihazda kovalanır); gruplama alanı sıralanabilir olmalı (asis hariç). Yeniden bağlamada kayıtlı ayar korunur."),
|
|
299
303
|
params: z.record(z.unknown()).optional().describe("Kaynak parametreleri (query/workflow/microservice): sabit değer ya da '$ad' sayfa değişkeni ({ yil: 2026, sube: '$secilenSube' }). Adlar connect_data_describe source.parameters'tan (CD-013); '$ad' page_state'te olmalı (CD-014). Entity/view parametre almaz — filtrede '$ad' kullanın."),
|
|
300
304
|
baseVersion: z.string().optional().describe('Sayfa sürümü; değiştiyse 409 ve hiçbir op yazılmaz. Verilmezse bu çağrıda okunan sürüm'),
|
|
301
305
|
dryRun: z.boolean().optional(),
|
|
@@ -342,7 +346,8 @@ export function registerConnectDataTools(server, platform) {
|
|
|
342
346
|
const effectiveInput = readAll === undefined ? carriedInput : { ...carriedInput, pageSize: readAll };
|
|
343
347
|
const sourceCapability = await describeSource(project, environment, { source: input.source });
|
|
344
348
|
const capability = isSameSource ? { ...sourceCapability, drift: existingDrift } : sourceCapability;
|
|
345
|
-
const
|
|
349
|
+
const designer = (await resolveProfile(platform, applicationId)).profile.platform;
|
|
350
|
+
const built = buildDefinition(effectiveInput, capability, connectable, Object.keys(connectData?.props ?? {}), fieldListItemProps(componentDefinition.props, connectable?.fieldsFrom), designer);
|
|
346
351
|
const carried = carryFieldDetails(built.definition, isSameSource ? working : null);
|
|
347
352
|
const stamped = input.source.kind === 'entity' && carried.fields.some(field => field.expand?.some(child => !child.id))
|
|
348
353
|
? stampExpandIds(carried, await describeSource(project, environment, { definition: carried }))
|
|
@@ -26,7 +26,7 @@ export function registerDeploymentTools(server, platform) {
|
|
|
26
26
|
const config = platform.config;
|
|
27
27
|
server.registerTool('deployment_start', {
|
|
28
28
|
title: 'Deployment başlat',
|
|
29
|
-
description: 'Projeyi kaynak environment\'tan hedef environment\'a deploy eder. Asenkron bir saga\'dır: sonuç bu yanıtta bitmez, deployment_status ile izleyin. Environment id\'lerini project_get çıktısından alın. BPM: süreç tanımları ve iş takvimleri taşınır — hedefte farklı aktif model yeni sürüm olarak yayınlanır (production\'da gelişte aktif), hedefin uyarlaması korunur; uyarlama, örnek, görev ve defter taşınmaz; kaynakta olmayan tanım silinir, açık örneği varsa emekliye ayrılır. KAYNAK YALNIZ DEVELOPMENT\'TIR: dal ya da production kaynak verilirse 400 (CantDeployFromBranchException) — dalı önce development\'a merge edin. ÖN DENETİM: deploy başlamadan production verisi denetlenir; veriyi bozacak ya da silecek her değişiklik 400 (DeploymentDataRiskException) ile listelenir ve deploy BAŞLAMAZ — biçim \'tablo.alan: tür (satır)\', türler not-null | unique | field-removed | table-removed | localization-move. Kayıt taşıması \'all\' olan tabloda production development\'ın AYNASIDIR (kaynakta olmayan satır ve çevirisi silinir). Microfunction\'ın yalnız YAYINLANMIŞ kodu taşınır ve hedefte yayınlanır; hiç yayınlanmamış olan hedefte taslak kalır. Zamanlanmış işin duraklatma durumu taşınır. YAYINLANMAMIŞ TASLAK TAŞINMAZ: kaynakta taslağı olan süreçler yanıtta unpublishedDrafts ({ slug, name, draftVersionNumber, activeVersionNumber }) ve note ile listelenir; hedefte aktif sürüm kalır. Taşımak için taslağı kaynakta yayınlayın (requireGreenScenarios açıksa önce senaryoları taslak üzerinde koşun), sonra yeniden deploy edin.',
|
|
29
|
+
description: 'Projeyi kaynak environment\'tan hedef environment\'a deploy eder. Asenkron bir saga\'dır: sonuç bu yanıtta bitmez, deployment_status ile izleyin. Environment id\'lerini project_get çıktısından alın. BPM: süreç tanımları ve iş takvimleri taşınır — hedefte farklı aktif model yeni sürüm olarak yayınlanır (production\'da gelişte aktif), hedefin uyarlaması korunur; uyarlama, örnek, görev ve defter taşınmaz; kaynakta olmayan tanım silinir, açık örneği varsa emekliye ayrılır. KAYNAK YALNIZ DEVELOPMENT\'TIR: dal ya da production kaynak verilirse 400 (CantDeployFromBranchException) — dalı önce development\'a merge edin. ÖN DENETİM: deploy başlamadan production verisi denetlenir; veriyi bozacak ya da silecek her değişiklik 400 (DeploymentDataRiskException) ile listelenir ve deploy BAŞLAMAZ — biçim \'tablo.alan: tür (satır)\', türler not-null | unique | field-removed | table-removed | localization-move. YALNIZ TANIM TAŞINIR: tablo satırları ancak tabloda recordTransferMode (\'all\' | \'specific\' + transferRowIds) açıksa gider, çeviri ve junction satırları ana tabloyla birlikte. Kayıt taşıması \'all\' olan tabloda production development\'ın AYNASIDIR (kaynakta olmayan satır ve çevirisi silinir). DOSYA yalnız id referansıyla gider (file tipli prop { type:\'file\', ref:<fileId> } ya da taşınan satırın dosya sütunu); sayfaya düz yazılan /d/ serve URL\'si /p/\'ye çevrilir ama dosya kopyalanmaz, production\'da 404 döner — platform_guide(\'deployment\'). Microfunction\'ın yalnız YAYINLANMIŞ kodu taşınır ve hedefte yayınlanır; hiç yayınlanmamış olan hedefte taslak kalır. Zamanlanmış işin duraklatma durumu taşınır. YAYINLANMAMIŞ TASLAK TAŞINMAZ: kaynakta taslağı olan süreçler yanıtta unpublishedDrafts ({ slug, name, draftVersionNumber, activeVersionNumber }) ve note ile listelenir; hedefte aktif sürüm kalır. Taşımak için taslağı kaynakta yayınlayın (requireGreenScenarios açıksa önce senaryoları taslak üzerinde koşun), sonra yeniden deploy edin.',
|
|
30
30
|
inputSchema: { projectId: guid, sourceEnvironmentId: guid, targetEnvironmentId: guid },
|
|
31
31
|
annotations: writes
|
|
32
32
|
}, handle(config, async ({ projectId, ...body }) => {
|
|
@@ -13,6 +13,9 @@ import { slotWarning } from './layouts.js';
|
|
|
13
13
|
import { batchEnvelopeNote, openBatchEnvelopes } from '../knowledge/platform/batchOps.js';
|
|
14
14
|
import { applicationTypeField, resolveProfile } from '../knowledge/platform/resolve.js';
|
|
15
15
|
import { boundFlowSteps, layoutPatchWarnings, screenProfileWarnings } from './profileWrite.js';
|
|
16
|
+
import { enforceTextHooks, textHookOverrideParam } from './textHookGuard.js';
|
|
17
|
+
import { overwrittenValues } from '../knowledge/textHooks.js';
|
|
18
|
+
import { attachFindings } from '../knowledge/policy.js';
|
|
16
19
|
import { z } from 'zod';
|
|
17
20
|
const HOSTS = {
|
|
18
21
|
layout: {
|
|
@@ -54,10 +57,11 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
54
57
|
ops: opsParam.optional(),
|
|
55
58
|
operations: opsParam.optional().describe("'ops' ile aynı alan (eşanlamlı)"),
|
|
56
59
|
allowFlowDeletion: z.boolean().optional().describe(`true: bu yama bir akışı referanssız bırakacaksa yine de uygulanır. Varsayılan false — platform ${host === 'layout' ? 'layout' : 'widget'} kaydında da referanssız akışı KALICI siler ve gateway isteği önden reddeder (G-21).`),
|
|
57
|
-
view: writeViewParam
|
|
60
|
+
view: writeViewParam,
|
|
61
|
+
policyOverride: textHookOverrideParam
|
|
58
62
|
},
|
|
59
63
|
annotations: updates
|
|
60
|
-
}, handle(config, async ({ project, environment, applicationId, id, ops, operations, allowFlowDeletion, view }) => {
|
|
64
|
+
}, handle(config, async ({ project, environment, applicationId, id, ops, operations, allowFlowDeletion, view, policyOverride }) => {
|
|
61
65
|
const opened = openBatchEnvelopes(spec.tool, microOps(spec.tool, ops, operations).map(op => op));
|
|
62
66
|
const allOps = opened.ops;
|
|
63
67
|
const removeEventOps = allOps.filter(entry => entry.op === 'remove_event');
|
|
@@ -83,6 +87,14 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
83
87
|
const events = host === 'layout' ? record.events : undefined;
|
|
84
88
|
const normalized = normalizeSchemaOps(opRecords);
|
|
85
89
|
assertOpBodies(opRecords, propTypeOps);
|
|
90
|
+
// TR-005 / TR-006: yamanın yükü op'ların EZDİĞİ kayıtlı değerle karşılaştırılır — orada zaten duran kanca eskidir.
|
|
91
|
+
const textFindings = await enforceTextHooks(config, {
|
|
92
|
+
platform, project, environment, applicationId,
|
|
93
|
+
after: [...opRecords, ...propTypeOps],
|
|
94
|
+
before: async () => overwrittenValues(nodes, [...opRecords, ...propTypeOps]),
|
|
95
|
+
hostTree: async () => nodes,
|
|
96
|
+
where: `${host === 'layout' ? 'layout' : 'widget'} yaması '${String(record.name ?? record.slug ?? id)}'`
|
|
97
|
+
}, policyOverride);
|
|
86
98
|
// CD-005 layout ve widget host'unda da: bağlı prop'u (iç içe yol dahil) ezen yazma ve bağlı olmayan prop'ta
|
|
87
99
|
// remove_connect_data okunan ağaca bakarak reddedilir (gövde denetimi assertOpBodies'te koştu).
|
|
88
100
|
const connectedProps = connectedPropKeys(nodes);
|
|
@@ -184,7 +196,7 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
184
196
|
opDrops = emptyWriteDiff();
|
|
185
197
|
}
|
|
186
198
|
}
|
|
187
|
-
return withWriteDiff(withNormalization(response, [
|
|
199
|
+
return attachFindings(withWriteDiff(withNormalization(response, [
|
|
188
200
|
...batchEnvelopeNote(opened),
|
|
189
201
|
...componentNameNote(normalized.names),
|
|
190
202
|
...sectionNotes,
|
|
@@ -192,6 +204,6 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
192
204
|
...validationNote(normalized.validations),
|
|
193
205
|
...catalogNotes,
|
|
194
206
|
...replacePropNote(normalized.props)
|
|
195
|
-
]), opDrops);
|
|
207
|
+
]), opDrops), textFindings);
|
|
196
208
|
}));
|
|
197
209
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { branchNamePattern, destructive, guid, isReservedEnvironmentName, readOnly, writes } from './common.js';
|
|
1
|
+
import { branchNamePattern, destructive, guid, isReservedEnvironmentName, readOnly, templateSubscriptionLock, writes } from './common.js';
|
|
2
2
|
import { PlatformApiError } from '../platform/errors.js';
|
|
3
3
|
import { handle } from '../platform/responses.js';
|
|
4
4
|
import { z } from 'zod';
|
|
@@ -20,7 +20,7 @@ export function registerEnvironmentTools(server, platform) {
|
|
|
20
20
|
}, handle(config, ({ projectId }) => platform.org('GET', `/environments/${projectId}`)));
|
|
21
21
|
server.registerTool('environment_create', {
|
|
22
22
|
title: 'Dal environment\'ı aç',
|
|
23
|
-
description:
|
|
23
|
+
description: `Projede yeni bir dal environment'ı açar: kendi PG şeması kurulur ve development'ın tanımları oraya kopyalanır. Asenkron bir saga'dır — yanıt 202 döner, dal hemen kullanılabilir DEĞİLDİR. includeRecords true ise projenin KENDİ veritabanındaki bütün veri tablolarının satırları da kopyalanır (çok dilli companion tablolar ve junction'lar dahil; files/folders ve audit trail tabloları hariç, dış datasource'lara dokunulmaz) — entity'de kayıt taşıması ayarı açık olmasa bile. Merge'de yalnız kayıt taşıması ayarı açık tabloların satırları üç yollu geri taşınır (rehber: merge, Tablo kayıtları). ${templateSubscriptionLock}`,
|
|
24
24
|
inputSchema: { projectId: guid, name: branchName, includeRecords: z.boolean().optional().describe('Projenin kendi veritabanındaki bütün tabloların satırları da kopyalansın mı (varsayılan false)') },
|
|
25
25
|
annotations: writes
|
|
26
26
|
}, handle(config, async ({ projectId, name, includeRecords }) => {
|
|
@@ -33,7 +33,7 @@ export function registerEnvironmentTools(server, platform) {
|
|
|
33
33
|
}));
|
|
34
34
|
server.registerTool('environment_delete', {
|
|
35
35
|
title: 'Dal environment\'ını sil',
|
|
36
|
-
description:
|
|
36
|
+
description: `Bir dal environment'ını ve PG şemasını siler. Sabit environment'lar (development, production) silinemez — gateway bunu HTTP'den önce reddeder. Yalnız 'ready', 'merged' ya da 'error' durumundaki dal silinebilir; 'merged' dal applied-with-drift merge'ünden artakalandır (merge rehberi §Merge bittikten sonra). Silme asenkrondur. ${templateSubscriptionLock}`,
|
|
37
37
|
inputSchema: { projectId: guid, environmentId: guid },
|
|
38
38
|
annotations: destructive
|
|
39
39
|
}, handle(config, async ({ projectId, environmentId }) => {
|
package/dist/tools/files.js
CHANGED
|
@@ -104,7 +104,7 @@ export function registerFileTools(server, platform) {
|
|
|
104
104
|
}, handle(config, ({ project, environment, id }) => platform.tenant('GET', project, `/files/${id}`, { environment })));
|
|
105
105
|
server.registerTool('file_upload', {
|
|
106
106
|
title: 'Dosya yükle',
|
|
107
|
-
description: "Dosyayı projenin depolamasına yükler ve FileDto (id + serve adresleri) döner. İçerik ÜÇ yoldan verilir: `text` — düz metin içerik (SVG, CSV, Markdown, JSON…) + `filename`; base64'e çevirmeye gerek yok, bunu gateway yapar. `path` — gateway sürecinin okuyabildiği yerel yol (büyük ve ikili dosyalar için). `content` — base64 (8 MB'a kadar) + `filename`. Platform sınırı 50 MB; dosya adı \\|!#~$%&/=?»«@£§€{};'<>, karakterlerini içeremez. Yüklenen dosya sayfada `image`/`file-viewer` komponentiyle gösterilir ya da satır alanına yol olarak yazılır.",
|
|
107
|
+
description: "Dosyayı projenin depolamasına yükler ve FileDto (id + serve adresleri) döner. İçerik ÜÇ yoldan verilir: `text` — düz metin içerik (SVG, CSV, Markdown, JSON…) + `filename`; base64'e çevirmeye gerek yok, bunu gateway yapar. `path` — gateway sürecinin okuyabildiği yerel yol (büyük ve ikili dosyalar için). `content` — base64 (8 MB'a kadar) + `filename`. Platform sınırı 50 MB; dosya adı \\|!#~$%&/=?»«@£§€{};'<>, karakterlerini içeremez. Yüklenen dosya sayfada `image`/`file-viewer` komponentiyle gösterilir ya da satır alanına yol olarak yazılır. DEPLOY dosyayı yalnız id referansıyla production'a taşır (sayfada file tipli prop `{ type:'file', ref:<fileId> }` ya da kayıt taşıması açık tablonun dosya sütunu); sayfaya düz yazılan serve URL'sinin öneki /d/ → /p/ çevrilir ama dosya kopyalanmaz ve production'da 404 döner, production'da bu araç da 400 CantWriteInProductionException ile reddedilir — platform_guide('deployment') §Dosyalar.",
|
|
108
108
|
inputSchema: {
|
|
109
109
|
project: projectParam,
|
|
110
110
|
environment: environmentParam,
|
package/dist/tools/flows.js
CHANGED
|
@@ -15,6 +15,7 @@ import { functionCallRefs } from '../knowledge/functionGuards.js';
|
|
|
15
15
|
import { existingMatch, ifExistsParam, platformRename, withRename } from '../platform/ifExists.js';
|
|
16
16
|
import { randomUUID } from 'node:crypto';
|
|
17
17
|
import { flowProfileWarnings } from './profileWrite.js';
|
|
18
|
+
import { enforceTextHooks } from './textHookGuard.js';
|
|
18
19
|
import { jsonArray } from '../platform/jsonArray.js';
|
|
19
20
|
import { z } from 'zod';
|
|
20
21
|
/**
|
|
@@ -448,6 +449,14 @@ export function registerFlowTools(server, platform) {
|
|
|
448
449
|
];
|
|
449
450
|
const existing = await existingFlowMatch(platform, host, project, environment, applicationId, hostId, hostRecord, name, slug, flowId, ifExists);
|
|
450
451
|
const writtenFlowId = existing === undefined ? resolvedFlowId : String(existing.id);
|
|
452
|
+
// TR-005 / TR-006: ifExists:'update' mevcut akışı yeniden yazar — onda zaten duran kanca eskidir.
|
|
453
|
+
const textFindings = await enforceTextHooks(config, {
|
|
454
|
+
platform, project, environment, applicationId,
|
|
455
|
+
after: { steps, errorSteps: errorSteps ?? [] },
|
|
456
|
+
before: async () => existing === undefined ? undefined : unwrap(await platform.tenant('GET', project, flowPath(host, applicationId, hostId, String(existing.id)), { environment })),
|
|
457
|
+
hostTree: async () => hostNodes(hostRecord),
|
|
458
|
+
where: `akış '${name}'`
|
|
459
|
+
}, policyOverride);
|
|
451
460
|
const parameterWarnings = [
|
|
452
461
|
...await requiredParameterWarnings(platform, project, environment, applicationId, steps),
|
|
453
462
|
...await functionWarnings(platform, host, project, environment, applicationId, hostId, steps),
|
|
@@ -481,7 +490,7 @@ export function registerFlowTools(server, platform) {
|
|
|
481
490
|
...(parameterWarnings.length > 0 ? { parameterWarnings } : {}),
|
|
482
491
|
...(platformWarnings.length > 0 ? { platformWarnings } : {}),
|
|
483
492
|
hint: "Akışı bir olaya bağlamak için: page_schema_patch (layout'ta layout_schema_patch, widget'ta component_schema_patch) ops:[{op:'set_event', target:<elementId>, event:'<component_get events listesindeki ad>', eventValue:<flow id>}]; olayda zaten akış bağlıysa ikinciyi add_event ile ekleyin (set_event bağlı akışı değiştirir, PAGE-034). 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."
|
|
484
|
-
}, normalized), drops.renamed), drops), findings);
|
|
493
|
+
}, normalized), drops.renamed), drops), [...findings, ...textFindings]);
|
|
485
494
|
}));
|
|
486
495
|
server.registerTool('flow_update', {
|
|
487
496
|
title: 'Akış güncelle',
|
|
@@ -525,6 +534,16 @@ export function registerFlowTools(server, platform) {
|
|
|
525
534
|
...enforcePolicy(evaluatePolicy(config, policyRules, 'flow', flow, { operation: 'update' }, policyOverride).filter(finding => finding.ruleId === 'FLOW-027'))
|
|
526
535
|
]
|
|
527
536
|
: [];
|
|
537
|
+
const textFindings = steps || errorSteps
|
|
538
|
+
? await enforceTextHooks(config, {
|
|
539
|
+
platform, project, environment, applicationId,
|
|
540
|
+
// Birleşik son hâl: yalnız steps gönderilip kayıtlı errorSteps'teki kanca steps'e kopyalanırsa o kopya yenidir.
|
|
541
|
+
after: { steps: flow.steps, errorSteps: flow.errorSteps },
|
|
542
|
+
before: async () => ({ steps: current.steps ?? [], errorSteps: current.errorSteps ?? [] }),
|
|
543
|
+
hostTree: async () => hostNodes(hostRecord),
|
|
544
|
+
where: `akış '${String(flow.name ?? flowId)}'`
|
|
545
|
+
}, policyOverride)
|
|
546
|
+
: [];
|
|
528
547
|
const parameterWarnings = [
|
|
529
548
|
...await requiredParameterWarnings(platform, project, environment, applicationId, flow.steps),
|
|
530
549
|
...await functionWarnings(platform, host, project, environment, applicationId, hostId, flow.steps),
|
|
@@ -543,7 +562,7 @@ export function registerFlowTools(server, platform) {
|
|
|
543
562
|
});
|
|
544
563
|
const platformWarnings = await flowProfileWarnings(platform, applicationId, [...(flow.steps ?? []), ...(flow.errorSteps ?? [])]);
|
|
545
564
|
return attachFindings(withWriteDiff(withNormalization({ host, hostId, flow: flowSummary(flow), ...(parameterWarnings.length > 0 ? { parameterWarnings } : {}),
|
|
546
|
-
...(platformWarnings.length > 0 ? { platformWarnings } : {}) }, normalized), drops), findings);
|
|
565
|
+
...(platformWarnings.length > 0 ? { platformWarnings } : {}) }, normalized), drops), [...findings, ...textFindings]);
|
|
547
566
|
}));
|
|
548
567
|
server.registerTool('flow_patch', {
|
|
549
568
|
title: "Akışa patch uygula",
|
|
@@ -579,7 +598,14 @@ export function registerFlowTools(server, platform) {
|
|
|
579
598
|
const normalized = [...normalizeFlowSteps(flow.steps), ...normalizeFlowSteps(flow.errorSteps)];
|
|
580
599
|
const findings = [
|
|
581
600
|
...enforcePolicy(evaluatePolicy(config, policyRules, 'flow', flow, { operation: 'update' }, policyOverride)),
|
|
582
|
-
...enforcePolicy(config.policyMode === 'off' ? [] : applyPolicyMode(config, connectedSetterFindings(flow.steps, flow.errorSteps, hostNodes(hostRecord)), policyOverride))
|
|
601
|
+
...enforcePolicy(config.policyMode === 'off' ? [] : applyPolicyMode(config, connectedSetterFindings(flow.steps, flow.errorSteps, hostNodes(hostRecord)), policyOverride)),
|
|
602
|
+
...await enforceTextHooks(config, {
|
|
603
|
+
platform, project, environment, applicationId,
|
|
604
|
+
after: { steps: flow.steps, errorSteps: flow.errorSteps },
|
|
605
|
+
before: async () => ({ steps: current.steps ?? [], errorSteps: current.errorSteps ?? [] }),
|
|
606
|
+
hostTree: async () => hostNodes(hostRecord),
|
|
607
|
+
where: `akış '${String(current.name ?? flowId)}'`
|
|
608
|
+
}, policyOverride)
|
|
583
609
|
];
|
|
584
610
|
const parameterWarnings = [
|
|
585
611
|
...await requiredParameterWarnings(platform, project, environment, applicationId, flow.steps),
|