@appaflytech/zeyrek-ai-mcp 0.0.35 → 0.0.37
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 +655 -424
- package/dist/knowledge/componentEvents.js +140 -23
- package/dist/knowledge/connectDataBind.js +843 -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 +59 -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 +864 -0
- package/dist/knowledge/mobileComponentEvents.js +239 -0
- package/dist/knowledge/mobileStore.js +321 -0
- package/dist/knowledge/normalize.js +235 -84
- 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 +423 -37
- package/dist/knowledge/schemaGuard.js +11 -0
- package/dist/knowledge/styleResolve.js +53 -19
- package/dist/knowledge/styleResolveNative.js +527 -0
- package/dist/knowledge/themeResolve.js +4 -4
- package/dist/knowledge/themeSchema.js +1 -2
- package/dist/observability/trace.js +6 -3
- 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 +19 -1
- 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 +93 -6
- 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 +37 -3
- package/dist/resources/guides/microservice.md +87 -2
- package/dist/resources/guides/middleware.md +68 -4
- package/dist/resources/guides/mobile-layout.md +193 -0
- package/dist/resources/guides/mobile-publish.md +124 -0
- package/dist/resources/guides/mobile.md +549 -0
- package/dist/resources/guides/page.md +463 -83
- package/dist/resources/guides/theme.md +8 -3
- 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 +25 -1
- package/dist/resources/schemas/MicroserviceDto.json +15 -3
- package/dist/resources/schemas/OrganizationProjectDto.json +335 -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 +274 -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 +60 -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 +10 -1
- 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/externalComponents.js +26 -5
- package/dist/tools/flows.js +119 -17
- 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 +556 -0
- package/dist/tools/modules.js +48 -0
- package/dist/tools/organizationStructure.js +137 -0
- package/dist/tools/pages.js +465 -98
- 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,280 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { environmentParam, guid, pagingParams, projectParam, readOnly, writes } from './common.js';
|
|
3
|
+
import { fetchHost, pageUpdateBody, unwrap } from './screens.js';
|
|
4
|
+
import { fetchCatalogSlugs } from './components.js';
|
|
5
|
+
import { mobileTabsWarnings } from './applications.js';
|
|
6
|
+
import { PlatformApiError } from '../platform/errors.js';
|
|
7
|
+
import { handle } from '../platform/responses.js';
|
|
8
|
+
import { PAGE_CONTAINER_ELEMENT_ID, ensurePageContainer } from '../knowledge/normalize.js';
|
|
9
|
+
import { mobileProfile, shellLessPageSkeleton } from '../knowledge/platform/mobile.js';
|
|
10
|
+
import { MAX_MOBILE_TABS } from '../knowledge/platform/mobileTabs.js';
|
|
11
|
+
import { forgetApplication } from '../knowledge/platform/resolve.js';
|
|
12
|
+
import { readKnowledgeFile } from '../resources/knowledge.js';
|
|
13
|
+
/** Ana sayfa: açıkça verilen, yoksa ilk sekme, yoksa ilk sayfa. */
|
|
14
|
+
function homepageSlug(input) {
|
|
15
|
+
return input.homepage ?? input.tabs?.[0]?.pageSlug ?? input.pages[0].pageSlug;
|
|
16
|
+
}
|
|
17
|
+
export function mobileAppInputErrors(input) {
|
|
18
|
+
const errors = [];
|
|
19
|
+
const slugs = input.pages.map(page => page.pageSlug);
|
|
20
|
+
const repeated = slugs.filter((slug, index) => slugs.indexOf(slug) !== index);
|
|
21
|
+
if (input.pages.length === 0)
|
|
22
|
+
errors.push('pages en az bir sayfa ister');
|
|
23
|
+
if (repeated.length > 0)
|
|
24
|
+
errors.push(`pages içinde tekrar eden slug var: ${[...new Set(repeated)].join(', ')}`);
|
|
25
|
+
if (input.tabs !== undefined) {
|
|
26
|
+
const tabSlugs = input.tabs.map(tab => tab.pageSlug);
|
|
27
|
+
if (input.tabs.length === 0)
|
|
28
|
+
errors.push("tabs verildiyse en az bir öğe taşır; sekme istemiyorsan alanı hiç gönderme (yığın)");
|
|
29
|
+
if (input.tabs.length > MAX_MOBILE_TABS)
|
|
30
|
+
errors.push(`tabs en fazla ${MAX_MOBILE_TABS} öğe taşır (${input.tabs.length} verildi) — fazlası yığına açılan sayfa olarak kalmalı`);
|
|
31
|
+
for (const slug of tabSlugs.filter(slug => !slugs.includes(slug)))
|
|
32
|
+
errors.push(`tabs '${slug}' pages içinde yok — sekme yalnız kurulan bir sayfayı gösterebilir`);
|
|
33
|
+
if (tabSlugs.some((slug, index) => tabSlugs.indexOf(slug) !== index))
|
|
34
|
+
errors.push('tabs içinde tekrar eden sayfa var');
|
|
35
|
+
}
|
|
36
|
+
if (input.homepage !== undefined && !slugs.includes(input.homepage))
|
|
37
|
+
errors.push(`homepage '${input.homepage}' pages içinde yok`);
|
|
38
|
+
return errors;
|
|
39
|
+
}
|
|
40
|
+
export function buildMobileAppPlan(input) {
|
|
41
|
+
const home = homepageSlug(input);
|
|
42
|
+
const others = input.pages.filter(page => page.pageSlug !== home);
|
|
43
|
+
return [
|
|
44
|
+
{ step: 'application_create', tool: 'application_create', summary: "type:'mobile' uygulama; backend ana sayfayı kendisi açar" },
|
|
45
|
+
{ step: 'layout_create', tool: 'layout_create', summary: `kabuk: app-bar + slot${input.drawer ? ' + drawer widget (app-bar.menuTarget)' : ''}` },
|
|
46
|
+
...others.map((page, index) => ({ step: `page_create#${index + 1}`, tool: 'page_create', summary: `'${page.pageSlug}' (${page.pageName}), layoutId ile` })),
|
|
47
|
+
{ step: 'page_update#homepage', tool: 'page_update', summary: `backend'in ana sayfası → '${home}' (ad, slug, layoutId)` },
|
|
48
|
+
...(input.tabs ? [{ step: 'application_update', tool: 'application_update', summary: `properties.mobile.tabs: ${input.tabs.map(tab => tab.pageSlug).join(' · ')}` }] : [])
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
/** Kabuk ağacı örnek dosyasından okunur — örnek ile aracın kurduğu layout tek kaynaktır. */
|
|
52
|
+
function shellViews(input) {
|
|
53
|
+
const example = JSON.parse(readKnowledgeFile('examples', input.drawer ? 'mobile-app-shell-drawer.json' : 'mobile-app-shell-stack.json'));
|
|
54
|
+
const views = structuredClone(example.layoutCreate.views);
|
|
55
|
+
// app-bar kabukta üst güvenli alanın içindedir (mizanpaj S1); ağaçta derinlikten bağımsız aranır.
|
|
56
|
+
const findAppBar = (nodes) => nodes.find(node => node.type === 'app-bar') ?? nodes.map(node => findAppBar(node.children ?? [])).find(Boolean);
|
|
57
|
+
const appBar = findAppBar(views);
|
|
58
|
+
if (appBar && input.title)
|
|
59
|
+
appBar.props.title = { type: 'static', ref: null, data: input.title };
|
|
60
|
+
return views;
|
|
61
|
+
}
|
|
62
|
+
const SHELL_TYPES = ['app-bar', 'slot'];
|
|
63
|
+
/** Boş sayfanın şeması: kabuklu sayfada kök şablonu, kabuksuzda iskeletiyle (üst pay + kenar boşluğu). */
|
|
64
|
+
function mobilePageSchema(shell) {
|
|
65
|
+
const schema = ensurePageContainer([], mobileProfile.layout.rootContainer).schema;
|
|
66
|
+
if (!shell && schema[0])
|
|
67
|
+
shellLessPageSkeleton(schema[0]);
|
|
68
|
+
return schema;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Push filtresi: `GetPushDevicesQuery` ve `SendPushNotificationCommand.audience` aynı alanları taşır
|
|
72
|
+
* (backend `PushAudienceModel`, tek kural `PushDevice.MatchesAudience`). Liste ve gönderim aynı
|
|
73
|
+
* şemayı okur ki ajan listede gördüğü kitleyi aynı alanlarla hedeflesin.
|
|
74
|
+
*/
|
|
75
|
+
const pushAudienceFields = {
|
|
76
|
+
channel: z.enum(['expo', 'apns', 'fcm']).optional().describe('Cihazın kanalı: expo = oynatıcıda önizleyen cihaz, apns / fcm = mağaza sürümü (iOS / Android)'),
|
|
77
|
+
platform: z.enum(['ios', 'android']).optional(),
|
|
78
|
+
language: z.string().min(1).optional().describe('Cihazın dili (küçük harf, ör. tr)'),
|
|
79
|
+
permission: z.enum(['granted', 'denied', 'undetermined']).optional().describe('Bildirim izni; gönderimde platform her zaman granted\'a daraltır'),
|
|
80
|
+
appVersion: z.string().min(1).optional(),
|
|
81
|
+
lastSeenAfter: z.string().datetime({ offset: true }).optional().describe('Bu andan sonra görülmüş cihazlar (ISO 8601)'),
|
|
82
|
+
isActive: z.boolean().optional().describe('Pasif = kanalın token\'ı yok ya da sağlayıcı cihazı ölü bildirdi (NTF-005); gönderimde platform her zaman aktife daraltır'),
|
|
83
|
+
search: z.string().min(1).optional().describe('Kurulum kimliği, uygulama kullanıcısı kimliği ya da görünen ad içinde arar')
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* NTF-001: AI production'a yazmaz (kök AGENTS.md §1). Politika kuralı DEĞİL, kapı: politika
|
|
87
|
+
* `policyOverride`/`POLICY=warn` ile gevşer, bu ret gevşememeli — `imports.ts`
|
|
88
|
+
* `assertNotProduction` ile aynı sınıf. Kuru koşu bir şey yazmadığı için serbesttir.
|
|
89
|
+
*/
|
|
90
|
+
function refuseProductionSend(environment, dryRun) {
|
|
91
|
+
if (dryRun || environment !== 'production')
|
|
92
|
+
return;
|
|
93
|
+
throw new PlatformApiError("notification_create [NTF-001]: production'da gerçek gönderim yapılmaz — kullanıcılara giden bildirim geri alınamaz ve production'a giden tek yol insan onayıdır. Development'ta testInstallationId ile kendi cihazınıza gönderin; production gönderimini Studio'daki Bildirimler ekranından bir insan yapar. Kitleyi saymak için dryRun: true serbesttir.", 400);
|
|
94
|
+
}
|
|
95
|
+
export function registerMobileTools(server, platform) {
|
|
96
|
+
const config = platform.config;
|
|
97
|
+
server.registerTool('notification_device_list', {
|
|
98
|
+
title: 'Push cihazlarını listele',
|
|
99
|
+
description: "Mobil uygulamanın push için kayıtlı cihazlarını (kurulumlarını) son görülme sırasıyla listeler: kanal, platform, dil, izin, uygulama sürümü, aktiflik. Kanal cihazın hangi sağlayıcıdan gönderileceğini söyler (NTF-004): `expo` oynatıcıda önizleyen cihaz (easProjectId dolu), `apns` / `fcm` mağaza sürümü (appIdentifier = bundle id / paket adı). Kayıt anonimdir ve kendiliğindendir — cihaz açtığı her uygulama ve environment için kendini kaydeder; oynatıcıda dev build gerekir (Expo Go uzak push almaz), mağaza sürümü yalnız production'a kaydolur. Token yanıtta YOKTUR: gateway sır maskeleyicisi `token` ve `expoPushToken`'ı `***` yapar; varlığını `hasDevicePushToken` ve `isActive` söyler. Gönderimde kitleyi aynı alanlarla notification_create.audience'a verin. Kullanıcı/grup bağı (appUserId, tags) henüz boştur.",
|
|
100
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid.describe('Mobil uygulama (slug da verilebilir, gateway GUID\'e çevirir)'), ...pushAudienceFields, pageIndex: pagingParams.pageIndex, pageLength: pagingParams.pageLength.describe('Sayfa boyu; varsayılan 25') },
|
|
101
|
+
annotations: readOnly
|
|
102
|
+
}, handle(config, ({ project, environment, applicationId, ...query }) => platform.tenant('GET', project, `/push/devices/${applicationId}`, { environment, query })));
|
|
103
|
+
server.registerTool('notification_create', {
|
|
104
|
+
title: 'Push bildirimi gönder (varsayılan kuru koşu)',
|
|
105
|
+
description: "Kitleye dile göre içerikle push bildirimi gönderir — `SendPushNotificationCommand: { dryRun, audience, content, defaultLanguage, path?, query?, androidChannel?, badge?, testInstallationId? }`. dryRun VARSAYILAN TRUE: yalnız kitleyi sayar ({ total, byLanguage }), hiçbir şey yazmaz; göndermek için dryRun: false AÇIKÇA verilir. Gerçek gönderim kuyruğa girer ve yanıt id döner; sonucu notification_history { id } ile okuyun (birkaç saniye sonra sent/failed, teslim sayıları ve hatalı cihazlar). Kitle her zaman aktif ve izinli cihazlara daraltılır; testInstallationId verilirse yalnız o kurulum (notification_device_list'teki installationId). İçerik dil → { title, body }; başlığı boş dil düşer ve o cihaz varsayılan dili alır; varsayılan dilde başlık zorunlu (NTF-002, platform 400). Dil anahtarları küçük harfe indirilir. path dokunuşta açılacak sayfanın slug'ı, query onun sorgusu. Ret: production'da dryRun: false istek gitmeden reddedilir (NTF-001).",
|
|
106
|
+
inputSchema: {
|
|
107
|
+
project: projectParam,
|
|
108
|
+
environment: environmentParam,
|
|
109
|
+
applicationId: guid.describe('Mobil uygulama (slug da verilebilir)'),
|
|
110
|
+
dryRun: z.boolean().optional().describe('Varsayılan true: yalnız kitleyi sayar. Göndermek için false'),
|
|
111
|
+
audience: z.object(pushAudienceFields).strict().optional().describe('notification_device_list filtreleri; boş = bütün aktif ve izinli cihazlar'),
|
|
112
|
+
content: z.record(z.object({ title: z.string().max(200), body: z.string().max(2000).optional() }).strict()).optional().describe('Dil → { title, body }; gerçek gönderimde zorunlu'),
|
|
113
|
+
defaultLanguage: z.string().min(1).max(16).optional().describe('İçeriği olmayan dildeki cihazların alacağı dil; gerçek gönderimde zorunlu'),
|
|
114
|
+
path: z.string().max(512).optional().describe('Dokunuşta açılacak sayfanın slug\'ı; boş = uygulamanın ana sayfası'),
|
|
115
|
+
query: z.string().max(1024).optional().describe('Sayfanın sorgusu, ör. id=42'),
|
|
116
|
+
androidChannel: z.string().max(64).optional(),
|
|
117
|
+
badge: z.number().int().min(0).optional(),
|
|
118
|
+
testInstallationId: z.string().max(64).optional().describe('Yalnız bu kuruluma gönder (kendi cihazınızla deneme)')
|
|
119
|
+
},
|
|
120
|
+
annotations: writes
|
|
121
|
+
}, handle(config, ({ project, environment, applicationId, dryRun = true, ...body }) => {
|
|
122
|
+
refuseProductionSend(environment ?? config.defaultEnvironment, dryRun);
|
|
123
|
+
return platform.tenant('POST', project, `/push/notifications/${applicationId}`, { environment, body: { ...body, dryRun } });
|
|
124
|
+
}));
|
|
125
|
+
server.registerTool('notification_history', {
|
|
126
|
+
title: 'Push gönderim geçmişi',
|
|
127
|
+
description: "Uygulamanın push gönderimlerini en yeniden listeler (durum queued/sending/sent/failed, hedef/kabul/teslim/hata sayıları). id verilirse tek gönderimin ayrıntısı: dil başına içerik ve ilk 100 hatalı cihaz (deviceId + sağlayıcının hatası, ör. DeviceNotRegistered, BadDeviceToken, UNREGISTERED). `sent` yalnız hattın bittiğini söyler: errorCount > 0 ise bazı cihazlara ulaşmamıştır. Teslim makbuzu (okCount) yalnız `expo` kanalında vardır ve gönderimden ~15 dk sonra işlenir; `apns` / `fcm` sonucu gönderim anında kesindir (sentCount). Hata `credentials-missing` ise o kanalın push kimliği yok ya da okunamıyor (NTF-004): kimlik Studio › Yayınla › Hesaplar ve kimlik bilgileri'nde bir insan tarafından yüklenir, ajan yükleyemez; durumu mobile_build_start { dryRun: true } (hedef testflight | internal) ön kontrolünün P-11 satırı gösterir; satır yoksa mobile_store_get { manifest: true } yanıtında manifest.push hazır olduğunu söyler.",
|
|
128
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid.describe('Mobil uygulama (slug da verilebilir)'), id: guid.optional().describe('Tek gönderimin ayrıntısı için'), pageIndex: pagingParams.pageIndex },
|
|
129
|
+
annotations: readOnly
|
|
130
|
+
}, handle(config, ({ project, environment, applicationId, id, ...query }) => platform.tenant('GET', project, id ? `/push/notifications/${applicationId}/${id}` : `/push/notifications/${applicationId}`, { environment, query })));
|
|
131
|
+
server.registerTool('mobile_app_create', {
|
|
132
|
+
title: 'Mobil uygulamayı kabuğuyla kur',
|
|
133
|
+
description: "Mobil uygulamayı (type:'mobile'), kabuk layout'unu (app-bar + slot; drawer:true ise çekmece widget'ı), sayfalarını ve isteğe bağlı sekme ayarını (properties.mobile.tabs) TEK çağrıda kurar. Sıra: application_create → layout_create → page_create (sayfalar layoutId ile doğar) → backend'in açtığı ana sayfa seçilen ana sayfaya dönüştürülür (yetim sayfa kalmaz) → application_update (yalnız tabs verildiyse). tabs yoksa gezinme yığındır. dryRun:true planı döner, hiçbir şey yazmaz. Platformda işlem yoktur: hata olursa yanıt completed (kurulanlar), failed (düşen adım) ve remaining (kalanlar) taşır — kalanları tekil araçlarla tamamlayın. Kabuk bileşenleri (app-bar, slot) mobil katalogda yoksa layout atlanır ve uyarı döner. Rehber: platform_guide('mobile').",
|
|
134
|
+
inputSchema: {
|
|
135
|
+
project: projectParam,
|
|
136
|
+
environment: environmentParam,
|
|
137
|
+
projectId: guid,
|
|
138
|
+
slug: z.string().min(1),
|
|
139
|
+
name: z.string().min(1),
|
|
140
|
+
color: z.string().optional(),
|
|
141
|
+
pages: z.array(z.object({ pageSlug: z.string().min(1), pageName: z.string().min(1) })).min(1),
|
|
142
|
+
tabs: z.array(z.object({ pageSlug: z.string().min(1), label: z.string().optional(), icon: z.string().optional() })).optional()
|
|
143
|
+
.describe(`1-${MAX_MOBILE_TABS} sekme; her pageSlug pages içinden. Verilmezse sekme çubuğu yok (yığın). label 't:<anahtar>' ile yazılırsa çeviri sözlüğünden çözülür (platform_guide('mobile') §Çok dil).`),
|
|
144
|
+
drawer: z.boolean().optional().describe("true: layout'a drawer widget'ı + app-bar.menuTarget (çekmece)"),
|
|
145
|
+
title: z.string().optional().describe('app-bar başlığı (bütün sayfalar için sabit metin). Boşsa çubuk başlıksız çizilir — sayfa adına düşen bir yedek YOKTUR; sayfaya göre değişen başlık için title\'ı bir değişkene bağlayın ya da başlığı sayfanın içine text olarak koyun'),
|
|
146
|
+
homepage: z.string().optional().describe('Ana sayfanın pageSlug\'ı; varsayılan ilk sekme, sekme yoksa ilk sayfa'),
|
|
147
|
+
dryRun: z.boolean().optional()
|
|
148
|
+
},
|
|
149
|
+
annotations: writes
|
|
150
|
+
}, handle(config, async ({ project, environment, projectId, slug, name, color, pages, tabs, drawer, title, homepage, dryRun }) => {
|
|
151
|
+
const input = { pages, ...(tabs ? { tabs } : {}), ...(drawer ? { drawer } : {}), ...(homepage ? { homepage } : {}), ...(title ? { title } : {}) };
|
|
152
|
+
const errors = mobileAppInputErrors(input);
|
|
153
|
+
if (errors.length > 0)
|
|
154
|
+
throw new PlatformApiError(`mobile_app_create: ${errors.join(' · ')}. Hiçbir şey yazılmadı.`, 400);
|
|
155
|
+
const plan = buildMobileAppPlan(input);
|
|
156
|
+
if (dryRun)
|
|
157
|
+
return { dryRun: true, plan };
|
|
158
|
+
const completed = [];
|
|
159
|
+
const skipped = new Set();
|
|
160
|
+
const warnings = [];
|
|
161
|
+
const home = homepageSlug(input);
|
|
162
|
+
const pageIds = new Map();
|
|
163
|
+
let applicationId;
|
|
164
|
+
let homepageId;
|
|
165
|
+
let layoutId;
|
|
166
|
+
let current = 'application_create';
|
|
167
|
+
try {
|
|
168
|
+
const created = unwrap(await platform.org('POST', `/applications/${projectId}`, {
|
|
169
|
+
environment,
|
|
170
|
+
body: { slug, name, type: 'mobile', ...(color ? { color } : {}) }
|
|
171
|
+
}));
|
|
172
|
+
applicationId = String(created.id ?? '');
|
|
173
|
+
homepageId = typeof created.homepageId === 'string'
|
|
174
|
+
? created.homepageId
|
|
175
|
+
: String(created.homepage?.id ?? '');
|
|
176
|
+
forgetApplication(platform);
|
|
177
|
+
if (!applicationId || !homepageId)
|
|
178
|
+
throw new PlatformApiError('application_create yanıtı uygulama ya da ana sayfa kimliği taşımıyor — application_get ile kontrol edin.');
|
|
179
|
+
completed.push({ step: 'application_create', id: applicationId });
|
|
180
|
+
current = 'layout_create';
|
|
181
|
+
const catalog = await fetchCatalogSlugs(platform, project, environment, applicationId);
|
|
182
|
+
const missing = SHELL_TYPES.filter(type => !catalog.has(type));
|
|
183
|
+
if (missing.length > 0) {
|
|
184
|
+
skipped.add('layout_create');
|
|
185
|
+
warnings.push(`MOB-003 (uyarı) — kabuk bileşenleri mobil katalogda yok (${missing.join(', ')}): layout KURULMADI, sayfalar layout'suz açıldı. Katalog yazıldıktan sonra kabuğu ngapps://examples/mobile-app-shell-${input.drawer ? 'drawer' : input.tabs ? 'tab' : 'stack'} ile layout_create + page_update(layoutId) olarak kurun.`);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
const layout = unwrap(await platform.tenant('POST', project, `/layouts/${applicationId}`, {
|
|
189
|
+
environment,
|
|
190
|
+
body: { name: `${name} kabuğu`, views: shellViews(input), events: [], parameters: null }
|
|
191
|
+
}));
|
|
192
|
+
layoutId = String(layout.id ?? '');
|
|
193
|
+
completed.push({ step: 'layout_create', id: layoutId });
|
|
194
|
+
}
|
|
195
|
+
const others = pages.filter(page => page.pageSlug !== home);
|
|
196
|
+
for (const [index, page] of others.entries()) {
|
|
197
|
+
current = `page_create#${index + 1}`;
|
|
198
|
+
const saved = unwrap(await platform.tenant('POST', project, `/pages/${applicationId}`, {
|
|
199
|
+
environment,
|
|
200
|
+
body: {
|
|
201
|
+
name: page.pageName,
|
|
202
|
+
slug: page.pageSlug,
|
|
203
|
+
browserTitle: '',
|
|
204
|
+
browserTitleKey: '',
|
|
205
|
+
layoutId: layoutId ?? null,
|
|
206
|
+
roleIds: [],
|
|
207
|
+
middlewareIds: [],
|
|
208
|
+
events: [],
|
|
209
|
+
schema: mobilePageSchema(layoutId !== undefined)
|
|
210
|
+
}
|
|
211
|
+
}));
|
|
212
|
+
pageIds.set(page.pageSlug, String(saved.id ?? ''));
|
|
213
|
+
completed.push({ step: current, id: String(saved.id ?? '') });
|
|
214
|
+
}
|
|
215
|
+
current = 'page_update#homepage';
|
|
216
|
+
const homePage = pages.find(page => page.pageSlug === home);
|
|
217
|
+
const existing = await fetchHost(platform, 'page', project, environment, applicationId, homepageId);
|
|
218
|
+
// Backend'in açtığı ana sayfanın kökü sözleşme dışıdır (`size: grow`, S-16). Sayfa henüz boşsa kök
|
|
219
|
+
// öteki sayfalarla aynı şablonla yeniden yazılır; içerik varsa dokunulmaz.
|
|
220
|
+
const homeSchema = existing.schema;
|
|
221
|
+
const blankHome = Array.isArray(homeSchema) && homeSchema.length === 1 && homeSchema[0]?.elementId === PAGE_CONTAINER_ELEMENT_ID && (homeSchema[0].children ?? []).length === 0;
|
|
222
|
+
await platform.tenant('PUT', project, `/pages/${applicationId}/${homepageId}`, {
|
|
223
|
+
environment,
|
|
224
|
+
body: pageUpdateBody(existing, {
|
|
225
|
+
name: homePage.pageName,
|
|
226
|
+
slug: homePage.pageSlug,
|
|
227
|
+
layoutId: layoutId ?? null,
|
|
228
|
+
...(blankHome ? { schema: mobilePageSchema(layoutId !== undefined) } : {})
|
|
229
|
+
})
|
|
230
|
+
});
|
|
231
|
+
pageIds.set(home, homepageId);
|
|
232
|
+
completed.push({ step: current, id: homepageId });
|
|
233
|
+
if (tabs) {
|
|
234
|
+
current = 'application_update';
|
|
235
|
+
const properties = {
|
|
236
|
+
mobile: {
|
|
237
|
+
tabs: {
|
|
238
|
+
enabled: true,
|
|
239
|
+
style: 'default',
|
|
240
|
+
showLabels: true,
|
|
241
|
+
items: tabs.map(tab => ({ page: pageIds.get(tab.pageSlug), ...(tab.label ? { label: tab.label } : {}), ...(tab.icon ? { icon: tab.icon } : {}) }))
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
warnings.push(...await mobileTabsWarnings(platform, applicationId, platform.resolveProject(project), homepageId, properties));
|
|
246
|
+
await platform.org('PUT', `/applications/${applicationId}`, {
|
|
247
|
+
environment,
|
|
248
|
+
body: { slug, name, homepageId, ...(color ? { color } : {}), properties }
|
|
249
|
+
});
|
|
250
|
+
forgetApplication(platform, applicationId);
|
|
251
|
+
completed.push({ step: current });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
if (completed.length === 0)
|
|
256
|
+
throw error;
|
|
257
|
+
const done = new Set(completed.map(entry => entry.step));
|
|
258
|
+
return {
|
|
259
|
+
applicationId,
|
|
260
|
+
...(layoutId ? { layoutId } : {}),
|
|
261
|
+
completed,
|
|
262
|
+
failed: { step: current, error: error instanceof Error ? error.message : String(error) },
|
|
263
|
+
remaining: plan.map(step => step.step).filter(step => !done.has(step) && !skipped.has(step) && step !== current),
|
|
264
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
265
|
+
note: 'Kurulum yarıda kaldı: completed kurulanları, remaining eksik adımları söyler. Kalanları tekil araçlarla tamamlayın (page_create, page_update, application_update) — mobile_app_create aynı slug ile tekrar çağrılırsa uygulamayı ikinci kez açmaya çalışır.'
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
applicationId,
|
|
270
|
+
homepageId,
|
|
271
|
+
...(layoutId ? { layoutId } : {}),
|
|
272
|
+
applicationType: 'mobile',
|
|
273
|
+
pages: pages.map(page => ({ slug: page.pageSlug, name: page.pageName, id: pageIds.get(page.pageSlug), ...(page.pageSlug === home ? { homepage: true } : {}) })),
|
|
274
|
+
...(tabs ? { tabs: tabs.map(tab => tab.pageSlug) } : {}),
|
|
275
|
+
completed,
|
|
276
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
277
|
+
next: "Ekranları page_schema_patch ile doldurun; doğrulama page_screenshot (mobilde varsayılan device: 'phone'). platform_guide('mobile')."
|
|
278
|
+
};
|
|
279
|
+
}));
|
|
280
|
+
}
|