@appaflytech/zeyrek-ai-mcp 0.0.53 → 0.0.54
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/resources/guides/merge.md +5 -0
- package/dist/resources/guides/mobile-publish.md +3 -2
- package/dist/resources/guides/mobile.md +3 -2
- package/dist/resources/prompts/mobile-publish-prepare.md +3 -2
- package/dist/tools/applications.js +4 -4
- package/dist/tools/connectData.js +13 -4
- package/dist/tools/microservices.js +25 -7
- package/dist/tools/mobileStore.js +1 -1
- package/dist/tools/pages.js +26 -7
- package/package.json +1 -1
|
@@ -70,6 +70,11 @@ Studio'nun runtime rotasının üç şeridi vardır ve **ortamı URL belirler**:
|
|
|
70
70
|
Sayfanın **bütün** istekleri o dala gider: sayfa şeması da, akışların uç çağrıları da. Yani kare
|
|
71
71
|
"yarı doğru" değildir — gördüğünüz ekran dalın gerçek ekranıdır.
|
|
72
72
|
|
|
73
|
+
`{app}` **o ortamın** uygulama slug'ıdır: dal uygulama kaydını açıldığı andaki hâliyle tutar, production
|
|
74
|
+
dağıtıma kadar eskisini; `application_list` ise development slug'ını verir. Uygulama sonradan yeniden
|
|
75
|
+
adlandıysa Core slug'ı dalda/production'da 404 verir — `applicationSlug`'a uygulama **id**'sini verin,
|
|
76
|
+
gateway ortamın slug'ını okur; dal açıldıktan sonra kurulan uygulama dalda yoktur (APP-002).
|
|
77
|
+
|
|
73
78
|
Reddedilen tek şey **dal adı olamayacak bir ad**: harfle başlamayan, küçük harf/rakam/alt çizgi/tire
|
|
74
79
|
ve `/` ile ayrılmış parçalar dışında karakter taşıyan, 3-31 aralığının dışına çıkan ya da rezerve bir
|
|
75
80
|
ad (`public`, `api`, `pg_*` …). Projedeki dalları ve slug'larını `environment_list` verir; tenant
|
|
@@ -96,8 +96,9 @@ bağlantısı) Studio › Yayınla'dan davet edilir — kişisel e-posta taşıd
|
|
|
96
96
|
gönderim** (adım 8: `app-store-review`, `app-store-release`, `play-promote`) geri dönüşsüz ya da insan onaylıdır;
|
|
97
97
|
ajan yalnız `dryRun` ile ön kontrolünü okur ve kullanıcıyı Studio'ya yönlendirir.
|
|
98
98
|
|
|
99
|
-
**Ekran görüntüsü:** `page_screenshot { environment: 'production', device, format: 'jpeg', quality: 92,
|
|
100
|
-
language, colorScheme: 'light', outPath }`
|
|
99
|
+
**Ekran görüntüsü:** `page_screenshot { environment: 'production', applicationSlug: <uygulama id'si>, device, format: 'jpeg', quality: 92,
|
|
100
|
+
language, colorScheme: 'light', outPath }` — id verilince gateway production'ın kendi slug'ını okur (yeniden adlanan
|
|
101
|
+
uygulama dağıtıma kadar eski slug'la açılır, APP-002) → `mobile_asset_create { path, kind: 'screenshot', deviceClass, locale }`.
|
|
101
102
|
`device`: iphone69 `{440,956,3}` · ipad13 `{1032,1376,2}` · phone `{360,720,3}` · tablet7 `{600,960,2}` ·
|
|
102
103
|
tablet10 `{800,1280,2}`. JPEG şart (iOS alfa kabul etmez); giriş/splash sayfası çekilmez (App Review 2.3.3).
|
|
103
104
|
ikon/splash/öne çıkan görsel tekli yuvadır: yeni yükleme eskisinin yerine geçer.
|
|
@@ -596,7 +596,8 @@ yaptıkları. Derlenen uygulama
|
|
|
596
596
|
(MOB-033), `Steps.<ad>.cache.at`, `error.network` — `platform_guide('page')` §Kural 1g-4. Önizleme önbellek kullanmaz.
|
|
597
597
|
Yalnız GET okuma saklanır (POST okuma ağsız açılmaz → GET + query-string, FLOW-044); kuyruk değişimi sayfanın
|
|
598
598
|
`offlineQueueChange` olayıdır, yoklama gerekmez (§Kural 1g-4).
|
|
599
|
-
|
|
600
|
-
`deployment_start`'tan sonra geçerli olur (APP-001) — önce development'ta
|
|
599
|
+
Uygulama kaydı (ad, slug, `properties`, ana sayfa, bulunamadı sayfası) `application_update` ile yalnız
|
|
600
|
+
**development**'a yazılır; production'da `deployment_start`'tan sonra geçerli olur (APP-001) — önce development'ta
|
|
601
|
+
deneyin. Slug değişirse production derlemesi dağıtıma kadar eski slug'ı taşır.
|
|
601
602
|
"Uygulama ağsızken de çalışsın" isteğinin tarifi (okuma GET, yazma kuyruğu, liste birleştirme, düğme kilidi, korumasız
|
|
602
603
|
açılış, bant kapalı): §Kural 1g-4 "Ağsızken fark göstermeyen uygulama", örnek `ngapps://examples/mobile-offline-seamless`.
|
|
@@ -54,8 +54,9 @@ başlamadan önce §Yolculuk, §Test derlemesi, §Akış, §Yaş derecelendirmes
|
|
|
54
54
|
`adaptiveMonochrome`, `splash`) eksikse kullanıcıdan dosya iste ya da var olan dosyanın yolunu sor.
|
|
55
55
|
Ekran görüntüsü: her mağaza dili ve açık platformun cihaz sınıfı için 3–5 gerçek ekran —
|
|
56
56
|
`page_screenshot { applicationSlug, pageSlug, environment: 'production', device, format: 'jpeg', quality: 92, language, colorScheme: 'light', outPath }`
|
|
57
|
-
→ `mobile_asset_create { applicationId, kind: 'screenshot', deviceClass, locale, path }`. `applicationSlug`
|
|
58
|
-
|
|
57
|
+
→ `mobile_asset_create { applicationId, kind: 'screenshot', deviceClass, locale, path }`. `applicationSlug`'a
|
|
58
|
+
uygulamanın id'sini ver (gateway production'ın kendi slug'ını okur; `application_list` development slug'ını döner ve
|
|
59
|
+
yeniden adlanan uygulamada production'da 404 verir — APP-002), `pageSlug` `page_list`'ten; `device` rehberdeki cihaz tablosundan; `language` o
|
|
59
60
|
yuvanın `locale`'idir — verilmezse kare projenin varsayılan dilinde çıkar ve başka dilin yuvasına yanlış dilde
|
|
60
61
|
yüklenir. Yuva birikimlidir (en çok 10 görsel): 1. adımda okuduğun yuvada görsel varsa kullanıcıya sormadan
|
|
61
62
|
ekleme yapma. Giriş ve splash sayfası çekilmez. Production'da sayfa yoksa kullanıcıya sor: ekran görüntüsü
|
|
@@ -91,7 +91,7 @@ export function registerApplicationTools(server, platform) {
|
|
|
91
91
|
}, handle(config, args => platform.org('GET', `/applications/${args.id}`)));
|
|
92
92
|
server.registerTool('application_create', {
|
|
93
93
|
title: 'Application oluştur',
|
|
94
|
-
description: `Projeye yeni application ekler. ${templateSubscriptionLock}`,
|
|
94
|
+
description: `Projeye yeni application ekler. ORTAM (b-17, APP-001): uygulama yalnız development'ta (ve Core'da) açılır; production onu ilk deployment_start ile alır, o zamana kadar production adresi yoktur. Dallar açıldıkları andaki kopyayı tutar, sonradan açılan uygulamayı görmez. ${templateSubscriptionLock}`,
|
|
95
95
|
inputSchema: {
|
|
96
96
|
projectId: guid,
|
|
97
97
|
slug: z.string().min(1),
|
|
@@ -113,7 +113,7 @@ export function registerApplicationTools(server, platform) {
|
|
|
113
113
|
}));
|
|
114
114
|
server.registerTool('application_update', {
|
|
115
115
|
title: 'Application güncelle',
|
|
116
|
-
description: `Application bilgilerini günceller; homepageId zorunludur (mevcut değeri application_get ile alın). ORTAM (K5, b-15):
|
|
116
|
+
description: `Application bilgilerini günceller; homepageId zorunludur (mevcut değeri application_get ile alın). ORTAM (K5, b-15): bütün alanlar — name, slug, color, dns, properties (mobile.offline, sekmeler, geçiş…), homepageId, notFoundPageId — yalnız development'a (ve Core'a) yazılır; production onları deployment_start ile alır (dal ortamları açıldıkları andaki kopyayı tutar), yani önce development'ta deneyin. Slug değişirse production adresi dağıtıma kadar eski slug'la çalışır. Yanıttaki environmentNote bunu çağrı başına söyler. ${templateSubscriptionLock}`,
|
|
117
117
|
inputSchema: {
|
|
118
118
|
id: guid,
|
|
119
119
|
slug: z.string().min(1),
|
|
@@ -136,12 +136,12 @@ export function registerApplicationTools(server, platform) {
|
|
|
136
136
|
const missing = ['properties', 'notFoundPageId'].filter((field) => body[field] === undefined);
|
|
137
137
|
return {
|
|
138
138
|
...withPlatformWarnings(response, warnings),
|
|
139
|
-
environmentNote: `properties, homepageId ve notFoundPageId verildiği gibi yalnız development'a (ve Core'a) yazıldı; production onları deployment_start ile alır, dal ortamları hiç almaz (APP-001)
|
|
139
|
+
environmentNote: `name, slug, color, dns, properties, homepageId ve notFoundPageId verildiği gibi yalnız development'a (ve Core'a) yazıldı; production onları deployment_start ile alır, dal ortamları hiç almaz (APP-001).${missing.length > 0 ? ` ${missing.join(' ve ')} verilmedi: uç tam kaydı yazar, development'taki değer boşaltıldı — korumak için application_get'teki mevcut değeri gönderin.` : ''}`
|
|
140
140
|
};
|
|
141
141
|
}));
|
|
142
142
|
server.registerTool('application_delete', {
|
|
143
143
|
title: 'Application sil',
|
|
144
|
-
description: `Application'ı kalıcı olarak siler. ${templateSubscriptionLock}`,
|
|
144
|
+
description: `Application'ı kalıcı olarak siler — development'tan (ve Core'dan). ORTAM (b-17, APP-001): production'daki kopya ve kullanıcı verisi deployment_start'a kadar yerinde kalır, silme dağıtımla gider; dallar kendi kopyasını tutar. ${templateSubscriptionLock}`,
|
|
145
145
|
inputSchema: { id: guid },
|
|
146
146
|
annotations: destructive
|
|
147
147
|
}, handle(config, async (args) => {
|
|
@@ -240,7 +240,8 @@ export function registerConnectDataTools(server, platform) {
|
|
|
240
240
|
anonymous,
|
|
241
241
|
mirrorAuthorizationCookie: true
|
|
242
242
|
});
|
|
243
|
-
|
|
243
|
+
// Development dışında slug o ortamın kendi kaydıdır; Core'un (yeni) slug'ı orada 404 verir (g-24, APP-002).
|
|
244
|
+
let application = await applicationSlug(platform, applicationId, { project, environment });
|
|
244
245
|
if (!application)
|
|
245
246
|
throw new PlatformApiError(`Uygulama ${applicationId} bulunamadı; runtime yolu kurulamadı.`, 404);
|
|
246
247
|
let raw;
|
|
@@ -250,7 +251,7 @@ export function registerConnectDataTools(server, platform) {
|
|
|
250
251
|
catch (error) {
|
|
251
252
|
if (!(error instanceof PlatformApiError) || error.status !== 404)
|
|
252
253
|
throw error;
|
|
253
|
-
const fresh = await applicationSlug(platform, applicationId, { fresh: true });
|
|
254
|
+
const fresh = await applicationSlug(platform, applicationId, { project, environment, fresh: true });
|
|
254
255
|
if (!fresh || fresh === application)
|
|
255
256
|
throw error;
|
|
256
257
|
application = fresh;
|
|
@@ -412,18 +413,26 @@ export function registerConnectDataTools(server, platform) {
|
|
|
412
413
|
environment,
|
|
413
414
|
body: { elementId: element, propPath: prop, name, slug, ...(version ? { baseVersion: version } : {}) }
|
|
414
415
|
}));
|
|
415
|
-
|
|
416
|
+
// Dönüşüm yazıldıktan sonra tarif düşmez: dağıtılmamış uygulamada yer tutucu slug ve neden döner (APP-002).
|
|
417
|
+
let notDeployed;
|
|
418
|
+
const application = await applicationSlug(platform, applicationId, { project, environment }).catch((error) => {
|
|
419
|
+
if (!(error instanceof PlatformApiError) || error.status !== 404)
|
|
420
|
+
throw error;
|
|
421
|
+
notDeployed = error.message;
|
|
422
|
+
return undefined;
|
|
423
|
+
});
|
|
416
424
|
// Kompleks + enforce projede kimliksiz çağrı 401 AppSessionRequired döner ve yeni uç kullanıcının hiçbir
|
|
417
425
|
// politikasında yoktur (39/M-15): tarif kullanıcı gözünü ve eksik grant'i söyler.
|
|
418
426
|
const access = await projectAccessContext(platform, project, environment);
|
|
419
427
|
const enforced = access?.accessModel === 'complex' && access.enforcementMode === 'enforce';
|
|
420
|
-
const verifyArguments = { application: application ?? '<application slug>', microservice: result.microserviceSlug, method: 'POST', body: { op: 'list', page: { limit: 5 } }, ...(enforced ? { asUserId: "<users satır id'si>" } : {}) };
|
|
428
|
+
const verifyArguments = { ...(environment ? { environment } : {}), application: application ?? '<application slug>', microservice: result.microserviceSlug, method: 'POST', body: { op: 'list', page: { limit: 5 } }, ...(enforced ? { asUserId: "<users satır id'si>" } : {}) };
|
|
421
429
|
return {
|
|
422
430
|
...result,
|
|
423
431
|
reference: { version: 1, microserviceId: result.microserviceId },
|
|
424
432
|
verify: {
|
|
425
433
|
tool: 'microservice_execute',
|
|
426
434
|
arguments: verifyArguments,
|
|
435
|
+
...(notDeployed ? { notDeployed } : {}),
|
|
427
436
|
...(enforced ? { note: `Kompleks + enforce proje: kimliksiz çağrı 401 döner, asUserId verin. Yeni uç hiçbir politikada yok — kullanıcının politikasına microservice.execute grant'i ekleyin (resourceKey 'microservice:${result.microserviceId}'), yoksa 403. Sayfanın kendi bağlantısı bu grant'i istemez; connect_data_detach sonrası grant yetim kalır.` } : {})
|
|
428
437
|
}
|
|
429
438
|
};
|
|
@@ -214,25 +214,36 @@ function executePath(prefix, application, microservice, routeParams) {
|
|
|
214
214
|
/**
|
|
215
215
|
* Slug application_update ile değişebildiği için önbellek kısa ömürlüdür (yalnız aynı turdaki tekrar okumaları ucuzlatır);
|
|
216
216
|
* runtime yolu 404 dönen çağıran `fresh: true` ile önbelleği atlar. Org API'nin 404 dışı hatası (yetki, ağ) çağırana gider.
|
|
217
|
+
*
|
|
218
|
+
* Uygulama kaydı (ad, slug, ayar) ve varlığı production'a yalnız dağıtımla gider, dal açıldığı andaki kopyayı tutar
|
|
219
|
+
* (backend ADR 0073, g-24): `GET /applications/{id}` Core'dur = development. Başka ortamın slug'ı o ortamın kendi
|
|
220
|
+
* kaydından (tenant `GET /{project}/applications/{id}`, X-Environment) okunur; yoksa uygulama oraya dağıtılmamıştır (APP-002).
|
|
217
221
|
*/
|
|
218
222
|
const APPLICATION_SLUG_TTL_MS = 60_000;
|
|
219
223
|
const applicationSlugCache = new Map();
|
|
220
224
|
export async function applicationSlug(platform, applicationId, options = {}) {
|
|
221
|
-
const
|
|
225
|
+
const environment = platform.resolveEnvironment(options.environment);
|
|
226
|
+
const core = environment === 'development';
|
|
227
|
+
const key = `${core ? '' : platform.resolveProject(options.project)}:${environment}:${applicationId}`;
|
|
228
|
+
const cached = options.fresh ? undefined : applicationSlugCache.get(key);
|
|
222
229
|
if (cached && cached.expiresAt > Date.now())
|
|
223
230
|
return cached.slug;
|
|
224
231
|
try {
|
|
225
|
-
const payload =
|
|
232
|
+
const payload = (core
|
|
233
|
+
? await platform.org('GET', `/applications/${applicationId}`)
|
|
234
|
+
: await platform.tenant('GET', options.project, `/applications/${applicationId}`, { environment }));
|
|
226
235
|
const application = (payload?.data ?? payload);
|
|
227
236
|
const slug = typeof application.slug === 'string' && application.slug !== '' ? application.slug : undefined;
|
|
228
237
|
if (slug)
|
|
229
|
-
applicationSlugCache.set(
|
|
238
|
+
applicationSlugCache.set(key, { slug, expiresAt: Date.now() + APPLICATION_SLUG_TTL_MS });
|
|
230
239
|
return slug;
|
|
231
240
|
}
|
|
232
241
|
catch (error) {
|
|
233
|
-
if (error instanceof PlatformApiError
|
|
242
|
+
if (!(error instanceof PlatformApiError) || error.status !== 404)
|
|
243
|
+
throw error;
|
|
244
|
+
if (core)
|
|
234
245
|
return undefined;
|
|
235
|
-
throw
|
|
246
|
+
throw new PlatformApiError(`Uygulama ${applicationId} '${environment}' ortamında yok: uygulama kaydı (ad, slug, ayar) ve varlığı production'a yalnız dağıtımla gider, dal açıldığı andaki kopyayı tutar (APP-002). Production için deployment_start ile dağıtın; dalda yalnız dal açılmadan önce var olan uygulama bulunur — development'ta deneyin ya da dalı yeniden açın.`, 404);
|
|
236
247
|
}
|
|
237
248
|
}
|
|
238
249
|
/**
|
|
@@ -247,7 +258,14 @@ async function resolveRuntimeUrl(platform, project, environment, applicationId,
|
|
|
247
258
|
const method = String(definition.httpMethod ?? 'get').toUpperCase();
|
|
248
259
|
const resolvedProject = platform.resolveProject(project);
|
|
249
260
|
const resolvedEnvironment = platform.resolveEnvironment(environment);
|
|
250
|
-
|
|
261
|
+
// Dağıtılmamış uygulamada tanım yine döner; tarif yer tutucu slug ve nedeni taşır.
|
|
262
|
+
let notDeployed;
|
|
263
|
+
const application = await applicationSlug(platform, applicationId, { project, environment }).catch((error) => {
|
|
264
|
+
if (!(error instanceof PlatformApiError) || error.status !== 404)
|
|
265
|
+
throw error;
|
|
266
|
+
notDeployed = error.message;
|
|
267
|
+
return undefined;
|
|
268
|
+
});
|
|
251
269
|
const path = `/${resolvedProject}/m/${application ?? '<application slug>'}/${slug}`;
|
|
252
270
|
return {
|
|
253
271
|
method,
|
|
@@ -257,7 +275,7 @@ async function resolveRuntimeUrl(platform, project, environment, applicationId,
|
|
|
257
275
|
Host: `${platform.config.organization}.${platform.config.hostSuffix}`,
|
|
258
276
|
'X-Environment': resolvedEnvironment
|
|
259
277
|
},
|
|
260
|
-
note: `Yolu SLUG kurar, 'url' alanı ('${String(definition.url ?? '')}') rotaya girmez. Route parametreleri slug'ın ardına eklenir. Anonim erişim testi için microservice_execute'u anonymous: true ile çağırın; korumalı uçlarda 401, anonim uçlarda 200 beklenir
|
|
278
|
+
note: `Yolu SLUG kurar, 'url' alanı ('${String(definition.url ?? '')}') rotaya girmez. Route parametreleri slug'ın ardına eklenir. Anonim erişim testi için microservice_execute'u anonymous: true ile çağırın; korumalı uçlarda 401, anonim uçlarda 200 beklenir.${notDeployed ? ` ${notDeployed}` : ''}`
|
|
261
279
|
};
|
|
262
280
|
}
|
|
263
281
|
export function registerMicroserviceTools(server, platform) {
|
|
@@ -590,7 +590,7 @@ export function registerMobileStoreTools(server, platform, storeArt = {}) {
|
|
|
590
590
|
}, handle(config, async ({ buildId: id }) => buildSummary(unwrap(await platform.org('POST', `/mobile-builds/${id}/cancel`)), new Date().toISOString())));
|
|
591
591
|
server.registerTool('mobile_asset_create', {
|
|
592
592
|
title: 'Mağaza görseli yükle',
|
|
593
|
-
description: `Mağaza görselini yükler (POST mobile-store/{appId}/assets, multipart). Kaynak path (gateway'in okuyabildiği yerel dosya) ya da base64 content'tir — dosya ajanın bağlamına girmez. Yuvalar: derleme görselleri icon (1024×1024 PNG, alfasız), adaptiveForeground / adaptiveBackground / adaptiveMonochrome (1024×1024 PNG), splash (≥ 1242×2436 PNG) — dilsizdir, locale verilmez; mağaza görselleri featureGraphic (1024×500, alfasız), promo, screenshot (deviceClass zorunlu, locale önerilir: 'screenshot/tr/iphone69'); reviewAttachment (dilsiz, PNG/JPEG) App Review'a ek görseldir — yalnız app-store-review gönderimine girer. Tuzak: screenshot ve promo dışındaki türler TEKLİDİR — yükleme yuvadaki önceki görselin YERİNE geçer ve eskisi silinir. Yuva başına en çok 10 ekran görüntüsü, dosya ≤ 10 MB, PNG ya da JPEG (derleme görseli yalnız PNG). Ölçü, biçim ve alfa backend'de denetlenir; 422 hangi ölçünün beklendiğini söyler. EKRAN GÖRÜNTÜSÜ uygulamanın kendi sayfasından üretilir: önce page_screenshot { environment: 'production', applicationSlug, pageSlug, device, format: 'jpeg', quality: 92, language, colorScheme: 'light', outPath }, sonra bu araç { path: <outPath>, kind: 'screenshot', deviceClass, locale }. Cihaz sınıfı → device: ${captureTable}. JPEG şarttır: iPhone ve iPad ekran görüntüsünde alfa kanalı reddedilir. Giriş/splash ekranını çekmeyin (App Review 2.3.3: uygulama kullanımda gösterilir). Sıra: mobile_asset_update; silme: mobile_asset_delete; liste: mobile_store_get { detail: ['assets'] }.`,
|
|
593
|
+
description: `Mağaza görselini yükler (POST mobile-store/{appId}/assets, multipart). Kaynak path (gateway'in okuyabildiği yerel dosya) ya da base64 content'tir — dosya ajanın bağlamına girmez. Yuvalar: derleme görselleri icon (1024×1024 PNG, alfasız), adaptiveForeground / adaptiveBackground / adaptiveMonochrome (1024×1024 PNG), splash (≥ 1242×2436 PNG) — dilsizdir, locale verilmez; mağaza görselleri featureGraphic (1024×500, alfasız), promo, screenshot (deviceClass zorunlu, locale önerilir: 'screenshot/tr/iphone69'); reviewAttachment (dilsiz, PNG/JPEG) App Review'a ek görseldir — yalnız app-store-review gönderimine girer. Tuzak: screenshot ve promo dışındaki türler TEKLİDİR — yükleme yuvadaki önceki görselin YERİNE geçer ve eskisi silinir. Yuva başına en çok 10 ekran görüntüsü, dosya ≤ 10 MB, PNG ya da JPEG (derleme görseli yalnız PNG). Ölçü, biçim ve alfa backend'de denetlenir; 422 hangi ölçünün beklendiğini söyler. EKRAN GÖRÜNTÜSÜ uygulamanın kendi sayfasından üretilir: önce page_screenshot { environment: 'production', applicationSlug: <uygulama id'si — production'ın kendi slug'ına çözülür, APP-002>, pageSlug, device, format: 'jpeg', quality: 92, language, colorScheme: 'light', outPath }, sonra bu araç { path: <outPath>, kind: 'screenshot', deviceClass, locale }. Cihaz sınıfı → device: ${captureTable}. JPEG şarttır: iPhone ve iPad ekran görüntüsünde alfa kanalı reddedilir. Giriş/splash ekranını çekmeyin (App Review 2.3.3: uygulama kullanımda gösterilir). Sıra: mobile_asset_update; silme: mobile_asset_delete; liste: mobile_store_get { detail: ['assets'] }.`,
|
|
594
594
|
inputSchema: {
|
|
595
595
|
applicationId,
|
|
596
596
|
path: z.string().min(1).optional().describe("Gateway sürecinin okuyabildiği yerel PNG/JPEG yolu (page_screenshot outPath'i); content ile birlikte verilmez"),
|
package/dist/tools/pages.js
CHANGED
|
@@ -30,6 +30,8 @@ import { boxSectionNote, collectIds, ensureBoxSections, expandSectionToggles } f
|
|
|
30
30
|
import { designSnapshotInput } from '../knowledge/designSnapshot.js';
|
|
31
31
|
import { discoverPage } from '../knowledge/pageDiscover.js';
|
|
32
32
|
import { applicationTypeField, resolveProfile, resolveProfileBySlug } from '../knowledge/platform/resolve.js';
|
|
33
|
+
import { applicationSlug as environmentApplicationSlug } from './microservices.js';
|
|
34
|
+
import { isGuid } from '../platform/refs.js';
|
|
33
35
|
import { batchEnvelopeNote, batchIndexHint, openBatchEnvelopes } from '../knowledge/platform/batchOps.js';
|
|
34
36
|
import { heroTags } from '../knowledge/platform/heroTag.js';
|
|
35
37
|
import { profileLintReadsSavedNodes } from '../knowledge/platform/lint.js';
|
|
@@ -58,6 +60,20 @@ function runtimeFunctions(value) {
|
|
|
58
60
|
.map(parameter => ({ name: parameter.name, required: parameter.required === true, defaultValue: parameter.defaultValue ?? null }))
|
|
59
61
|
}));
|
|
60
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* g-24 / APP-002: uygulama kaydı production'a yalnız dağıtımla gider, dal açıldığı andaki kopyayı tutar; application_list
|
|
65
|
+
* ve application_get Core'u (development) döner. Yeniden adlanan uygulamanın production/dal adresi eski slug'la çalışır,
|
|
66
|
+
* Core slug'ı orada 404 verir — id verilirse slug'ı o ortamın kaydından gateway okur.
|
|
67
|
+
*/
|
|
68
|
+
const runtimeApplicationParam = z.string().min(1).describe("Uygulamanın slug'ı ya da id'si (GUID). application_list / application_get DEVELOPMENT slug'ını döner; production ve dal uygulama kaydını yalnız dağıtımla/açılışla alır, yeniden adlanan uygulama orada dağıtıma kadar eski slug'la açılır. production ya da dalda id verin: gateway o ortamın kendi slug'ını okur, uygulama o ortamda yoksa (dağıtılmamış) istek gitmeden hata döner (APP-002)");
|
|
69
|
+
async function runtimeApplicationSlug(platform, project, ref, environment) {
|
|
70
|
+
if (!isGuid(ref))
|
|
71
|
+
return ref;
|
|
72
|
+
const slug = await environmentApplicationSlug(platform, ref, { project, environment });
|
|
73
|
+
if (!slug)
|
|
74
|
+
throw new PlatformApiError(`Uygulama ${ref} bulunamadı: id'yi application_list ile doğrulayın.`, 404);
|
|
75
|
+
return slug;
|
|
76
|
+
}
|
|
61
77
|
/**
|
|
62
78
|
* G-10: `index` taşıyan add/move op'u varsa sayfa okunur ve `flexIndexOrder` flex kapta görünen
|
|
63
79
|
* sırayı konumlara çevirir. Okuma başarısız olursa op'lar olduğu gibi gider — bugünkü davranış.
|
|
@@ -181,7 +197,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
181
197
|
inputSchema: {
|
|
182
198
|
project: projectParam,
|
|
183
199
|
environment: environmentParam,
|
|
184
|
-
applicationSlug:
|
|
200
|
+
applicationSlug: runtimeApplicationParam,
|
|
185
201
|
pageSlug: z.string().min(1).describe("Sayfanın slug'ı (page_list)"),
|
|
186
202
|
view: z.enum(['summary', 'full']).optional().describe('summary (varsayılan): şema/layout ağaçları id/name/type özetine indirgenir. full: ham gövde — büyük olabilir'),
|
|
187
203
|
resolveStyles: z.boolean().optional().describe("summary görünümünde her düğüme EFEKTİF stilini ekler: tema componentStyles → preset → düğümün kendi stili kaskadı çözülür (aibox'ta tipli stil grupları da derlenir ve TABAN katman olur). styleLayers hangi katmanın konuştuğunu söyler — 'bu kutu neden böyle görünüyor' sorusunun cevabı budur."),
|
|
@@ -190,7 +206,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
190
206
|
asUserId: asUserIdParam
|
|
191
207
|
},
|
|
192
208
|
annotations: readOnly
|
|
193
|
-
}, handle(config, async ({ project, environment, applicationSlug, pageSlug, view, resolveStyles, anonymous, asUser, asUserId }) => {
|
|
209
|
+
}, handle(config, async ({ project, environment, applicationSlug: applicationRef, pageSlug, view, resolveStyles, anonymous, asUser, asUserId }) => {
|
|
194
210
|
let response;
|
|
195
211
|
if (anonymous === true && asUser !== undefined) {
|
|
196
212
|
throw new PlatformApiError('page_runtime_get: anonymous:true ile asUser birlikte verilemez — biri kimliksiz isteği, diğeri belirli bir üyenin kimliğiyle atılan isteği ister.');
|
|
@@ -199,6 +215,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
199
215
|
// yapısal olarak geçemiyordu. Kimlik artık iki kanalda birden gider (Y-02).
|
|
200
216
|
const eye = await impersonationHeaders(platform, 'page_runtime_get', project, environment, { asUserId, asUser, anonymous }, { runtime: true });
|
|
201
217
|
const identity = asUser === undefined ? (eye ? { headers: eye } : {}) : { headers: { authorization: `Bearer ${await resolveIdentity(asUser, platform, project, environment)}` } };
|
|
218
|
+
const applicationSlug = await runtimeApplicationSlug(platform, project, applicationRef, environment);
|
|
202
219
|
try {
|
|
203
220
|
response = await platform.tenant('GET', project, `/${applicationSlug}/p/${pageSlug}`, { environment, anonymous, mirrorAuthorizationCookie: true, ...identity });
|
|
204
221
|
}
|
|
@@ -210,7 +227,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
210
227
|
status: error.status,
|
|
211
228
|
message: error.message,
|
|
212
229
|
...(decision ? { accessDecision: decision } : {}),
|
|
213
|
-
note: "Sayfaya bağlı middleware isteği reddetti ya da kayıt bulunamadı. Korumalı sayfada kimliksiz/yetkisiz isteğin reddedilmesi BEKLENEN davranıştır. Sayfanın bağlarını page_get ile, middleware tarifini platform_guide('middleware') ile kontrol edin; slug'ları page_list/application_list doğrular."
|
|
230
|
+
note: "Sayfaya bağlı middleware isteği reddetti ya da kayıt bulunamadı. Korumalı sayfada kimliksiz/yetkisiz isteğin reddedilmesi BEKLENEN davranıştır. Sayfanın bağlarını page_get ile, middleware tarifini platform_guide('middleware') ile kontrol edin; slug'ları page_list/application_list doğrular — application_list development slug'ını verir; production ya da dalda applicationSlug yerine uygulama id'sini verin (APP-002)."
|
|
214
231
|
};
|
|
215
232
|
}
|
|
216
233
|
throw error;
|
|
@@ -272,11 +289,11 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
272
289
|
const maxInlinePngBytes = 5 * 1024 * 1024;
|
|
273
290
|
server.registerTool('page_screenshot', {
|
|
274
291
|
title: 'Sayfanın ekran görüntüsünü al',
|
|
275
|
-
description: "Telefonda (mağaza kabuğu ya da oynatıcı) düşen akış hatasını bu araç görmez — onu mobile_error_list okur. ÖNCE KARESİZ DENE: 'kart göründü mü, tablo doldu mu, hata var mı' gibi olgu sorularında image:'none' verin — kare çekilmez, yanıttaki summaryText her kök düğümün görünürlüğünü, metnini, satır sayısını, açık sekmesini ve hata mesajlarını söyler (bir tam sayfa karesinin yüzlerce katı küçük). Kareyi yalnız görsel soru (renk, hizalama, referansa sadakat) için alın ve küçültün: clip ile bölge, scale:0.5 ile yarı çözünürlük; aynı sayfayı yeniden çekerken image:'ifChanged'. Studio arayüzünün runtime render rotasını (/dev ya da /view) headless Chromium'da açar ve sayfanın PNG ekran görüntüsünü inline görüntü olarak döner — kurduğun sayfayı GÖZLE doğrulamanın yolu budur (page_runtime_get paketi verir, bu tool ekranda ne ÇİZİLDİĞİNİ gösterir). Sayfaya bağlı middleware'ler gerçekten koşar; reddedilirse yanıtta middlewareBlocked işaretlenir ve Studio'nun çizdiği hata durumu karesi döner — bu da bir doğrulamadır. YÜKLENMEMİŞ SAYFA ASLA ÇEKİLMEZ: kare, runtime çizilip mount akışları bitince alınır (yanıtta pageState + readyAfterMs); bütçe dolduğunda sayfa hâlâ iskelette ya da akış koşuyorsa çağrı ne görüldüğünü söyleyen bir hatayla döner, boş/yarım kare dönmez. Sayfa akışı bir adımda düşerse (patlayan ifade, hata dönen uç) yanıt runtimeErrors taşır ve hangi komponentin hangi prop'unun YAZILMADIĞINI söyler — kare 'çalışıyor' görünürken sessizce boş kalan komponenti ancak bu yakalar. Bir komponent prop'unun İFADESİ patlarsa (Studio'da 'Expression error' toast'ı) yanıt expressionErrors taşır: {elementId, prop, expression, message, count}, öğe başına en çok iki; aynı satırın konsol kopyası consoleErrors'a düşmez. Sayfa ya da bir bileşen ÇİZİLEMEZSE neden yanıttadır: mobilde çöken düğüm renderErrors ({elementId, message}; sayfanın geri kalanı çizilir), yakalanmamış istisna pageErrors, konsol hatası consoleErrors — kare alınamadıysa bütün nedenler hata metninde. KARE DONUKTUR: tarayıcı bağlamı 'hareketi azalt' tercihiyle açılır; mobil runtime bileşen hareketini (giriş, döngü, geçiş, iskelet parıltısı, yükleme göstergesi) durdurur ve aynı sayfanın iki karesi tekrarlanabilir. Web sayfasında yalnız CSS'i prefers-reduced-motion'a uyan animasyonlar durur. Hareketin kendisini görmek için motion:'play'. Kare DİLİ ve TEMASI seçilebilir: 'language' adrese ?lang ekler (çeviriye bağlı prop'lar da çok dilli alanların satır değerleri de o dilde çözülür), 'colorScheme' ziyaretçinin tema tercihini sayfa açılmadan eker — ikisi de sayfaya dil değiştirici ya da theme-toggle koymayı GEREKTİRMEZ, yani referansa sadık bir tasarımın ikinci dili ve koyu teması bozulmadan kanıtlanır. STUDIO_BASE_URL tanımlı olmalı; ilk kullanım için `npx playwright-core install chromium` gerekir. DAL ORTAMI DA GÖRÜNTÜLENİR (2026-09-09'dan beri): 'development' /dev, 'production' /view, dal ise /branch/{dal} şeridinden açılır ve sayfanın BÜTÜN istekleri (şema da akışlar da) o dala gider — dalda kurulan sayfa artık merge'i beklemeden gözle doğrulanır (platform_guide('merge') §Dalda görsel doğrulama).",
|
|
292
|
+
description: "Telefonda (mağaza kabuğu ya da oynatıcı) düşen akış hatasını bu araç görmez — onu mobile_error_list okur. ÖNCE KARESİZ DENE: 'kart göründü mü, tablo doldu mu, hata var mı' gibi olgu sorularında image:'none' verin — kare çekilmez, yanıttaki summaryText her kök düğümün görünürlüğünü, metnini, satır sayısını, açık sekmesini ve hata mesajlarını söyler (bir tam sayfa karesinin yüzlerce katı küçük). Kareyi yalnız görsel soru (renk, hizalama, referansa sadakat) için alın ve küçültün: clip ile bölge, scale:0.5 ile yarı çözünürlük; aynı sayfayı yeniden çekerken image:'ifChanged'. Studio arayüzünün runtime render rotasını (/dev ya da /view) headless Chromium'da açar ve sayfanın PNG ekran görüntüsünü inline görüntü olarak döner — kurduğun sayfayı GÖZLE doğrulamanın yolu budur (page_runtime_get paketi verir, bu tool ekranda ne ÇİZİLDİĞİNİ gösterir). Sayfaya bağlı middleware'ler gerçekten koşar; reddedilirse yanıtta middlewareBlocked işaretlenir ve Studio'nun çizdiği hata durumu karesi döner — bu da bir doğrulamadır. YÜKLENMEMİŞ SAYFA ASLA ÇEKİLMEZ: kare, runtime çizilip mount akışları bitince alınır (yanıtta pageState + readyAfterMs); bütçe dolduğunda sayfa hâlâ iskelette ya da akış koşuyorsa çağrı ne görüldüğünü söyleyen bir hatayla döner, boş/yarım kare dönmez. Sayfa akışı bir adımda düşerse (patlayan ifade, hata dönen uç) yanıt runtimeErrors taşır ve hangi komponentin hangi prop'unun YAZILMADIĞINI söyler — kare 'çalışıyor' görünürken sessizce boş kalan komponenti ancak bu yakalar. Bir komponent prop'unun İFADESİ patlarsa (Studio'da 'Expression error' toast'ı) yanıt expressionErrors taşır: {elementId, prop, expression, message, count}, öğe başına en çok iki; aynı satırın konsol kopyası consoleErrors'a düşmez. Sayfa ya da bir bileşen ÇİZİLEMEZSE neden yanıttadır: mobilde çöken düğüm renderErrors ({elementId, message}; sayfanın geri kalanı çizilir), yakalanmamış istisna pageErrors, konsol hatası consoleErrors — kare alınamadıysa bütün nedenler hata metninde. KARE DONUKTUR: tarayıcı bağlamı 'hareketi azalt' tercihiyle açılır; mobil runtime bileşen hareketini (giriş, döngü, geçiş, iskelet parıltısı, yükleme göstergesi) durdurur ve aynı sayfanın iki karesi tekrarlanabilir. Web sayfasında yalnız CSS'i prefers-reduced-motion'a uyan animasyonlar durur. Hareketin kendisini görmek için motion:'play'. Kare DİLİ ve TEMASI seçilebilir: 'language' adrese ?lang ekler (çeviriye bağlı prop'lar da çok dilli alanların satır değerleri de o dilde çözülür), 'colorScheme' ziyaretçinin tema tercihini sayfa açılmadan eker — ikisi de sayfaya dil değiştirici ya da theme-toggle koymayı GEREKTİRMEZ, yani referansa sadık bir tasarımın ikinci dili ve koyu teması bozulmadan kanıtlanır. STUDIO_BASE_URL tanımlı olmalı; ilk kullanım için `npx playwright-core install chromium` gerekir. DAL ORTAMI DA GÖRÜNTÜLENİR (2026-09-09'dan beri): 'development' /dev, 'production' /view, dal ise /branch/{dal} şeridinden açılır ve sayfanın BÜTÜN istekleri (şema da akışlar da) o dala gider — dalda kurulan sayfa artık merge'i beklemeden gözle doğrulanır (platform_guide('merge') §Dalda görsel doğrulama). Production ve dalda uygulamanın slug'ı o ortamın kendi kaydıdır (dağıtıma/açılışa kadar eski slug; application_list development'ı döner): applicationSlug'a uygulama id'sini verin, gateway ortamın slug'ını okur (APP-002).",
|
|
276
293
|
inputSchema: {
|
|
277
294
|
project: projectParam,
|
|
278
295
|
environment: environmentParam,
|
|
279
|
-
applicationSlug:
|
|
296
|
+
applicationSlug: runtimeApplicationParam,
|
|
280
297
|
pageSlug: z.string().min(1).describe("Sayfanın slug'ı (page_list); sayfa parametresi sorguyla verilebilir: 'ilac-detay?id=7' (language '&lang=' olarak eklenir)"),
|
|
281
298
|
viewport: z.object({
|
|
282
299
|
width: z.number().int().min(320).max(3840),
|
|
@@ -326,7 +343,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
326
343
|
outPath: z.string().min(1).optional().describe('Verilirse PNG bu yerel yola yazılır ve inline görüntü DÖNMEZ; yalnız özet (yol, boyut) döner — büyük fullPage kareleri için')
|
|
327
344
|
},
|
|
328
345
|
annotations: readOnly
|
|
329
|
-
}, async ({ project, environment, applicationSlug, pageSlug, viewport, device, fullPage, delayMs, captureUnready, anonymous, asUser, asUserId, cookies, layoutReport, actions, outPath, expectFailure, language, colorScheme, motion, summary, image, format, quality, scale, clip, diff, session }) => {
|
|
346
|
+
}, async ({ project, environment, applicationSlug: applicationRef, pageSlug, viewport, device, fullPage, delayMs, captureUnready, anonymous, asUser, asUserId, cookies, layoutReport, actions, outPath, expectFailure, language, colorScheme, motion, summary, image, format, quality, scale, clip, diff, session }) => {
|
|
330
347
|
try {
|
|
331
348
|
if (anonymous === true && (asUser !== undefined || cookies !== undefined)) {
|
|
332
349
|
throw new PlatformApiError("page_screenshot: anonymous:true ile asUser/cookies birlikte verilemez — biri kimliksiz kareyi, diğeri belirli bir kimlikle çekilen kareyi ister. Anonim kare için yalnız anonymous:true, üye kimliğiyle kare için yalnız asUser (ve gerekiyorsa cookies) gönderin.");
|
|
@@ -398,6 +415,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
398
415
|
// 37 S-17 / 38 S-13: sorgulu slug (`ilac-detay?id=7`) dizeyle birleşince dil ikinci bir '?' ile
|
|
399
416
|
// ekleniyordu ve runtime `id`'yi '7?lang=tr' okudu; sayfa isteği işareti de sorguyu taşıdığı
|
|
400
417
|
// için sayfanın kendi veri isteğiyle hiç eşleşmiyordu.
|
|
418
|
+
const applicationSlug = await runtimeApplicationSlug(platform, project, applicationRef, environment);
|
|
401
419
|
const address = new URL(`${config.studioBaseUrl}/${lane}/${projectSlug}/${applicationSlug}/${pageSlug}`);
|
|
402
420
|
if (languageChoice)
|
|
403
421
|
address.searchParams.set('lang', languageChoice.code);
|
|
@@ -405,7 +423,8 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
405
423
|
const pagePath = pageSlug.split(/[?#]/, 1)[0];
|
|
406
424
|
// Hedef ve cihaz uygulamanın platform profilinden (G3): mobil uygulama react-native-web
|
|
407
425
|
// çizimidir, varsayılan kare telefon ön ayarıdır. Tip okunamazsa web davranışı aynen.
|
|
408
|
-
|
|
426
|
+
// Eski production slug'ı Core'da yoktur: id verildiyse tip id'den okunur, slug'la arama web'e düşerdi (APP-002).
|
|
427
|
+
const resolved = isGuid(applicationRef) ? await resolveProfile(platform, applicationRef) : await resolveProfileBySlug(platform, projectSlug, applicationSlug);
|
|
409
428
|
const target = captureTargetFor(resolved.profile.capture.target);
|
|
410
429
|
const mobile = resolved.profile.platform === 'mobile';
|
|
411
430
|
if (device !== undefined && !mobile) {
|
package/package.json
CHANGED