@appaflytech/zeyrek-ai-mcp 0.0.36 → 0.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +15 -0
- package/README.md +5 -5
- package/dist/config.js +37 -0
- package/dist/index.js +2 -0
- package/dist/knowledge/access.js +802 -0
- package/dist/knowledge/backendConstraints.js +18 -1
- package/dist/knowledge/case.js +41 -0
- package/dist/knowledge/componentCatalog.js +524 -53
- package/dist/knowledge/componentEvents.js +132 -7
- package/dist/knowledge/connectDataBind.js +851 -0
- package/dist/knowledge/connectDataDefinition.js +344 -0
- package/dist/knowledge/connectDataExecute.js +93 -0
- package/dist/knowledge/connectDataRules.js +128 -0
- package/dist/knowledge/decision.js +121 -0
- package/dist/knowledge/expressionPrelude.js +60 -0
- package/dist/knowledge/layoutSummary.js +140 -2
- package/dist/knowledge/microserviceParameters.js +58 -0
- package/dist/knowledge/mining.js +94 -0
- package/dist/knowledge/mobileComponentCatalog.js +876 -0
- package/dist/knowledge/mobileComponentEvents.js +259 -0
- package/dist/knowledge/mobileStore.js +396 -0
- package/dist/knowledge/normalize.js +240 -72
- package/dist/knowledge/outlines.js +36 -0
- package/dist/knowledge/platform/lint.js +385 -0
- package/dist/knowledge/platform/mobile.js +142 -0
- package/dist/knowledge/platform/mobileLayoutLint.js +324 -0
- package/dist/knowledge/platform/mobileRules.js +231 -0
- package/dist/knowledge/platform/mobileSpacing.js +82 -0
- package/dist/knowledge/platform/mobileTabs.js +108 -0
- package/dist/knowledge/platform/nativeBoxModel.js +44 -0
- package/dist/knowledge/platform/profile.js +25 -0
- package/dist/knowledge/platform/resolve.js +122 -0
- package/dist/knowledge/platform/web.js +39 -0
- package/dist/knowledge/policy.js +18 -4
- package/dist/knowledge/process.js +510 -0
- package/dist/knowledge/reservedTables.js +79 -0
- package/dist/knowledge/rowFilter.js +43 -0
- package/dist/knowledge/rules.js +485 -22
- package/dist/knowledge/schemaGuard.js +11 -0
- package/dist/knowledge/styleResolve.js +64 -10
- package/dist/knowledge/styleResolveNative.js +529 -0
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/observability/trace.js +13 -5
- package/dist/platform/argumentAliases.js +128 -0
- package/dist/platform/bodyFile.js +103 -0
- package/dist/platform/capture/actions.js +398 -0
- package/dist/platform/capture/browser.js +1 -0
- package/dist/platform/capture/capture.js +683 -0
- package/dist/platform/capture/console-log.js +122 -0
- package/dist/platform/capture/index.js +11 -0
- package/dist/platform/capture/readiness.js +103 -0
- package/dist/platform/capture/summary.js +77 -0
- package/dist/platform/capture/target.js +5 -0
- package/dist/platform/capture/targets/studio-mobile.js +15 -0
- package/dist/platform/capture/targets/studio-web.js +5 -0
- package/dist/platform/diagnostics.js +10 -4
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/errors.js +163 -10
- package/dist/platform/http.js +29 -8
- package/dist/platform/impersonation.js +45 -0
- package/dist/platform/jsonArray.js +24 -0
- package/dist/platform/responses.js +39 -5
- package/dist/platform/schemaReadMemory.js +60 -0
- package/dist/platform/secrets.js +68 -2
- package/dist/platform/strictArguments.js +68 -12
- package/dist/platform/tokenVault.js +187 -4
- package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
- package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
- package/dist/resources/examples/bpm-satinalma.json +376 -0
- package/dist/resources/examples/connect-data-convert-verify.json +107 -0
- package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
- package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
- package/dist/resources/examples/connect-data-query-params.json +50 -0
- package/dist/resources/examples/connect-data-table-entity.json +147 -0
- package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
- package/dist/resources/examples/middleware-authenticate.json +1 -1
- package/dist/resources/examples/mobile-app-shell-drawer.json +244 -0
- package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
- package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
- package/dist/resources/examples/mobile-form-screen.json +365 -0
- package/dist/resources/examples/mobile-list-detail-form.json +703 -0
- package/dist/resources/examples/page-detail-profile.json +2 -1
- package/dist/resources/examples/page-dynamic-form.json +3 -2
- package/dist/resources/examples/page-flex-sections.json +1 -1
- package/dist/resources/examples/page-html-embed.json +1 -1
- package/dist/resources/examples/workflow-basic.json +8 -2
- package/dist/resources/guides/access.md +810 -0
- package/dist/resources/guides/bpm.md +691 -0
- package/dist/resources/guides/case.md +153 -0
- package/dist/resources/guides/component.md +92 -5
- package/dist/resources/guides/decision.md +185 -0
- package/dist/resources/guides/entity.md +50 -8
- package/dist/resources/guides/expressions.md +14 -5
- package/dist/resources/guides/functions.md +1 -1
- package/dist/resources/guides/getting-started.md +38 -1
- package/dist/resources/guides/layout.md +11 -7
- package/dist/resources/guides/merge.md +42 -7
- package/dist/resources/guides/microservice.md +88 -3
- package/dist/resources/guides/middleware.md +69 -5
- package/dist/resources/guides/mobile-layout.md +193 -0
- package/dist/resources/guides/mobile-publish.md +148 -0
- package/dist/resources/guides/mobile.md +565 -0
- package/dist/resources/guides/page.md +469 -82
- package/dist/resources/guides/theme.md +8 -2
- package/dist/resources/guides/translation.md +1 -1
- package/dist/resources/guides/workflow.md +58 -1
- package/dist/resources/read.js +41 -0
- package/dist/resources/rules.js +11 -5
- package/dist/resources/schema-fingerprint.json +7 -5
- package/dist/resources/schemas/AccessExplanationDto.json +144 -0
- package/dist/resources/schemas/AccessMatrixDto.json +170 -0
- package/dist/resources/schemas/AccessPermissionDto.json +57 -0
- package/dist/resources/schemas/AccessPolicyDto.json +223 -0
- package/dist/resources/schemas/AccessResourceDto.json +86 -0
- package/dist/resources/schemas/AppSessionDto.json +53 -0
- package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
- package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
- package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
- package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
- package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
- package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
- package/dist/resources/schemas/CreateEntityCommand.json +118 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
- package/dist/resources/schemas/CreateQueryCommand.json +4 -0
- package/dist/resources/schemas/CreateWorkflowCommand.json +146 -1
- package/dist/resources/schemas/DataJobDto.json +6 -1
- package/dist/resources/schemas/DecisionDto.json +302 -0
- package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
- package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
- package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +24 -0
- package/dist/resources/schemas/MicroserviceDto.json +15 -3
- package/dist/resources/schemas/OrganizationProjectDto.json +343 -0
- package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
- package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
- package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
- package/dist/resources/schemas/PreviewWorkflowCommand.json +25 -1
- package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
- package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
- package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
- package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
- package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
- package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
- package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
- package/dist/resources/schemas/StartMobileBuildCommand.json +54 -0
- package/dist/resources/schemas/StepDto.json +25 -1
- package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
- package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
- package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
- package/dist/resources/schemas/UpdateViewCommand.json +0 -2
- package/dist/resources/schemas/UpdateWorkflowCommand.json +146 -1
- package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +333 -0
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
- package/dist/server.js +31 -1
- package/dist/tools/access.js +369 -2
- package/dist/tools/accessOperations.js +135 -0
- package/dist/tools/accessPolicies.js +170 -0
- package/dist/tools/accessRuntime.js +203 -0
- package/dist/tools/applications.js +63 -3
- package/dist/tools/bpm.js +343 -0
- package/dist/tools/bpmGovernance.js +266 -0
- package/dist/tools/bpmOperations.js +230 -0
- package/dist/tools/bpmReporting.js +154 -0
- package/dist/tools/bpmRouting.js +150 -0
- package/dist/tools/bpmRuntime.js +396 -0
- package/dist/tools/case.js +178 -0
- package/dist/tools/caseRuntime.js +89 -0
- package/dist/tools/common.js +19 -6
- package/dist/tools/components.js +118 -14
- package/dist/tools/connectData.js +450 -0
- package/dist/tools/connectors.js +193 -0
- package/dist/tools/datasources.js +3 -3
- package/dist/tools/decisions.js +206 -0
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +38 -3
- package/dist/tools/designSchemaPatch.js +34 -9
- package/dist/tools/dtos.js +36 -5
- package/dist/tools/entities.js +79 -12
- package/dist/tools/environments.js +1 -1
- package/dist/tools/externalComponents.js +26 -5
- package/dist/tools/flows.js +119 -13
- package/dist/tools/folders.js +1 -1
- package/dist/tools/imports.js +5 -2
- package/dist/tools/layouts.js +50 -17
- package/dist/tools/merges.js +2 -2
- package/dist/tools/microservices.js +77 -25
- package/dist/tools/mobile.js +280 -0
- package/dist/tools/mobileStore.js +572 -0
- package/dist/tools/modules.js +48 -0
- package/dist/tools/organizationStructure.js +137 -0
- package/dist/tools/pages.js +467 -100
- package/dist/tools/platformInfo.js +28 -8
- package/dist/tools/profileWrite.js +92 -0
- package/dist/tools/projects.js +71 -9
- package/dist/tools/rows.js +69 -13
- package/dist/tools/schemaOps.js +61 -1
- package/dist/tools/screenWrite.js +36 -7
- package/dist/tools/screens.js +95 -5
- package/dist/tools/templates.js +26 -1
- package/dist/tools/translations.js +1 -1
- package/dist/tools/variables.js +11 -5
- package/dist/tools/workflows.js +10 -5
- package/package.json +2 -2
- package/dist/platform/screenshot.js +0 -1162
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive } from './common.js';
|
|
2
|
+
import { accessPolicyEntityFindings, accessResourceTypes, accessScopes } from '../knowledge/access.js';
|
|
3
|
+
import { evaluatePolicy, enforcePolicy, attachFindings, applyPolicyMode } from '../knowledge/policy.js';
|
|
4
|
+
import { unmappedMembers, withUnmappedMembers } from '../knowledge/schemaGuard.js';
|
|
5
|
+
import { withNormalization } from '../knowledge/normalize.js';
|
|
6
|
+
import { policyRules } from '../knowledge/rules.js';
|
|
7
|
+
import { handle } from '../platform/responses.js';
|
|
8
|
+
import { RefResolver } from '../platform/refs.js';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
const policyOverrideParam = z.boolean().optional()
|
|
11
|
+
.describe('true: politika block bulgularını warn\'a indirir (bilinçli istisna; yanıtta işaretlenir)');
|
|
12
|
+
const grantInput = z.object({
|
|
13
|
+
id: guid.optional().describe('Var olan grant güncelleniyorsa onun id\'si; yeni grant\'te verilmez'),
|
|
14
|
+
permission: z.string().min(1).describe("İzin slug'ı (access_permission_list): 'entity.read', 'field.read', 'microservice.execute' …"),
|
|
15
|
+
resourceKey: z.string().min(1).nullable().optional().describe("Kaynak anahtarı (access_resource_list 'key'): 'entity:<entityId>', 'field:<entityId>:<fieldId>', '<tip>:<id>'; verilmezse iznin tipinin TAMAMI"),
|
|
16
|
+
effect: z.enum(['allow', 'deny']).describe("Kapsamsız ya da koşulsuz scope: 'all' taşıyan 'deny' eylemi keser (TB-132); daraltıcı kapsamlı (own · unit · unit-tree · subordinates · function · company) ya da scope: 'all' + koşullu 'deny' eylemi kesmez, yalnız kapsamındaki / koşula uyan satırları süzer. 'deny' alan erişimi taşımaz (K-10)"),
|
|
17
|
+
scope: z.enum(accessScopes).optional().describe('Satır kapsamı; yalnız entity.read/update/delete/export/reveal/reassign'),
|
|
18
|
+
subordinatesLine: z.enum(['administrative', 'functional', 'both']).optional().describe("Yalnız scope: 'subordinates' ile; varsayılan idari hat"),
|
|
19
|
+
fieldAccess: z.enum(['hidden', 'masked', 'plain']).optional().describe("Yalnız 'field.read' izninde"),
|
|
20
|
+
condition: z.record(z.unknown()).optional().describe("Kapsama AND ile eklenen koşul: { all|any: [...] } ya da { field, op, value }; op eq·neq·in·notin·lt·lte·gt·gte·isnull·isnotnull; value sabit, dizi, { subject: 'userId'|'unitIds'|'positionIds'|'companyIds'|'roleSlugs'|'functionCodes' } ya da { now: true }. Derinlik ≤ 5, düğüm ≤ 50")
|
|
21
|
+
});
|
|
22
|
+
const assignmentInput = z.object({
|
|
23
|
+
id: guid.optional(),
|
|
24
|
+
subjectType: z.enum(['role', 'position', 'unit', 'user', 'authenticated']),
|
|
25
|
+
subjectId: guid.describe("roles / organization_positions / organization_units / users satır id'si (row_list)"),
|
|
26
|
+
includeDescendants: z.boolean().optional().describe("Yalnız subjectType: 'unit' — alt birimleri de kapsar"),
|
|
27
|
+
startsAt: z.string().optional().describe('ISO tarih; verilmezse şimdi'),
|
|
28
|
+
endsAt: z.string().optional().describe('ISO tarih; startsAt\'ten sonra'),
|
|
29
|
+
overridesDeny: z.boolean().optional().describe("Yalnız subjectType: 'user' ve dolu endsAt ile; kapsamlı/kapsamsız bütün deny'leri aşar; yazmak access.override.manage izni ister — 403 alırsanız izni olan hesapla tekrarlayın")
|
|
30
|
+
});
|
|
31
|
+
function compact(value) {
|
|
32
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
|
|
33
|
+
}
|
|
34
|
+
function grantFromRecord(record) {
|
|
35
|
+
return compact({
|
|
36
|
+
id: record.id,
|
|
37
|
+
permission: String(record.permission ?? ''),
|
|
38
|
+
resourceKey: (record.resourceKey ?? undefined),
|
|
39
|
+
effect: record.effect,
|
|
40
|
+
scope: (record.scope ?? undefined),
|
|
41
|
+
subordinatesLine: (record.subordinatesLine ?? undefined),
|
|
42
|
+
fieldAccess: (record.fieldAccess ?? undefined),
|
|
43
|
+
condition: (record.condition ?? undefined)
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function assignmentFromRecord(record) {
|
|
47
|
+
return compact({
|
|
48
|
+
id: record.id,
|
|
49
|
+
subjectType: record.subjectType,
|
|
50
|
+
subjectId: String(record.subjectId ?? ''),
|
|
51
|
+
includeDescendants: (record.includeDescendants ?? undefined),
|
|
52
|
+
startsAt: (record.startsAt ?? undefined),
|
|
53
|
+
endsAt: (record.endsAt ?? undefined),
|
|
54
|
+
overridesDeny: (record.overridesDeny ?? undefined)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function lintPolicy(platform, project, environment, body, operation, policyOverride, entityBody) {
|
|
58
|
+
const config = platform.config;
|
|
59
|
+
const resolver = new RefResolver(platform, project, environment);
|
|
60
|
+
return [
|
|
61
|
+
...enforcePolicy(evaluatePolicy(config, policyRules, 'accessPolicy', body, { operation }, policyOverride)),
|
|
62
|
+
...enforcePolicy(applyPolicyMode(config, await accessPolicyEntityFindings(platform, resolver, entityBody), policyOverride))
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
export function registerAccessPolicyTools(server, platform) {
|
|
66
|
+
const config = platform.config;
|
|
67
|
+
server.registerTool('access_permission_list', {
|
|
68
|
+
title: 'İzin kataloğunu listele',
|
|
69
|
+
description: "Kompleks projenin izin kataloğunu döner (slug · resourceType · action · supportsScope · supportsFieldAccess) — grant'lerin permission değerinin tek kaynağı. Katalog YALNIZ kompleks projede tohumlanır: boş dizi projenin basit olduğunu söyler.",
|
|
70
|
+
inputSchema: { project: projectParam, environment: environmentParam },
|
|
71
|
+
annotations: readOnly
|
|
72
|
+
}, handle(config, async ({ project, environment }) => {
|
|
73
|
+
const payload = await platform.tenant('GET', project, '/access/permissions', { environment });
|
|
74
|
+
const count = Array.isArray(payload?.data) ? payload.data.length : 0;
|
|
75
|
+
return count > 0
|
|
76
|
+
? payload
|
|
77
|
+
: { ...payload, data: [], complex: false, note: "İzin kataloğu boş: proje KOMPLEKS DEĞİL (katalog yalnız accessModel 'complex' projede tohumlanır). project_get accessModel ile doğrulayın; basit projede yetki middleware ve page.roleIds ile kurulur." };
|
|
78
|
+
}));
|
|
79
|
+
server.registerTool('access_resource_list', {
|
|
80
|
+
title: 'Kaynak kataloğunu listele',
|
|
81
|
+
description: "Grant'in bağlanabileceği kaynakları (entity, alan, microservice, sayfa …) sayfalı listeler; 'key' grant'in resourceKey'idir (K-31). Türetilmiş alanlar katalogdadır (isDerived), gizli sistem alanları değildir; isProtected korumalı alanı işaretler. parentId bir KATALOG id'sidir (listedeki 'id'), entity id değil — bir entity'nin alanları için önce entity satırının id'sini alın.",
|
|
82
|
+
inputSchema: {
|
|
83
|
+
project: projectParam,
|
|
84
|
+
environment: environmentParam,
|
|
85
|
+
type: z.enum(accessResourceTypes).optional(),
|
|
86
|
+
parentId: guid.optional().describe("Katalog satırının id'si: o kaynak ve doğrudan çocukları"),
|
|
87
|
+
search: z.string().optional(),
|
|
88
|
+
pageIndex: z.number().int().min(0).optional(),
|
|
89
|
+
pageLength: z.number().int().min(1).max(1000).optional().describe('Platform varsayılanı 200')
|
|
90
|
+
},
|
|
91
|
+
annotations: readOnly
|
|
92
|
+
}, handle(config, ({ project, environment, ...query }) => platform.tenant('GET', project, '/access/resources', { environment, query })));
|
|
93
|
+
server.registerTool('access_policy_list', {
|
|
94
|
+
title: 'Erişim politikalarını listele',
|
|
95
|
+
description: "Projenin erişim politikalarını grant ve atamalarıyla listeler. origin: system · project · runtime (dikey çözümün production'da ayrıştırdığı, deployment dokunmaz) · module; orphaned: true grant'in kaynağı artık yok.",
|
|
96
|
+
inputSchema: { project: projectParam, environment: environmentParam },
|
|
97
|
+
annotations: readOnly
|
|
98
|
+
}, handle(config, ({ project, environment }) => platform.tenant('GET', project, '/access/policies', { environment })));
|
|
99
|
+
server.registerTool('access_policy_get', {
|
|
100
|
+
title: 'Erişim politikasını getir',
|
|
101
|
+
description: 'Tek politikayı grant ve atamalarıyla getirir; access_policy_update için şablondur.',
|
|
102
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid },
|
|
103
|
+
annotations: readOnly
|
|
104
|
+
}, handle(config, ({ project, environment, id }) => platform.tenant('GET', project, `/access/policies/${id}`, { environment })));
|
|
105
|
+
server.registerTool('access_policy_create', {
|
|
106
|
+
title: 'Erişim politikası oluştur',
|
|
107
|
+
description: "Kompleks projede politika açar: grant'ler (izin × kaynak × etki × kapsam/koşul/alan erişimi) ve atamalar (rol · pozisyon · birim · kullanıcı) tek gövdede. Gövde istek gitmeden ACC-001 (grant/atama biçimi), ACC-002 (koşulda korumalı alan), ACC-003 (sahiplik alanı olmayan entity'ye kapsam, warn) ve ACC-017 (korumasız alana field.read 'masked' — maske alanın entity_protection_update korumasından gelir, korumasız alanda değer düz döner; warn) kontrolünden geçer. Tasarım eseridir: development'ta (ya da dalda) yazılır, production'a deployment taşır. Kavramlar ve CZA örneği: platform_guide('access').",
|
|
108
|
+
inputSchema: {
|
|
109
|
+
project: projectParam,
|
|
110
|
+
environment: environmentParam,
|
|
111
|
+
name: z.string().min(1).max(150),
|
|
112
|
+
slug: z.string().min(1).max(150).describe('Projede tekil (409 AccessPolicySlugDuplicate)'),
|
|
113
|
+
description: z.string().optional(),
|
|
114
|
+
grants: z.array(grantInput).optional().describe('Verilmezse boş'),
|
|
115
|
+
assignments: z.array(assignmentInput).optional().describe('Verilmezse boş — atamasız politika kimseye uygulanmaz'),
|
|
116
|
+
policyOverride: policyOverrideParam
|
|
117
|
+
},
|
|
118
|
+
annotations: writes
|
|
119
|
+
}, handle(config, async ({ project, environment, name, slug, description, grants, assignments, policyOverride }) => {
|
|
120
|
+
const body = compact({ name, slug, description, grants: (grants ?? []).map(compact), assignments: (assignments ?? []).map(compact) });
|
|
121
|
+
const findings = await lintPolicy(platform, project, environment, body, 'create', policyOverride, body);
|
|
122
|
+
const drift = unmappedMembers('CreateAccessPolicyCommand', body);
|
|
123
|
+
const payload = await platform.tenant('POST', project, '/access/policies', { environment, body });
|
|
124
|
+
return attachFindings(withUnmappedMembers(payload, drift), findings);
|
|
125
|
+
}));
|
|
126
|
+
server.registerTool('access_policy_update', {
|
|
127
|
+
title: 'Erişim politikasını güncelle',
|
|
128
|
+
description: "Politikayı günceller (platform ucu tam değiştirmedir). Gateway kaydı önce okur: VERİLMEYEN alan mevcut değeriyle taşınır ve bu gatewayNormalized ile söylenir. grants / assignments VERİLİRSE TAM LİSTEDİR — listede olmayan grant ya da atama SİLİNİR; var olanı değiştirmek için id'sini koruyun. Yetim anahtarlı var olan grant PUT'u düşürmez, yeni gelen bilinmeyen anahtar 400 AccessResourceUnknown. Aynı ACC-001…003 ve ACC-017 kontrolü (yalnız bu çağrıda verilen grant'lere) uygulanır.",
|
|
129
|
+
inputSchema: {
|
|
130
|
+
project: projectParam,
|
|
131
|
+
environment: environmentParam,
|
|
132
|
+
id: guid,
|
|
133
|
+
name: z.string().min(1).max(150).optional(),
|
|
134
|
+
slug: z.string().min(1).max(150).optional(),
|
|
135
|
+
description: z.string().optional(),
|
|
136
|
+
grants: z.array(grantInput).optional().describe('TAM liste; verilmezse mevcut grant\'ler taşınır'),
|
|
137
|
+
assignments: z.array(assignmentInput).optional().describe('TAM liste; verilmezse mevcut atamalar taşınır'),
|
|
138
|
+
policyOverride: policyOverrideParam
|
|
139
|
+
},
|
|
140
|
+
annotations: updates
|
|
141
|
+
}, handle(config, async ({ project, environment, id, name, slug, description, grants, assignments, policyOverride }) => {
|
|
142
|
+
const current = ((await platform.tenant('GET', project, `/access/policies/${id}`, { environment }))?.data ?? {});
|
|
143
|
+
const carried = [
|
|
144
|
+
...(name === undefined ? ['name'] : []),
|
|
145
|
+
...(slug === undefined ? ['slug'] : []),
|
|
146
|
+
...(description === undefined && typeof current.description === 'string' ? ['description'] : []),
|
|
147
|
+
...(grants === undefined ? ['grants'] : []),
|
|
148
|
+
...(assignments === undefined ? ['assignments'] : [])
|
|
149
|
+
];
|
|
150
|
+
const body = compact({
|
|
151
|
+
name: name ?? String(current.name ?? ''),
|
|
152
|
+
slug: slug ?? String(current.slug ?? ''),
|
|
153
|
+
description: description ?? (typeof current.description === 'string' ? current.description : undefined),
|
|
154
|
+
grants: grants !== undefined ? grants.map(compact) : (Array.isArray(current.grants) ? current.grants : []).map(entry => grantFromRecord(entry)),
|
|
155
|
+
assignments: assignments !== undefined ? assignments.map(compact) : (Array.isArray(current.assignments) ? current.assignments : []).map(entry => assignmentFromRecord(entry))
|
|
156
|
+
});
|
|
157
|
+
// Taşınan grant'ler platformda zaten geçerli; entity okuması yalnız bu çağrıda VERİLEN grant'ler için yapılır.
|
|
158
|
+
const findings = await lintPolicy(platform, project, environment, body, 'update', policyOverride, { grants: grants ?? [] });
|
|
159
|
+
const drift = unmappedMembers('UpdateAccessPolicyCommand', body);
|
|
160
|
+
const payload = await platform.tenant('PUT', project, `/access/policies/${id}`, { environment, body });
|
|
161
|
+
const notes = carried.length > 0 ? [`Verilmeyen alanlar mevcut kayıttan taşındı: ${carried.join(', ')}.`] : [];
|
|
162
|
+
return attachFindings(withUnmappedMembers(withNormalization(payload, notes), drift), findings);
|
|
163
|
+
}));
|
|
164
|
+
server.registerTool('access_policy_delete', {
|
|
165
|
+
title: 'Erişim politikasını sil',
|
|
166
|
+
description: 'Politikayı grant ve atamalarıyla birlikte siler; etkisi erişim sürümü artınca runtime kararlarına yansır.',
|
|
167
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid },
|
|
168
|
+
annotations: destructive
|
|
169
|
+
}, handle(config, ({ project, environment, id }) => platform.tenant('DELETE', project, `/access/policies/${id}`, { environment })));
|
|
170
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { guid, projectParam, environmentParam, readOnly, writes } from './common.js';
|
|
2
|
+
import { identityHeaders } from '../platform/tokenVault.js';
|
|
3
|
+
import { resourceKeyProblem } from '../knowledge/access.js';
|
|
4
|
+
import { enforcePolicy, attachFindings, applyPolicyMode } from '../knowledge/policy.js';
|
|
5
|
+
import { PlatformApiError } from '../platform/errors.js';
|
|
6
|
+
import { handle } from '../platform/responses.js';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
function asRecord(value) {
|
|
9
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
10
|
+
}
|
|
11
|
+
function previewNote(data) {
|
|
12
|
+
const trace = Array.isArray(data.trace) ? data.trace.map(asRecord) : [];
|
|
13
|
+
const rowScoped = trace.some(entry => entry.outcome === 'row-scoped')
|
|
14
|
+
? " Kapsamlı deny satır süzer, eylemi kesmez — rowScope metnindeki 'except' parçasına bakın."
|
|
15
|
+
: '';
|
|
16
|
+
if (data.reason === 'allowed-by-override') {
|
|
17
|
+
const endsAt = trace.find(entry => entry.overridesDeny === true && typeof entry.endsAt === 'string')?.endsAt;
|
|
18
|
+
const scope = typeof data.rowScope === 'string' && data.rowScope !== ''
|
|
19
|
+
? ` Satırlar yine şu kapsamla süzülür: ${data.rowScope} — istisna yasağı kaldırır, öteki izinleri daraltmaz.`
|
|
20
|
+
: '';
|
|
21
|
+
return `Kullanıcı istisnası deny'leri aştı; iz 'outcome: overridden' satırlarını gösterir${typeof endsAt === 'string' ? `, bitiş ${endsAt}` : ''}.${scope}${rowScoped}`;
|
|
22
|
+
}
|
|
23
|
+
if (data.isAllowed === true) {
|
|
24
|
+
return (typeof data.rowScope === 'string' && data.rowScope !== ''
|
|
25
|
+
? `İzin var; satırlar şu kapsamla süzülür: ${data.rowScope}. Kararı getiren grant'ler trace'te.`
|
|
26
|
+
: "İzin var. Kararı getiren grant'ler trace'te.") + rowScoped;
|
|
27
|
+
}
|
|
28
|
+
const cuttingDenies = trace.filter(entry => entry.effect === 'deny' && entry.outcome !== 'row-scoped');
|
|
29
|
+
if (cuttingDenies.length > 0) {
|
|
30
|
+
return cuttingDenies.every(entry => entry.scope === 'all')
|
|
31
|
+
? "scope: 'all' taşıyan koşulsuz bir deny grant'i kesiyor (trace'te effect 'deny', outcome 'applied'): koşulsuz 'all' daraltıcı bir kapsam değildir, bu yüzden kapsamsız deny gibi bütün allow'ları ezer (TB-132). Yalnız belli satırları dışlamak istiyorsanız deny'a own · unit · unit-tree · subordinates · function · company kapsamlarından birini ya da 'all' ile birlikte bir koşul verin."
|
|
32
|
+
: "Kapsamsız bir deny grant'i kesiyor (trace'te effect 'deny', outcome 'applied'): kapsamsız deny bütün allow'ları ezer.";
|
|
33
|
+
}
|
|
34
|
+
return (trace.length === 0
|
|
35
|
+
? "Bu kullanıcıya bu izni getiren hiçbir atama yok. Kullanıcının pozisyonunu/rolünü (row_list('user_positions'), user_list) ve politikanın atamasını (access_policy_get) kontrol edin; kaynak anahtarı grant'teki anahtarla aynı mı bakın."
|
|
36
|
+
: "Atama var ama eşleşen allow grant'i yok (izin ya da kaynak anahtarı farklı).") + rowScoped;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The shortest reveal reason THIS environment accepts. A refused attempt is written to the audit
|
|
40
|
+
* trail and eats the rate limit, so the gateway reads the setting and stops a short reason before
|
|
41
|
+
* it becomes a record (ACC-010). It is read off the runtime access map rather than off the core
|
|
42
|
+
* project row: the platform enforces the environment's own copy, and the two can differ while an
|
|
43
|
+
* environment has not picked the change up. An unreadable map is treated as "no minimum" — the
|
|
44
|
+
* rule must never turn a working reveal into a gateway error.
|
|
45
|
+
*/
|
|
46
|
+
async function revealReasonMinLength(platform, project, environment, application, asUser) {
|
|
47
|
+
try {
|
|
48
|
+
const payload = await platform.tenant('GET', project, `/runtime/${encodeURIComponent(application)}/access`, {
|
|
49
|
+
environment,
|
|
50
|
+
headers: identityHeaders(asUser, undefined, undefined)
|
|
51
|
+
});
|
|
52
|
+
const minLength = Number(asRecord(payload?.data).revealReasonMinLength);
|
|
53
|
+
return Number.isFinite(minLength) && minLength > 0 ? minLength : 0;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const asUserRefParam = z.string().min(1).describe("Uygulama kullanıcısının kimliği: access_session_create yanıtındaki tok_… referansı ya da ham JWT. Gateway'in servis jetonu bir uygulama kullanıcısı DEĞİLDİR — bu alan olmadan uç uygulama kullanıcısı göremez.");
|
|
60
|
+
export function registerAccessRuntimeTools(server, platform) {
|
|
61
|
+
const config = platform.config;
|
|
62
|
+
server.registerTool('access_preview', {
|
|
63
|
+
title: 'Erişim kararını açıkla',
|
|
64
|
+
description: "Karar motorunun bir uygulama kullanıcısı için vereceği kararı YAN ETKİSİZ açıklar (POST /{project}/access/explain): isAllowed, reason (allowed · allowed-by-override · no-matching-grant · explicit-deny · system), rowScope (satır kapsamının metni; kapsamlı deny 'X except Y' olarak görünür), fieldAccess (alan başına hidden/masked/plain · canWrite · canFilter) ve trace (atama → politika → grant → etki + scope · outcome 'applied'|'row-scoped'|'overridden' · endsAt · overridesDeny). Veri döndürmez, zorlama modunu okumaz ya da değiştirmez. 'Bu kullanıcı neden göremiyor / güncelleyemiyor?' ve gölge moddaki 'reddedilirdi' kayıtlarının cevabı budur. Kavramlar: platform_guide('access') §'Açıklama ve sınama'.",
|
|
65
|
+
inputSchema: {
|
|
66
|
+
project: projectParam,
|
|
67
|
+
environment: environmentParam,
|
|
68
|
+
userId: guid.describe("Uygulama kullanıcısının id'si (users tablosu / user_list)"),
|
|
69
|
+
permission: z.string().min(1).describe("İzin slug'ı (access_permission_list): 'entity.update', 'microservice.execute' …"),
|
|
70
|
+
resourceKey: z.string().min(1).describe("'entity:<entityId>' · 'field:<entityId>:<fieldId>' · 'row:<entityId>:<rowId>' · '<tip>:<id>' ya da yalnız tip ('page')"),
|
|
71
|
+
rowId: z.string().min(1).optional().describe("Satır kapsamını tek bir satır için sınar: satırın cevabı rowInScope ve summary.row'dadır, isAllowed tablonun cevabı olarak kalır")
|
|
72
|
+
},
|
|
73
|
+
annotations: readOnly
|
|
74
|
+
}, handle(config, async ({ project, environment, userId, permission, resourceKey, rowId }) => {
|
|
75
|
+
const problem = resourceKeyProblem(resourceKey);
|
|
76
|
+
if (problem) {
|
|
77
|
+
throw new PlatformApiError(`access_preview: ${problem}. Anahtarları access_resource_list verir; istek gönderilmedi.`, 400);
|
|
78
|
+
}
|
|
79
|
+
const payload = await platform.tenant('POST', project, '/access/explain', { environment, body: { userId, permission, resourceKey, ...(rowId ? { rowId } : {}) } });
|
|
80
|
+
const data = asRecord(payload?.data);
|
|
81
|
+
return {
|
|
82
|
+
...payload,
|
|
83
|
+
summary: {
|
|
84
|
+
decision: data.isAllowed === true ? 'allow' : 'deny',
|
|
85
|
+
...(typeof data.reason === 'string' ? { reason: data.reason } : {}),
|
|
86
|
+
...(typeof data.rowScope === 'string' ? { rowScope: data.rowScope } : {}),
|
|
87
|
+
traceEntries: Array.isArray(data.trace) ? data.trace.length : 0,
|
|
88
|
+
...(typeof data.rowInScope === 'boolean' ? { row: data.rowInScope ? 'in-scope' : 'out-of-scope' } : {})
|
|
89
|
+
},
|
|
90
|
+
note: data.isAllowed === true && data.rowInScope === false
|
|
91
|
+
? `Tabloda izin var ama bu satır kapsam dışında (${typeof data.rowScope === 'string' ? data.rowScope : 'satır kapsamı'}): çalışma zamanında okuma/güncelleme 404 döner. isAllowed tablonun, rowInScope satırın cevabıdır.`
|
|
92
|
+
: previewNote(data)
|
|
93
|
+
};
|
|
94
|
+
}));
|
|
95
|
+
server.registerTool('access_session_create', {
|
|
96
|
+
title: 'Uygulama oturumu aç',
|
|
97
|
+
description: "Kompleks projede uygulama kullanıcısı olarak giriş yapar (POST /{project}/runtime/{application}/auth/sign-in) — korumalı uçları ve sayfaları O KULLANICININ kimliğiyle sınamanın yolu. Erişim jetonu yanıtta maskelenir; tokenRefs'teki tok_… referansını microservice_execute / microservice_debug / page_runtime_get 'asUser' alanına verin (15 dk). Jeton Bearer olarak gider ve platform Bearer'lı isteğe CSRF sormaz; oturum çerezleri gateway'de tutulmaz. Hatalar: 401 AppSessionInvalid (yanlış e-posta/parola), 429 (5 dk'da 10 deneme — tekrar denemeden önce bekleyin). Basit projede giriş uygulamanın kendi microservice ucudur (platform_guide('middleware')).",
|
|
98
|
+
inputSchema: {
|
|
99
|
+
project: projectParam,
|
|
100
|
+
environment: environmentParam,
|
|
101
|
+
application: z.string().min(1).describe("Application slug'ı"),
|
|
102
|
+
email: z.string().min(1),
|
|
103
|
+
password: z.string().min(1).describe('Uygulama kullanıcısının parolası; gateway günlüğüne maskeli yazılır, yanıta girmez'),
|
|
104
|
+
expectFailure: z.boolean().optional().describe('true: hatalı giriş BEKLENİYOR (kasıtlı negatif test)')
|
|
105
|
+
},
|
|
106
|
+
annotations: writes
|
|
107
|
+
}, handle(config, async ({ project, environment, application, email, password }) => {
|
|
108
|
+
// anonymous: gateway kendi jetonunu eklemez ve 401'de jeton yenileyip İKİNCİ bir giriş denemesi
|
|
109
|
+
// yapmaz — yanlış parolanın iki kez sayılması hız sınırını (5 dk'da 10) boşuna tüketirdi.
|
|
110
|
+
const response = await platform.tenantRaw('POST', project, `/runtime/${encodeURIComponent(application)}/auth/sign-in`, { environment, body: { email, password }, anonymous: true });
|
|
111
|
+
const body = asRecord(response.body);
|
|
112
|
+
const data = asRecord(body.data);
|
|
113
|
+
const ok = response.httpStatus < 400;
|
|
114
|
+
const code = typeof body.code === 'string' ? body.code : undefined;
|
|
115
|
+
return {
|
|
116
|
+
httpStatus: response.httpStatus,
|
|
117
|
+
body: response.body,
|
|
118
|
+
...(ok ? { session: { user: data.user, accessExpiresAt: data.accessTokenExpiresAt, refreshExpiresAt: data.refreshTokenExpiresAt } } : {}),
|
|
119
|
+
note: ok
|
|
120
|
+
? "Oturum açıldı. tokenRefs içindeki 'body.data.accessToken' referansını asUser olarak verin; 15 dk sonra yeniden giriş gerekir. Kullanıcının yetkisi atamalarından gelir — erişemediği bir şey için access_preview."
|
|
121
|
+
: code === 'AppSessionRateLimitedException'
|
|
122
|
+
? 'Giriş hız sınırına takıldı (5 dk\'da 10 deneme). Beklemeden tekrar denemeyin.'
|
|
123
|
+
: code === 'AppSignInFailedException'
|
|
124
|
+
? "E-posta ya da parola yanlış (ya da kullanıcı bu projenin 'users' tablosunda yok). Yeni projede users boş gelir — platform_guide('getting-started') §'Kompleks projede ilk kullanıcı'."
|
|
125
|
+
: 'Giriş reddedildi; gövdedeki code/message nedeni söyler.'
|
|
126
|
+
};
|
|
127
|
+
}));
|
|
128
|
+
server.registerTool('access_runtime_get', {
|
|
129
|
+
title: 'Uygulama kullanıcısının erişim haritası',
|
|
130
|
+
description: "Giriş yapmış bir uygulama kullanıcısının O ANDAKİ erişim haritasını getirir (GET /{project}/runtime/{application}/access): zorlama modu, görebildiği sayfa slug'ları, çağırabildiği microservice slug'ları ve entity başına eylemler (read · create · update · delete …) + alan erişimleri (hidden/masked/plain · canWrite · canFilter). Uygulama menüsünü/butonlarını buna göre çizer. access_preview'dan farkı: o TEK bir kararı açıklar, bu kullanıcının bütün yüzeyini verir. asUser ZORUNLUDUR — bu uç gateway'in servis jetonuyla çağrılırsa uygulama kullanıcısı yoktur ve harita boş döner.",
|
|
131
|
+
inputSchema: {
|
|
132
|
+
project: projectParam,
|
|
133
|
+
environment: environmentParam,
|
|
134
|
+
application: z.string().min(1).describe("Application slug'ı"),
|
|
135
|
+
asUser: asUserRefParam
|
|
136
|
+
},
|
|
137
|
+
annotations: readOnly
|
|
138
|
+
}, handle(config, async ({ project, environment, application, asUser }) => {
|
|
139
|
+
const payload = await platform.tenant('GET', project, `/runtime/${encodeURIComponent(application)}/access`, { environment, headers: identityHeaders(asUser, undefined, undefined) });
|
|
140
|
+
const data = asRecord(payload?.data);
|
|
141
|
+
return {
|
|
142
|
+
...payload,
|
|
143
|
+
summary: {
|
|
144
|
+
mode: data.mode,
|
|
145
|
+
pages: Array.isArray(data.pages) ? data.pages.length : 0,
|
|
146
|
+
microservices: Array.isArray(data.microservices) ? data.microservices.length : 0,
|
|
147
|
+
entities: Array.isArray(data.entities) ? data.entities.length : 0
|
|
148
|
+
},
|
|
149
|
+
note: data.mode === 'off'
|
|
150
|
+
? "Zorlama KAPALI: harita her şeyi açık gösterir çünkü karar motoru çalışmıyor. Gerçek kapsamı görmek için environment'ı shadow/enforce'a alın (access_enforcement_mode_set)."
|
|
151
|
+
: "Harita bu kullanıcının o andaki yetkisidir; bir öğe eksikse nedenini access_preview söyler."
|
|
152
|
+
};
|
|
153
|
+
}));
|
|
154
|
+
server.registerTool('access_reveal', {
|
|
155
|
+
title: 'Maskeli alan değerini aç',
|
|
156
|
+
description: "Maskelenmiş (korumalı) bir alanın açık değerini TEK BİR SATIR için okur (POST /{project}/runtime/{application}/reveal). Kullanıcının o alanda 'field.reveal' izni olmalıdır; her çağrı GEREKÇESİYLE BİRLİKTE denetim izine yazılır ve hız sınırına tabidir (429). Maskeli değerin açığını okumanın başka yolu yoktur — row_list korumalı alanı maskeli döner. asUser zorunludur: gerekçe ve iz o kullanıcıya yazılır. Projenin revealReasonMinLength ayarı 0'dan büyükse daha kısa bir gerekçe 400 RevealReasonTooShort alır, hız sınırını tüketir ve denetim defterine REVEAL-DENIED düşer — gateway bunu çağrı gitmeden durdurur (ACC-010).",
|
|
157
|
+
inputSchema: {
|
|
158
|
+
project: projectParam,
|
|
159
|
+
environment: environmentParam,
|
|
160
|
+
application: z.string().min(1).describe("Application slug'ı"),
|
|
161
|
+
asUser: asUserRefParam,
|
|
162
|
+
entityId: guid,
|
|
163
|
+
rowId: z.string().min(1).describe("Satırın birincil anahtar değeri"),
|
|
164
|
+
fieldSlug: z.string().min(1).describe("Alanın slug'ı (entity_get)"),
|
|
165
|
+
reason: z.string().min(1).max(1000).describe("Denetim izine yazılacak gerekçe; zorunludur ve boş bırakılamaz. Projenin revealReasonMinLength ayarından kısa olamaz (project_get / access_runtime_get)"),
|
|
166
|
+
expectFailure: z.boolean().optional().describe('true: reddedilmesi BEKLENİYOR (kasıtlı negatif test) — ACC-010 ön denetimi de atlanır')
|
|
167
|
+
},
|
|
168
|
+
annotations: writes
|
|
169
|
+
}, handle(config, async ({ project, environment, application, asUser, entityId, rowId, fieldSlug, reason, expectFailure }) => {
|
|
170
|
+
let findings = [];
|
|
171
|
+
if (expectFailure !== true && config.policyMode !== 'off') {
|
|
172
|
+
const minLength = await revealReasonMinLength(platform, project, environment, application, asUser);
|
|
173
|
+
if (reason.trim().length < minLength) {
|
|
174
|
+
findings = enforcePolicy(applyPolicyMode(config, [{
|
|
175
|
+
ruleId: 'ACC-010',
|
|
176
|
+
severity: 'block',
|
|
177
|
+
message: `Gerekçe ${reason.trim().length} karakter, bu ortam en az ${minLength} istiyor (access_runtime_get → revealReasonMinLength). Platform bunu 400 RevealReasonTooShort ile reddeder, hız sınırını tüketir ve denetim defterine REVEAL-DENIED yazar — istek gönderilmedi. Kasıtlı olumsuz ölçüm yapıyorsanız expectFailure: true verin.`,
|
|
178
|
+
guide: 'ngapps://guides/access'
|
|
179
|
+
}]), false);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const response = await platform.tenantRaw('POST', project, `/runtime/${encodeURIComponent(application)}/reveal`, {
|
|
183
|
+
environment,
|
|
184
|
+
body: { entityId, rowId, fieldSlug, reason },
|
|
185
|
+
headers: identityHeaders(asUser, undefined, undefined)
|
|
186
|
+
});
|
|
187
|
+
const body = asRecord(response.body);
|
|
188
|
+
const code = typeof body.code === 'string' ? body.code : undefined;
|
|
189
|
+
return attachFindings({
|
|
190
|
+
httpStatus: response.httpStatus,
|
|
191
|
+
body: response.body,
|
|
192
|
+
note: response.httpStatus < 400
|
|
193
|
+
? 'Değer açıldı ve çağrı denetim izine gerekçesiyle yazıldı.'
|
|
194
|
+
: code === 'AccessRevealDeniedException'
|
|
195
|
+
? "Kullanıcının bu alanda 'field.reveal' izni yok: grant'i access_matrix_get ile kontrol edin, kararı access_preview açıklar."
|
|
196
|
+
: typeof body.message === 'string' && body.message.includes('RevealReasonTooShort')
|
|
197
|
+
? 'Gerekçe projenin asgari uzunluğunun altında (project_get → revealReasonMinLength); deneme REVEAL-DENIED olarak kaydedildi ve hız sınırını tüketti.'
|
|
198
|
+
: response.httpStatus === 429
|
|
199
|
+
? 'Açma hız sınırına takıldı; beklemeden tekrar denemeyin.'
|
|
200
|
+
: 'Açma reddedildi; gövdedeki code/message nedeni söyler.'
|
|
201
|
+
}, findings);
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
import { guid, pagingParams, readOnly, writes, updates, destructive } from './common.js';
|
|
2
2
|
import { handle } from '../platform/responses.js';
|
|
3
|
+
import { forgetApplication, readApplicationPlatform, resolveApplication } from '../knowledge/platform/resolve.js';
|
|
4
|
+
import { mobileTabsProblems, readMobileTabs } from '../knowledge/platform/mobileTabs.js';
|
|
5
|
+
import { findingLine } from '../knowledge/platform/profile.js';
|
|
6
|
+
import { listComponents } from './components.js';
|
|
3
7
|
import { z } from 'zod';
|
|
8
|
+
/**
|
|
9
|
+
* MOB-007 (sekme ayarı) — `properties.mobile.tabs` yazan mobil uygulama çağrısının denetimi. Yazım
|
|
10
|
+
* platforma YİNE gider (D3; backend `properties`'i doğrulamaz): bulgu `platformWarnings`'e düşer.
|
|
11
|
+
* Sayfa ve widget listesi okunamazsa o denetimler atlanır, biçim denetimi yine koşar. Widget listesi
|
|
12
|
+
* `listComponents`'tan gelir: uç `type` verilmezse yalnız web bileşenlerini döner ve mobil widget'ı
|
|
13
|
+
* gösteren sekme ayarı yanlış MOB-007 alırdı (ARC-245).
|
|
14
|
+
*/
|
|
15
|
+
export async function mobileTabsWarnings(platform, applicationId, projectSlug, homepageId, properties) {
|
|
16
|
+
const tabs = readMobileTabs(properties);
|
|
17
|
+
if (tabs === undefined)
|
|
18
|
+
return [];
|
|
19
|
+
const pages = applicationId && projectSlug
|
|
20
|
+
? await platform.tenant('GET', projectSlug, `/pages/${applicationId}`, { query: { pageIndex: 0, pageLength: 200 } })
|
|
21
|
+
.then(response => (response?.data ?? []).map(page => ({ id: String(page.id ?? '') })))
|
|
22
|
+
.catch(() => undefined)
|
|
23
|
+
: undefined;
|
|
24
|
+
const widgetIds = applicationId && projectSlug
|
|
25
|
+
? await listComponents(platform, projectSlug, undefined, applicationId)
|
|
26
|
+
.then(components => new Set(components.filter(entry => entry.kind === 'widget').map(entry => String(entry.id ?? ''))))
|
|
27
|
+
.catch(() => undefined)
|
|
28
|
+
: undefined;
|
|
29
|
+
return mobileTabsProblems(tabs, {
|
|
30
|
+
...(pages ? { pages } : {}),
|
|
31
|
+
...(homepageId ? { homepageId } : {}),
|
|
32
|
+
...(widgetIds ? { widgetIds } : {})
|
|
33
|
+
}).map(message => findingLine({
|
|
34
|
+
code: 'MOB-007',
|
|
35
|
+
severity: 'warn',
|
|
36
|
+
message,
|
|
37
|
+
guide: "platform_guide('mobile') §Gezinme"
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
function withPlatformWarnings(response, warnings) {
|
|
41
|
+
if (warnings.length === 0 || !response || typeof response !== 'object' || Array.isArray(response))
|
|
42
|
+
return response;
|
|
43
|
+
return { ...response, platformWarnings: warnings };
|
|
44
|
+
}
|
|
4
45
|
export function registerApplicationTools(server, platform) {
|
|
5
46
|
const config = platform.config;
|
|
6
47
|
server.registerTool('application_list', {
|
|
@@ -34,7 +75,14 @@ export function registerApplicationTools(server, platform) {
|
|
|
34
75
|
notFoundPageId: guid.optional()
|
|
35
76
|
},
|
|
36
77
|
annotations: writes
|
|
37
|
-
}, handle(config, ({ projectId, ...body }) =>
|
|
78
|
+
}, handle(config, async ({ projectId, ...body }) => {
|
|
79
|
+
const warnings = readApplicationPlatform(body.type) === 'mobile'
|
|
80
|
+
? await mobileTabsWarnings(platform, undefined, undefined, undefined, body.properties)
|
|
81
|
+
: [];
|
|
82
|
+
const response = await platform.org('POST', `/applications/${projectId}`, { body });
|
|
83
|
+
forgetApplication(platform);
|
|
84
|
+
return withPlatformWarnings(response, warnings);
|
|
85
|
+
}));
|
|
38
86
|
server.registerTool('application_update', {
|
|
39
87
|
title: 'Application güncelle',
|
|
40
88
|
description: 'Application bilgilerini günceller; homepageId zorunludur (mevcut değeri application_get ile alın).',
|
|
@@ -49,11 +97,23 @@ export function registerApplicationTools(server, platform) {
|
|
|
49
97
|
notFoundPageId: guid.optional()
|
|
50
98
|
},
|
|
51
99
|
annotations: updates
|
|
52
|
-
}, handle(config, ({ id, ...body }) =>
|
|
100
|
+
}, handle(config, async ({ id, ...body }) => {
|
|
101
|
+
const current = await resolveApplication(platform, id);
|
|
102
|
+
const warnings = current?.platform === 'mobile'
|
|
103
|
+
? await mobileTabsWarnings(platform, id, current.projectSlug, body.homepageId, body.properties)
|
|
104
|
+
: [];
|
|
105
|
+
const response = await platform.org('PUT', `/applications/${id}`, { body });
|
|
106
|
+
forgetApplication(platform, id);
|
|
107
|
+
return withPlatformWarnings(response, warnings);
|
|
108
|
+
}));
|
|
53
109
|
server.registerTool('application_delete', {
|
|
54
110
|
title: 'Application sil',
|
|
55
111
|
description: 'Application\'ı kalıcı olarak siler.',
|
|
56
112
|
inputSchema: { id: guid },
|
|
57
113
|
annotations: destructive
|
|
58
|
-
}, handle(config, args =>
|
|
114
|
+
}, handle(config, async (args) => {
|
|
115
|
+
const response = await platform.org('DELETE', `/applications/${args.id}`);
|
|
116
|
+
forgetApplication(platform, args.id);
|
|
117
|
+
return response;
|
|
118
|
+
}));
|
|
59
119
|
}
|