@appaflytech/zeyrek-ai-mcp 0.0.57 → 0.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/knowledge/storeImport.js +7 -2
- package/dist/platform/bodyFile.js +26 -1
- package/dist/platform/responses.js +2 -1
- package/dist/platform/strictArguments.js +7 -4
- package/dist/resources/guides/microfunction.md +8 -0
- package/dist/resources/guides/microservice.md +3 -1
- package/dist/resources/guides/mobile-publish.md +6 -2
- package/dist/resources/schema-fingerprint.json +3 -3
- package/dist/tools/microfunctions.js +58 -8
- package/dist/tools/microservices.js +8 -1
- package/dist/tools/mobileStore.js +1 -1
- package/dist/tools/mobileStoreImport.js +4 -4
- package/dist/tools/platformInfo.js +1 -1
- package/package.json +1 -1
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/** Backend `MobileStoreImportConstants.Section*` — rapor bölümleri */
|
|
7
7
|
export const STORE_IMPORT_SECTIONS = ['identity', 'texts', 'urls', 'appInfo', 'assets', 'declarations'];
|
|
8
|
+
/** Backend `MobileStoreImportConstants.IssueLockedKept` — ilk derlemenin kilitlediği, Uygula'nın yazmadığı satır */
|
|
9
|
+
export const LOCKED_KEPT_ISSUE = 'locked_kept';
|
|
8
10
|
/** Backend `MobileStoreDiffConstants.Choice*` */
|
|
9
11
|
export const STORE_DIFF_CHOICES = ['zeyrek', 'store'];
|
|
10
12
|
/** Backend `PurchaseOptions.ImportMaxBytes` */
|
|
@@ -17,8 +19,10 @@ const items = (value) => (Array.isArray(value) ? value : []).filter(isObject);
|
|
|
17
19
|
export function importReportOutline(record, section) {
|
|
18
20
|
const report = isObject(record.report) ? record.report : {};
|
|
19
21
|
const sections = items(report.sections);
|
|
22
|
+
const rowsOf = sections.flatMap(entry => items(entry.rows).map(row => ({ section: entry.key, row })));
|
|
23
|
+
const lockedKept = rowsOf.filter(({ row }) => Array.isArray(row.issues) && row.issues.includes(LOCKED_KEPT_ISSUE)).map(({ row }) => row.field);
|
|
20
24
|
const issues = sections.flatMap(entry => items(entry.rows)
|
|
21
|
-
.filter(row => Array.isArray(row.issues) && row.issues.
|
|
25
|
+
.filter(row => Array.isArray(row.issues) && row.issues.some(issue => issue !== LOCKED_KEPT_ISSUE))
|
|
22
26
|
.map(row => ({ section: entry.key, field: row.field, store: row.store, issues: row.issues, value: row.value, alternative: row.alternative })));
|
|
23
27
|
const violations = items(record.violations);
|
|
24
28
|
return {
|
|
@@ -28,7 +32,8 @@ export function importReportOutline(record, section) {
|
|
|
28
32
|
progress: record.progress,
|
|
29
33
|
summary: report.summary,
|
|
30
34
|
issues,
|
|
31
|
-
|
|
35
|
+
...(lockedKept.length > 0 ? { lockedKept: { fields: lockedKept, note: "İlk derleme bu alanları kilitledi: Uygula yazmaz, Zeyrek'teki korunur (MOB-037). Karar gerektirmez." } } : {}),
|
|
36
|
+
products: items(report.products).map((product, index) => ({ index, kind: product.kind, bucket: product.bucket, suggestedKey: product.suggestedKey, suggestedEntitlement: product.suggestedEntitlement, existingKey: product.existingKey, apple: product.apple, play: product.play, storeManaged: product.storeManaged, issues: product.issues })),
|
|
32
37
|
mapping: record.mapping,
|
|
33
38
|
...(violations.length > 0 ? { violations, next: `Taslak kaydedildi ama ${violations.length} kural ihlali var; mobile_store_import_apply bunlar düzelmeden 422 döner. Satırı düzeltip mobile_store_import_mapping'i yeniden çağırın.` } : {}),
|
|
34
39
|
...(section ? { rows: items(sections.find(entry => entry.key === section)?.rows) } : {}),
|
|
@@ -98,7 +98,7 @@ async function allowedRoots(config) {
|
|
|
98
98
|
* reddedilir. Bu pencereyi DARALTIR, kapatmaz — iki denetim arasında geri çevrilen bir bağ yine görülmez; tam çözüm
|
|
99
99
|
* (`openat2` RESOLVE_BENEATH) Node'da yoktur. Okunan bayt yine de açılan tutamaçtan gelir.
|
|
100
100
|
*/
|
|
101
|
-
async function readGuardedFile(tool, label, absolute, extensions, maxBytes, roots, config) {
|
|
101
|
+
async function readGuardedFile(tool, label, absolute, extensions, maxBytes, roots, config, tooLarge) {
|
|
102
102
|
const accepted = extensions.join(', ');
|
|
103
103
|
if (!extensions.includes(extname(absolute).toLowerCase())) {
|
|
104
104
|
throw new PlatformApiError(`${tool}: ${label} yalnız ${accepted} okur — verilen: ${absolute}. İstek gönderilmedi.`, 400);
|
|
@@ -145,6 +145,8 @@ async function readGuardedFile(tool, label, absolute, extensions, maxBytes, root
|
|
|
145
145
|
bytesRead += chunk.bytesRead;
|
|
146
146
|
}
|
|
147
147
|
if (bytesRead > maxBytes) {
|
|
148
|
+
if (tooLarge)
|
|
149
|
+
throw new PlatformApiError(`${tool}: ${label} ${tooLarge} İstek gönderilmedi.`, 400);
|
|
148
150
|
throw new PlatformApiError(maxBytes === maxBodyFileBytes
|
|
149
151
|
? `${tool}: ${label} 5 MB sınırını aşıyor. Tanımı parçalara bölün — sayfa için page_schema_patch'e birden çok batch, akış için flow_patch. İstek gönderilmedi.`
|
|
150
152
|
: `${tool}: ${label} ${maxBytes / (1024 * 1024)} MB sınırını aşıyor — ${real}. Kodu birden çok dosyaya bölün. İstek gönderilmedi.`, 400);
|
|
@@ -258,3 +260,26 @@ async function includeFiles(tool, value, folder, bodyBytes, roots, config) {
|
|
|
258
260
|
}
|
|
259
261
|
return { value: replaceInclusions(value, '', texts), files };
|
|
260
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* 2026-10-30, QM SAML girişi (ADR 0027): inline microfunction kodu dosyadan okunur. Paketlenmiş `saml-dogrula`
|
|
265
|
+
* 433 KB'tı; satır içi `code` onu bütünüyle ajan bağlamından geçirirdi. Dosya JSON değil ham kaynaktır (`.js` /
|
|
266
|
+
* `.py`) ve backend'in `MaxInlineCodeBytes` sınırı (`MicrofunctionOptions.cs`, 524288) burada da denetlenir.
|
|
267
|
+
*/
|
|
268
|
+
export const CODE_FILE_TOOLS = new Set(['microfunction_create', 'microfunction_update']);
|
|
269
|
+
const maxCodeFileBytes = 524288;
|
|
270
|
+
export function codeFileParam() {
|
|
271
|
+
return z.string().min(1).optional().describe("'code' yerine okunacak yerel kaynak dosyası (.js ya da .py, en çok 512 KB — backend MaxInlineCodeBytes): kod ajan bağlamından geçmez, gateway dosyanın metnini 'code' olarak gönderir. 'code' ile birlikte verilemez. Yol gateway sürecinin çalışma dizininin ya da MCP_INPUT_DIR'in altında olmalı (sembolik bağ çözülür). Yanıtın codeFile alanı hangi dosyanın okunduğunu söyler.");
|
|
272
|
+
}
|
|
273
|
+
export async function loadCodeFile(tool, args, config) {
|
|
274
|
+
if (!CODE_FILE_TOOLS.has(tool) || !args || typeof args !== 'object' || Array.isArray(args))
|
|
275
|
+
return { args };
|
|
276
|
+
const { codeFile, ...rest } = args;
|
|
277
|
+
if (typeof codeFile !== 'string' || codeFile.length === 0)
|
|
278
|
+
return { args };
|
|
279
|
+
if (rest.code !== undefined) {
|
|
280
|
+
throw new PlatformApiError(`${tool}: codeFile ile 'code' birlikte verilemez — kod ya dosyadan ya satır içinden gelir. Birini kaldırın. İstek gönderilmedi.`, 400);
|
|
281
|
+
}
|
|
282
|
+
const base = config.inputDirectory ? resolve(config.inputDirectory) : process.cwd();
|
|
283
|
+
const code = await readGuardedFile(tool, 'codeFile', resolve(base, codeFile), ['.js', '.py'], maxCodeFileBytes, await allowedRoots(config), config, "512 KB inline sınırını (backend MaxInlineCodeBytes) aşıyor. Paketi küçültün (minify, yinelenen bağımlılık) ya da sourceKind: 'archive' ile zip'i Studio'dan yükleyin.");
|
|
284
|
+
return { args: { ...rest, code: code.buffer.toString('utf8') }, note: `${code.real} → code (${code.buffer.length} B)` };
|
|
285
|
+
}
|
|
@@ -193,7 +193,8 @@ export function toolResult(payload, config, expectFailure) {
|
|
|
193
193
|
...(Object.keys(tokenRefs).length > 0 ? { tokenRefs, tokenRefNote: `Maskelenen JWT'ler gateway içinde tutuldu; bu referansları microservice_execute / microservice_debug / page_runtime_get / page_screenshot 'asUser' alanına verebilirsiniz (${tokenTtlMinutes()} dk geçerli; kompleks projede referans access_session_create'ten gelir). Jetonun kendisi yanıta ve günlüğe hiç girmez.` } : {}),
|
|
194
194
|
...(truncated ? { truncated: true, truncationNote: 'Liste, yanıt boyutu sınırı nedeniyle kırpıldı; sayfalama ya da daha dar filtre kullanın.' } : {}),
|
|
195
195
|
...(argumentNotes().aliasesApplied.length > 0 ? { aliasesApplied: argumentNotes().aliasesApplied } : {}),
|
|
196
|
-
...(argumentNotes().bodyFile ? { bodyFile: argumentNotes().bodyFile } : {})
|
|
196
|
+
...(argumentNotes().bodyFile ? { bodyFile: argumentNotes().bodyFile } : {}),
|
|
197
|
+
...(argumentNotes().codeFile ? { codeFile: argumentNotes().codeFile } : {})
|
|
197
198
|
};
|
|
198
199
|
return {
|
|
199
200
|
content: [{ type: 'text', text: JSON.stringify(enriched) }],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { diagnosticsParam, runWithDiagnostics } from './diagnostics.js';
|
|
3
3
|
import { applyArgumentAliases, closestArgument, runWithArgumentNotes } from './argumentAliases.js';
|
|
4
|
-
import { BODY_FILE_ARGUMENTS, bodyFileParam, loadBodyFile } from './bodyFile.js';
|
|
4
|
+
import { BODY_FILE_ARGUMENTS, CODE_FILE_TOOLS, bodyFileParam, codeFileParam, loadBodyFile, loadCodeFile } from './bodyFile.js';
|
|
5
5
|
import { PlatformApiError } from './errors.js';
|
|
6
6
|
import { logError } from '../observability/trace.js';
|
|
7
7
|
/** Doğrulanmış argümanlara takılan çeviri notları (`ArgumentNotes`); JSON'a ve platform isteğine girmez. */
|
|
@@ -46,15 +46,17 @@ export function rejectUnknownArguments(server, platform) {
|
|
|
46
46
|
const validate = validator.validateToolInput.bind(server);
|
|
47
47
|
validator.validateToolInput = async (tool, args, name) => {
|
|
48
48
|
try {
|
|
49
|
-
const
|
|
49
|
+
const body = await loadBodyFile(name, args, platform.config);
|
|
50
|
+
const code = await loadCodeFile(name, body.args, platform.config);
|
|
51
|
+
const loaded = { args: code.args, note: body.note };
|
|
50
52
|
const requiredBody = requiredBodies.get(name);
|
|
51
53
|
if (requiredBody && loaded.args?.[requiredBody] === undefined) {
|
|
52
54
|
throw new PlatformApiError(`${name}: '${requiredBody}' zorunlu — satır içinde ya da bodyFile ile (.json dosyası) verin. İstek gönderilmedi.`, 400);
|
|
53
55
|
}
|
|
54
56
|
const { args: aliased, applied } = await applyArgumentAliases(name, loaded.args, { platform, shape: tool.inputSchema?.shape ?? {} });
|
|
55
57
|
const parsed = await validate(tool, aliased, name);
|
|
56
|
-
const notes = { aliasesApplied: applied, ...(loaded.note ? { bodyFile: loaded.note } : {}) };
|
|
57
|
-
return (applied.length > 0 || loaded.note) && parsed && typeof parsed === 'object' ? Object.assign(parsed, { [ARGUMENT_NOTES]: notes }) : parsed;
|
|
58
|
+
const notes = { aliasesApplied: applied, ...(loaded.note ? { bodyFile: loaded.note } : {}), ...(code.note ? { codeFile: code.note } : {}) };
|
|
59
|
+
return (applied.length > 0 || loaded.note || code.note) && parsed && typeof parsed === 'object' ? Object.assign(parsed, { [ARGUMENT_NOTES]: notes }) : parsed;
|
|
58
60
|
}
|
|
59
61
|
catch (error) {
|
|
60
62
|
// Doğrulamadan önceki çeviri (dosya, takma ad) `handle`'ın dışında koşar: hata burada günlüğe yazılır.
|
|
@@ -81,6 +83,7 @@ export function rejectUnknownArguments(server, platform) {
|
|
|
81
83
|
const shape = {
|
|
82
84
|
...config.inputSchema,
|
|
83
85
|
...(bodyArgument && bodySchema ? { [bodyArgument]: bodySchema.optional(), bodyFile: bodyFileParam(bodyArgument) } : {}),
|
|
86
|
+
...(CODE_FILE_TOOLS.has(name) ? { codeFile: codeFileParam() } : {}),
|
|
84
87
|
...(exposesDiagnostics ? { diagnostics: diagnosticsParam } : {})
|
|
85
88
|
};
|
|
86
89
|
const invoke = (args, extra) => {
|
|
@@ -279,6 +279,14 @@ Paketler zip'teki `requirements.txt` / `package.json`'a yazılır ve Built-In ya
|
|
|
279
279
|
- **Tek parça zip yükleme ve indirme MCP'de yok:** zip'in içeriği dosya düzeyinde okunur ve yazılır
|
|
280
280
|
(§Zip kaynağı). Zip'in tamamı platform ucunda okunabilir durumdadır
|
|
281
281
|
(`GET /{proje}/microfunctions/{id}/archive`, `application/zip`) ve Studio'dan indirilir.
|
|
282
|
+
- **Büyük kod dosyadan verilir:** `microfunction_create` / `microfunction_update` `codeFile` ile `.js` / `.py` dosyasını
|
|
283
|
+
okur (inline sınırı **512 KB**, `MaxInlineCodeBytes`) ve metnini `code` olarak gönderir — kod ajan bağlamından geçmez; `code` ile birlikte verilemez. Yazma yanıtı kodu geri
|
|
284
|
+
getirmez (uzunluk + SHA-256 özeti); `microfunction_get` / `_list` 64 KB üstü kodu aynı özete indirir — yerel paketle
|
|
285
|
+
karşılaştırmak için özeti kullanın.
|
|
286
|
+
- **Ortam değişkeni başka kayıttan kopyalanır:** `environmentFrom: <microfunction id>` kaynağın değişkenlerini gateway'de
|
|
287
|
+
kopyalar, değerler yanıtta `***` döner; `environmentVariables` aynı anahtarda kazanır. **Güncellemede değişkenleri
|
|
288
|
+
korumanın yolu budur** (`environmentFrom` = kaydın kendi id'si): PUT gövdesinde `environmentVariables` yoksa platform
|
|
289
|
+
hepsini siler.
|
|
282
290
|
- `timeoutSeconds` ve `memoryMb` komutta non-nullable int'tir: **göndermezseniz 0 gider ve platform
|
|
283
291
|
422 döner.** Gateway değer uydurmaz, eksikliği çağrıdan önce söyler (MF-001, block; ürün
|
|
284
292
|
varsayılanları 30 / 256). Aynı kural provider ↔ `runtimeId` eşleşmesini, inline kaynakta `code`
|
|
@@ -124,7 +124,9 @@ servis formunda görünür ve `microservice_get` outline görünümünde geri ok
|
|
|
124
124
|
adım adım executionItems ağacı döner. Zamanlanmış (`schedule-job`) uçta sıra farklıdır:
|
|
125
125
|
`workflow_preview` → `microservice_debug` → `microservice_schedule_control` `trigger` →
|
|
126
126
|
`microservice_job_history`; geçmişteki `completed` başarı kanıtı DEĞİLDİR (§Zamanlanmış işin
|
|
127
|
-
gerçek sonucunu görmek).
|
|
127
|
+
gerçek sonucunu görmek). **`microservice_debug` production'da kapalıdır** (adım izi giriş
|
|
128
|
+
istemeden okunur; platform 404 döner, gateway isteği göndermez): debug development'ta ya da
|
|
129
|
+
dalda yapılır, production'da `microservice_execute` + `microservice_job_history` kalır.
|
|
128
130
|
Çağrı parametrelerini (sorgu parametresi, filtre değeri) üst düzey `parameters: { ad: değer }`
|
|
129
131
|
ile ver; gateway GET'te query-string'e, POST/PUT'ta gövdeye koyar. Değeri NULL bağlanan query
|
|
130
132
|
parametresini debug yanıtı `unboundParameters` ile söyler — ağaçta o satırın `value` anahtarı
|
|
@@ -329,13 +329,17 @@ ve Play iç test satın almaları `environment: test` etiketiyle, `includeTest:
|
|
|
329
329
|
## Mağazadan taşıma
|
|
330
330
|
|
|
331
331
|
Yayındaki bir uygulama (App Store / Google Play) Zeyrek'e **bir kez okunur, bir kez onaylanır**; sonra tek kaynak Zeyrek'tir
|
|
332
|
-
(kök ADR 0028).
|
|
332
|
+
(kök ADR 0028). İlk derlemeden sonra da yeniden okunabilir ve uygulanabilir (MOB-037); ilk derlemenin kilitlediği alanlar
|
|
333
|
+
(Bundle ID, paket adı, URL şeması, iOS build numarası, Android version code) ve sürüm Zeyrek'teki değerde kalır, farklı
|
|
334
|
+
Bundle ID / paket adı 409 verir. Aynı Bundle ID / paket adıyla **yeniden eşlenebilir**: yeni okuma açılır, `replace.<satır>` (tek görsellik yuva) ve `replace.asset.screenshot.<dil>.<cihaz>` (ekran görüntüsü seti)
|
|
335
|
+
dolu yuvaları mağazadakiyle değiştirir, raporda `existingKey` dolu ürün yeni ürün açmadan güncellenir.
|
|
333
336
|
|
|
334
337
|
1. `mobile_store_import_start { applicationId, bundleId, packageName }` → bağlantı satırı ve okuma işi.
|
|
335
338
|
2. `mobile_store_import_report` → `state: ready` olunca kovalar: **auto** (Zeyrek'e gelir) · **upload** (siz yüklersiniz:
|
|
336
339
|
ikon, 6.5" ekran görüntüsü) · **kept** (mağazada kalır: beyan içerikleri, ülke fiyatı, promosyon, tüketilen ürün).
|
|
337
340
|
3. `mobile_store_import_mapping` → `scheme` (eski uygulamanın URL şeması, zorunlu), iki mağaza farkı
|
|
338
|
-
(`choice.<satır>`), ürün anahtarı/hak (`product.<i>.key|entitlement`), beyan onayı.
|
|
341
|
+
(`choice.<satır>`), ürün anahtarı/hak (`product.<i>.key|entitlement`; `existingKey` dolu üründe yok sayılır), beyan onayı.
|
|
342
|
+
Mağaza kimliği seçilmez (MOB-038).
|
|
339
343
|
4. `mobile_store_import_apply` → tek işlem; mağazaya hiçbir şey gitmez. Örnek: `store-import-mapping`.
|
|
340
344
|
|
|
341
345
|
**Aboneler — üç kaynak:** eski liste (`mobile_purchase_import`, önce `dryRun` — MOB-039) · mağaza bildirimi (katalogdaki
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"hash": "
|
|
3
|
-
"generatedAt": "2026-11-
|
|
2
|
+
"hash": "34361b013283a252757338ed07d8cdcd70294df1c334d35723a9b194c62d1c4b",
|
|
3
|
+
"generatedAt": "2026-11-03T11:41:34.837Z",
|
|
4
4
|
"types": 377,
|
|
5
5
|
"files": 196,
|
|
6
6
|
"note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
|
|
@@ -9,4 +9,4 @@
|
|
|
9
9
|
"System.Text.Json.Nodes.JsonNode",
|
|
10
10
|
"System.Text.Json.Nodes.JsonNodeOptions"
|
|
11
11
|
]
|
|
12
|
-
}
|
|
12
|
+
}
|
|
@@ -4,7 +4,9 @@ import { unmappedMembers, withUnmappedMembers } from '../knowledge/schemaGuard.j
|
|
|
4
4
|
import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
|
|
5
5
|
import { policyRules, microfunctionArchiveEntrypointFindings } from '../knowledge/rules.js';
|
|
6
6
|
import { handle } from '../platform/responses.js';
|
|
7
|
+
import { PlatformApiError } from '../platform/errors.js';
|
|
7
8
|
import { z } from 'zod';
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
8
10
|
const policyOverrideParam = z.boolean().optional()
|
|
9
11
|
.describe("true: politika block bulgularını warn'a indirir (bilinçli istisna; yanıtta işaretlenir)");
|
|
10
12
|
const behaviorParam = z.enum(['primitive', 'object', 'array']);
|
|
@@ -29,6 +31,7 @@ const definitionInput = {
|
|
|
29
31
|
timeoutSeconds: z.number().int().min(1).max(900).optional().describe("Fonksiyon bütçesi; varsayılan 30. Built-In üst sınırı 300, Lambda 900. Bütçe dolarsa çağrı MicrofunctionInvocationTimeoutException ile step'in fail dalına düşer"),
|
|
30
32
|
memoryMb: z.number().int().min(128).max(3008).optional().describe("Varsayılan 256. Built-In'de yalnız kaydedilir (küme environment limiti geçerlidir); Lambda'da gerçekten uygulanır"),
|
|
31
33
|
environmentVariables: environmentVariablesParam.optional(),
|
|
34
|
+
environmentFrom: guid.optional().describe("Ortam değişkenlerini bu microfunction'dan kopyalar: gateway kaynağı okur, değerler ajan bağlamından geçmez ve yanıtta '***' döner. environmentVariables ile birlikte verilirse aynı anahtarda environmentVariables kazanır. GÜNCELLEMEDE değişkenleri korumak için kaydın kendi id'sini verin: PUT gövdesinde environmentVariables yoksa platform hepsini siler (Microfunction.cs, environmentVariables ?? [])"),
|
|
32
35
|
dependencies: z.string().optional().describe("Inline kaynağın dış paketleri, SATIR BAŞINA BİR PAKET (≤ 16 KB; boş ve '#' satırları yorum). python: requirements.txt satırı olduğu gibi — 'pillow==11.1.0', 'requests>=2.32'; pip seçeneği ('-r', '--index-url', '-e') YASAK. node: 'ad@aralık' — 'sharp@^0.33.0', '@scope/ad@1.2', aralıksız 'dayjs' → latest. Built-In'de yayın paketleri Fission builder'da kurar: yayın build süresince (5 dk'ya kadar) 'publishing' kalır, kurulum düşerse publish-failed ve publishError'da build günlüğünün son 4 KB'ı. aws-lambda'da KURULMAZ (MF-007) — paketler zip'te gelmeli. archive kaynağında yok sayılır (manifest zip'te). Built-In Python 3.13, Node 22 (Lambda'daki sürümlerle aynı); C uzantılı paketler (Pillow gibi) hazır wheel'den kurulur"),
|
|
33
36
|
testArguments: z.string().optional().describe("Studio Test call'unun kayıtlı argüman metni (≤ 64 KB, MicrofunctionTestArgumentsTooLarge); olduğu gibi saklanır, JSON geçerliliği DENETLENMEZ ve koşuma girmez. update TAM GÖVDEDİR: verilmezse kayıtlı metin silinir — önce microfunction_get'ten alın. Kaydetmek revision'ı artırır (yayındaki kayıt hasUnpublishedChanges: true olur)"),
|
|
34
37
|
publish: z.boolean().optional().describe("true: kayıt açıldıktan/güncellendikten sonra aynı çağrıda yayın istenir ('Kaydet ve yayınla'). Yayın ASENKRONDUR — çağrı status: 'publishing' döner; sonucu microfunction_get ile izleyin (published | publish-failed + publishError)")
|
|
@@ -48,6 +51,51 @@ const runtimeInput = {
|
|
|
48
51
|
secretAccessKey: z.string().min(1).describe("AWS secret access key. Kayıt düz metin tutulur; platform DTO'da maskeler (**** + son 4), gateway bu tool'ların yanıtında değeri tümüyle '***' yapar"),
|
|
49
52
|
roleArn: z.string().optional().describe("Lambda execution rolü: arn:aws:iam::<12 hane>:role/<ad>. Rolü müşteri oluşturur; fonksiyon bu rolle koşar")
|
|
50
53
|
};
|
|
54
|
+
// 2026-10-30, QM SAML girişi (ADR 0027): kayıtlı bir anahtarı (OIDC_SECRET_KEY) ikinci bir microfunction'a taşımak değeri çağrıya
|
|
55
|
+
// düz yazmayı gerektiriyordu; güncellemede de PUT gövdesi değişkensizse platform hepsini siliyor. Kopya gateway'de yapılır ve
|
|
56
|
+
// yanıttaki değerler maskelenir — değer ne çağrıya ne yanıta girer.
|
|
57
|
+
async function withEnvironmentFrom(platform, project, environment, body, sourceId) {
|
|
58
|
+
if (!sourceId)
|
|
59
|
+
return body;
|
|
60
|
+
let source;
|
|
61
|
+
try {
|
|
62
|
+
source = await platform.tenant('GET', project, `/microfunctions/${sourceId}`, { environment });
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
throw new PlatformApiError(`environmentFrom (${sourceId}) okunamadı: ${error instanceof Error ? error.message : String(error)}. Kaynak kayıt bu projede ve bu ortamda olmalı (microfunction_list). İstek gönderilmedi.`, 400);
|
|
66
|
+
}
|
|
67
|
+
const record = source?.data ?? source;
|
|
68
|
+
const copied = record?.environmentVariables;
|
|
69
|
+
const variables = copied && typeof copied === 'object' && !Array.isArray(copied) ? copied : {};
|
|
70
|
+
return { ...body, environmentVariables: { ...variables, ...body.environmentVariables } };
|
|
71
|
+
}
|
|
72
|
+
// 2026-10-30 canlı koşu: platform yazılan kaydı `code` ile birlikte döndürüyor; codeFile ile gönderilen 433 KB'lık saml-dogrula
|
|
73
|
+
// yanıtta 283 bin karakter olarak geri geldi. Yazmada kod hiç yansıtılmaz (çağıran onu gönderdi); okumada 64 KB üstü kod özetlenir.
|
|
74
|
+
const codeSummaryBytes = 64 * 1024;
|
|
75
|
+
function summarizeCode(value, always) {
|
|
76
|
+
if (!value || typeof value !== 'object')
|
|
77
|
+
return value;
|
|
78
|
+
if (Array.isArray(value))
|
|
79
|
+
return value.map(item => summarizeCode(item, always));
|
|
80
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
|
|
81
|
+
if (key !== 'code' || typeof entry !== 'string')
|
|
82
|
+
return [key, summarizeCode(entry, always)];
|
|
83
|
+
const bytes = Buffer.byteLength(entry, 'utf8');
|
|
84
|
+
if (!always && bytes <= codeSummaryBytes)
|
|
85
|
+
return [key, entry];
|
|
86
|
+
return [key, `[kod yanıtta gösterilmez: ${bytes} bayt, sha256 ${createHash('sha256').update(entry, 'utf8').digest('hex')}]`];
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
function maskEnvironmentValues(value, active) {
|
|
90
|
+
if (!active || !value || typeof value !== 'object')
|
|
91
|
+
return value;
|
|
92
|
+
if (Array.isArray(value))
|
|
93
|
+
return value.map(item => maskEnvironmentValues(item, active));
|
|
94
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key,
|
|
95
|
+
key === 'environmentVariables' && entry && typeof entry === 'object' && !Array.isArray(entry)
|
|
96
|
+
? Object.fromEntries(Object.keys(entry).map(name => [name, '***']))
|
|
97
|
+
: maskEnvironmentValues(entry, active)]));
|
|
98
|
+
}
|
|
51
99
|
export function registerMicrofunctionTools(server, platform) {
|
|
52
100
|
const config = platform.config;
|
|
53
101
|
server.registerTool('microfunction_list', {
|
|
@@ -61,35 +109,37 @@ export function registerMicrofunctionTools(server, platform) {
|
|
|
61
109
|
...pagingParams
|
|
62
110
|
},
|
|
63
111
|
annotations: readOnly
|
|
64
|
-
}, handle(config, ({ project, environment, status, searchTerm, pageIndex, pageLength, order, isDescending }) => platform.tenant('GET', project, '/microfunctions', {
|
|
112
|
+
}, handle(config, async ({ project, environment, status, searchTerm, pageIndex, pageLength, order, isDescending }) => summarizeCode(await platform.tenant('GET', project, '/microfunctions', {
|
|
65
113
|
environment,
|
|
66
114
|
query: { status, searchTerm, pageIndex: pageIndex ?? 0, pageLength: pageLength ?? 200, order, isDescending }
|
|
67
|
-
})));
|
|
115
|
+
}), false)));
|
|
68
116
|
server.registerTool('microfunction_get', {
|
|
69
117
|
title: 'Microfunction getir',
|
|
70
|
-
description: "Tek bir microfunction'ın tanımını ve yayın durumunu getirir: status, revision/publishedRevision, hasUnpublishedChanges (kayıt yayında ama üstüne yazılmış), publishError, parameterScheme/returnScheme. Yayın asenkron olduğu için 'publishing' durumunda birkaç saniye arayla tekrar çağırın.",
|
|
118
|
+
description: "Tek bir microfunction'ın tanımını ve yayın durumunu getirir: status, revision/publishedRevision, hasUnpublishedChanges (kayıt yayında ama üstüne yazılmış), publishError, parameterScheme/returnScheme. Yayın asenkron olduğu için 'publishing' durumunda birkaç saniye arayla tekrar çağırın. 64 KB üstü kod yanıtta uzunluk ve SHA-256 özetine indirilir; yerel paketle karşılaştırmak için özeti kullanın.",
|
|
71
119
|
inputSchema: { project: projectParam, environment: environmentParam, id: guid },
|
|
72
120
|
annotations: readOnly
|
|
73
|
-
}, handle(config, ({ project, environment, id }) => platform.tenant('GET', project, `/microfunctions/${id}`, { environment })));
|
|
121
|
+
}, handle(config, async ({ project, environment, id }) => summarizeCode(await platform.tenant('GET', project, `/microfunctions/${id}`, { environment }), false)));
|
|
74
122
|
server.registerTool('microfunction_create', {
|
|
75
123
|
title: 'Microfunction tanımla',
|
|
76
124
|
description: "Yeni microfunction kaydeder. Kayıt DRAFT doğar; yayına almadan hiçbir workflow step'i onu koşturamaz (publish: true ile aynı çağrıda yayın isteyebilirsiniz). Yayın adımı kodu runtime'a kurar ve bir 'probe' çağrısıyla duman testi yapar: sözdizimi/import hatası yayın anında publish-failed olur, koşumda değil. Protokol ve sınırlar: platform_guide('microfunction'). " + constraintSummary('CreateMicrofunctionCommand'),
|
|
77
125
|
inputSchema: { project: projectParam, environment: environmentParam, ...definitionInput, policyOverride: policyOverrideParam },
|
|
78
126
|
annotations: writes
|
|
79
|
-
}, handle(config, async ({ project, environment, policyOverride, ...
|
|
127
|
+
}, handle(config, async ({ project, environment, policyOverride, environmentFrom, ...input }) => {
|
|
128
|
+
const body = await withEnvironmentFrom(platform, project, environment, input, environmentFrom);
|
|
80
129
|
const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'microfunction', body, { operation: 'create' }, policyOverride));
|
|
81
130
|
const drift = unmappedMembers('CreateMicrofunctionCommand', body);
|
|
82
|
-
return attachFindings(withUnmappedMembers(await platform.tenant('POST', project, '/microfunctions', { environment, body }), drift), findings);
|
|
131
|
+
return summarizeCode(maskEnvironmentValues(attachFindings(withUnmappedMembers(await platform.tenant('POST', project, '/microfunctions', { environment, body }), drift), findings), environmentFrom !== undefined), true);
|
|
83
132
|
}));
|
|
84
133
|
server.registerTool('microfunction_update', {
|
|
85
134
|
title: 'Microfunction güncelle',
|
|
86
135
|
description: "Microfunction'ın TAMAMINI değiştirir (PUT — değiştirmediğiniz alanları da gönderin; önce microfunction_get). Her güncelleme revision'ı artırır: yayındaki kayıt güncellenirse runtime ESKİ sürümü koşmaya devam eder ve kayıt hasUnpublishedChanges: true olur — yeni kodu canlıya almak için publish: true verin ya da microfunction_publish çağırın. PROVIDER YAYINDAYKEN DEĞİŞMEZ: bir kez yayınlanmış kaydın provider'ını değiştiren PUT 400 döner (MicrofunctionProviderChangeNotAllowedException) — kaydı silip yeniden oluşturun. " + constraintSummary('UpdateMicrofunctionCommand'),
|
|
87
136
|
inputSchema: { project: projectParam, environment: environmentParam, id: guid, ...definitionInput, policyOverride: policyOverrideParam },
|
|
88
137
|
annotations: updates
|
|
89
|
-
}, handle(config, async ({ project, environment, id, policyOverride, ...
|
|
138
|
+
}, handle(config, async ({ project, environment, id, policyOverride, environmentFrom, ...input }) => {
|
|
139
|
+
const body = await withEnvironmentFrom(platform, project, environment, input, environmentFrom);
|
|
90
140
|
const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'microfunction', body, { operation: 'update' }, policyOverride));
|
|
91
141
|
const drift = unmappedMembers('UpdateMicrofunctionCommand', body);
|
|
92
|
-
return attachFindings(withUnmappedMembers(await platform.tenant('PUT', project, `/microfunctions/${id}`, { environment, body }), drift), findings);
|
|
142
|
+
return summarizeCode(maskEnvironmentValues(attachFindings(withUnmappedMembers(await platform.tenant('PUT', project, `/microfunctions/${id}`, { environment, body }), drift), findings), environmentFrom !== undefined), true);
|
|
93
143
|
}));
|
|
94
144
|
server.registerTool('microfunction_publish', {
|
|
95
145
|
title: 'Microfunction yayınla',
|
|
@@ -478,10 +478,17 @@ export function registerMicroserviceTools(server, platform) {
|
|
|
478
478
|
}));
|
|
479
479
|
server.registerTool('microservice_debug', {
|
|
480
480
|
title: 'Microservice debug',
|
|
481
|
-
description: "Microservice'i debug modunda çalıştırır; yanıtta adım adım executionItems ağacı döner (her adımın girdi/çıktısı). executionItems YALNIZ ucun kendi adımlarını kapsar — ucun ÖNÜNDE koşan middleware'ler ayrıca 'middlewares' alanında adım özetiyle listelenir (09-04 Y-04). Değeri NULL bağlanan query parametreleri 'unboundParameters' alanında adıyla listelenir (ağaçta bu satırların value anahtarı görünmez). Preview semantiğiyle koşar: tek transaction + sonunda rollback, kalıcı satır yazmaz. httpStatus akışın kendi sonucudur (4xx/5xx'te runtimeErrors eklenir); ağaç düğümlerinde hata bayrağı YOKTUR — patlayan adımın output'u boştur, hata gövdesi hata dalındaki response adımının çıktısındadır. Zamanlanmış (schedule-job) uçlar da slug yoluyla debug edilir (method: 'POST', parametresiz): job geçmişi hatayı yalnız mesaj olarak yazar, adım adım girdi/çıktı burada görülür (platform_guide('microservice') §Zamanlanmış işin gerçek sonucunu görmek). Kompleks projede kullanıcı gözü asUserId (parolasız) ya da asUser ile verilir ve yanıta 'accessDecision' eklenir: { decision: allow | deny | not-enforced | unknown, mode, code?, permission?, reason, next } — 401/403'ün erişim kararından mı geldiğini ve sıradaki adımı söyler.",
|
|
481
|
+
description: "Microservice'i debug modunda çalıştırır; yanıtta adım adım executionItems ağacı döner (her adımın girdi/çıktısı). PRODUCTION'DA KAPALIDIR: istek gönderilmez, development'ta ya da dalda debug edin. executionItems YALNIZ ucun kendi adımlarını kapsar — ucun ÖNÜNDE koşan middleware'ler ayrıca 'middlewares' alanında adım özetiyle listelenir (09-04 Y-04). Değeri NULL bağlanan query parametreleri 'unboundParameters' alanında adıyla listelenir (ağaçta bu satırların value anahtarı görünmez). Preview semantiğiyle koşar: tek transaction + sonunda rollback, kalıcı satır yazmaz. httpStatus akışın kendi sonucudur (4xx/5xx'te runtimeErrors eklenir); ağaç düğümlerinde hata bayrağı YOKTUR — patlayan adımın output'u boştur, hata gövdesi hata dalındaki response adımının çıktısındadır. Zamanlanmış (schedule-job) uçlar da slug yoluyla debug edilir (method: 'POST', parametresiz): job geçmişi hatayı yalnız mesaj olarak yazar, adım adım girdi/çıktı burada görülür (platform_guide('microservice') §Zamanlanmış işin gerçek sonucunu görmek). Kompleks projede kullanıcı gözü asUserId (parolasız) ya da asUser ile verilir ve yanıta 'accessDecision' eklenir: { decision: allow | deny | not-enforced | unknown, mode, code?, permission?, reason, next } — 401/403'ün erişim kararından mı geldiğini ve sıradaki adımı söyler.",
|
|
482
482
|
inputSchema: executeInput,
|
|
483
483
|
annotations: writes
|
|
484
484
|
}, handle(config, async ({ project, environment, application, microservice, method, routeParams, query, body, parameters, headers, anonymous, asUser, asUserId, cookies, language }) => {
|
|
485
|
+
// Debug rotası [AllowAnonymous]'tır ve adım izini (girdi/çıktı, okunan sırlar dahil) döner;
|
|
486
|
+
// backend production'da 404 verir (DebugMicroserviceCommand, 2026-10-28). Gateway isteği
|
|
487
|
+
// göndermeden durur: o backend dağıtılmadan önce de iz production'dan okunmaz ve ara katmanlar
|
|
488
|
+
// ile dış çağrılar (http adımı, AD bind) koşmaz.
|
|
489
|
+
if (platform.resolveEnvironment(environment) === 'production') {
|
|
490
|
+
throw new PlatformApiError("microservice_debug: debug production'da kapalı — adım izi giriş istemeden okunabildiği için platform bu rotayı production'da reddeder; istek gönderilmedi. Akışı development'ta ya da bir dalda debug edin. Production'da sonucu microservice_execute ile, zamanlanmış işin hatasını microservice_job_history ile görün.", 400);
|
|
491
|
+
}
|
|
485
492
|
const notes = [];
|
|
486
493
|
const eye = await impersonationHeaders(platform, 'microservice_debug', project, environment, { asUserId, asUser, anonymous, ownAuthorization: hasAuthorization(headers) }, { runtime: true });
|
|
487
494
|
const member = anonymous === true ? asUser : await resolveIdentity(asUser, platform, project, environment);
|
|
@@ -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 (
|
|
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 (kare, ≥ 512×512 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"),
|
|
@@ -18,7 +18,7 @@ export function registerMobileStoreImportTools(server, platform) {
|
|
|
18
18
|
const config = platform.config;
|
|
19
19
|
server.registerTool('mobile_store_import_start', {
|
|
20
20
|
title: 'Mağazadan içe aktarmayı başlat',
|
|
21
|
-
description: "Yayındaki uygulamanın App Store ve Google Play kaydını OKUMAYA başlar (POST mobile-store/{appId}/imports; ADR 0028). Önce bağlantı eşzamanlı doğrulanır (connection { ios: found | not_found | no_access, android: … }, upload anahtarı uyarısı), sonra arka plan işi iki mağazayı okur ve üç kovalı rapor yazar (Gelir · Sen yüklersin · Mağazada kalır). Okuma mağazaya HİÇBİR ŞEY yazmaz, Zeyrek'te de hiçbir şey değişmez — değişiklik yalnız mobile_store_import_apply ile olur.
|
|
21
|
+
description: "Yayındaki uygulamanın App Store ve Google Play kaydını OKUMAYA başlar (POST mobile-store/{appId}/imports; ADR 0028). Önce bağlantı eşzamanlı doğrulanır (connection { ios: found | not_found | no_access, android: … }, upload anahtarı uyarısı), sonra arka plan işi iki mağazayı okur ve üç kovalı rapor yazar (Gelir · Sen yüklersin · Mağazada kalır). Okuma mağazaya HİÇBİR ŞEY yazmaz, Zeyrek'te de hiçbir şey değişmez — değişiklik yalnız mobile_store_import_apply ile olur. İlk derlemeden sonra da çalışır: yeniden okuma (yeniden eşleme) ve Uygula her zaman açıktır; ilk derlemenin kilitlediği alanlar (Bundle ID, paket adı, URL şeması, iOS build numarası, Android version code) ve sürüm Zeyrek'teki değerde kalır. Kilitli kimlikten farklı bundleId / packageName verilirse 409 MobileStoreImportLockedException (MOB-037). Yeni okuma açık eski kaydı vazgeçilmiş sayar. Sonra mobile_store_import_report ile durumu okuyun (state queued → reading → ready).",
|
|
22
22
|
inputSchema: {
|
|
23
23
|
applicationId,
|
|
24
24
|
bundleId: z.string().min(1).optional().describe("App Store'daki Bundle ID ('com.anatoli.app'); yalnız Android ise verilmez"),
|
|
@@ -34,7 +34,7 @@ export function registerMobileStoreImportTools(server, platform) {
|
|
|
34
34
|
}
|
|
35
35
|
catch (error) {
|
|
36
36
|
if (error instanceof PlatformApiError && error.status === 409) {
|
|
37
|
-
throw new PlatformApiError(`${error.message} — MOB-037:
|
|
37
|
+
throw new PlatformApiError(`${error.message} — MOB-037: verilen Bundle ID / paket adı uygulamanın kilitlediğinden farklı; yeniden eşleme için kilitli Bundle ID / paket adını verin (mobile_store_get ile okunur).`, 409, error.code);
|
|
38
38
|
}
|
|
39
39
|
throw error;
|
|
40
40
|
}
|
|
@@ -54,7 +54,7 @@ export function registerMobileStoreImportTools(server, platform) {
|
|
|
54
54
|
}));
|
|
55
55
|
server.registerTool('mobile_store_import_mapping', {
|
|
56
56
|
title: 'İçe aktarma seçimlerini kaydet',
|
|
57
|
-
description: "Raporun Zeyrek'e nasıl yazılacağını kaydeder (PUT mobile-store/{appId}/imports/{importId}/mapping; seçimler birleşmez, verilen sözlük kaydı değiştirir — önce mobile_store_import_report ile okuyun). Anahtarlar: scheme (ZORUNLU — URL şeması mağazadan okunamaz, eski uygulamanınki girilir; MOB-041/P-31) · choice.<satır> = apple | play | custom ve custom.<satır> (iki mağazada farklı metin, K6) · skip.<satır> / skip.language.<dil> = true · replace.<satır> = true (ikon, adaptive ön plan ya da öne çıkan görsel yuvası doluysa üzerine yazar; verilmezse mevcut görsel korunur) · target.language.<mağaza>.<dil> = <Zeyrek dili> (eşlenemeyen dilin metnini o dile yazar; eşlenmiş satırı ezmez) · product.<i>.{key, entitlement, period, trial, price, skip} (i = rapordaki ürün sırası; anahtar ve hak ^[a-z][a-z0-9_]{1,63}$) · declaration.<anahtar> = true. Taslak kural ihlaliyle de saklanır: ihlaller yanıtın violations listesinde [{ row, field, code }] döner (choice_required, period_required, price_required, key_taken…) ve mobile_store_import_apply onlar düzelmeden 422 verir. İçe aktarılan ürünün MAĞAZA KİMLİĞİ seçilmez — mağazadan gelir ve kilitlidir (MOB-038).",
|
|
57
|
+
description: "Raporun Zeyrek'e nasıl yazılacağını kaydeder (PUT mobile-store/{appId}/imports/{importId}/mapping; seçimler birleşmez, verilen sözlük kaydı değiştirir — önce mobile_store_import_report ile okuyun). Anahtarlar: scheme (ZORUNLU — URL şeması mağazadan okunamaz, eski uygulamanınki girilir; MOB-041/P-31) · choice.<satır> = apple | play | custom ve custom.<satır> (iki mağazada farklı metin, K6) · skip.<satır> / skip.language.<dil> = true · replace.<satır> = true (ikon, adaptive ön plan ya da öne çıkan görsel yuvası doluysa üzerine yazar; verilmezse mevcut görsel korunur) · replace.asset.screenshot.<dil>.<cihaz> = true (o yuvadaki ekran görüntüsü setini siler, mağazadakileri yazar; verilmezse mevcutlar korunur ve yuva başına 10 sınırı mevcutları da sayar — taşan satır asset_invalid) · target.language.<mağaza>.<dil> = <Zeyrek dili> (eşlenemeyen dilin metnini o dile yazar; eşlenmiş satırı ezmez) · product.<i>.{key, entitlement, period, trial, price, skip} (i = rapordaki ürün sırası; anahtar ve hak ^[a-z][a-z0-9_]{1,63}$) · declaration.<anahtar> = true. Taslak kural ihlaliyle de saklanır: ihlaller yanıtın violations listesinde [{ row, field, code }] döner (choice_required, period_required, price_required, key_taken…) ve mobile_store_import_apply onlar düzelmeden 422 verir. İçe aktarılan ürünün MAĞAZA KİMLİĞİ seçilmez — mağazadan gelir ve kilitlidir (MOB-038). Raporda existingKey dolu ürün Zeyrek'te aynı mağaza kimliğiyle duran üründür: Uygula yeni ürün açmaz, onu günceller (fiyat, süre, deneme, metin, mağaza modeli); anahtar ve hak Zeyrek'tekidir, product.<i>.key / entitlement yok sayılır; tür farklıysa kind ihlali.",
|
|
58
58
|
inputSchema: {
|
|
59
59
|
applicationId,
|
|
60
60
|
importId,
|
|
@@ -69,7 +69,7 @@ export function registerMobileStoreImportTools(server, platform) {
|
|
|
69
69
|
}));
|
|
70
70
|
server.registerTool('mobile_store_import_apply', {
|
|
71
71
|
title: 'İçe aktarmayı uygula',
|
|
72
|
-
description: "Onaylanan raporu TEK İŞLEMDE Zeyrek'e yazar (POST mobile-store/{appId}/imports/{importId}/apply): mağaza ayarı, beyanlar (kaynak import), görseller, ürün kataloğu (saklanan mağaza kimlikleriyle, değişiklik beklemez) ve kimlik kilidi; mağaza tabanı farka kaydedilir. Mağazaya HİÇBİR ŞEY gönderilmez. Uygulanmış kayıt yeniden uygulanmaz (409). Sonra: mobile_product_list (içe aktarılan ürünler), yayından önce mobile_store_diff ve ön kontrol P-24…P-31.",
|
|
72
|
+
description: "Onaylanan raporu TEK İŞLEMDE Zeyrek'e yazar (POST mobile-store/{appId}/imports/{importId}/apply): mağaza ayarı, beyanlar (kaynak import), görseller, ürün kataloğu (saklanan mağaza kimlikleriyle, değişiklik beklemez) ve kimlik kilidi; mağaza tabanı farka kaydedilir. Mağazaya HİÇBİR ŞEY gönderilmez. Uygulanmış kayıt yeniden uygulanmaz (409); yeniden eşleme için (ilk derlemeden sonra da) mobile_store_import_start ile yeni okuma açılır. İlk derlemenin kilitlediği alanlar ve sürüm yazılmaz, Zeyrek'teki değer kalır. Sonra: mobile_product_list (içe aktarılan ürünler), yayından önce mobile_store_diff ve ön kontrol P-24…P-31.",
|
|
73
73
|
inputSchema: { applicationId, importId },
|
|
74
74
|
annotations: writes
|
|
75
75
|
}, handle(config, async ({ applicationId: appId, importId: id }) => importReportOutline(unwrap(await platform.org('POST', `/mobile-store/${appId}/imports/${id}/apply`)))));
|
|
@@ -103,7 +103,7 @@ export function registerPlatformTools(server, platform) {
|
|
|
103
103
|
'Modülün iş kuralları rehberini döner. Bir modülde İLK yazma işleminden önce ilgili rehberi okuyun:',
|
|
104
104
|
...guides.map(name => `- ${name}${guideRouting[name] ? `: ${guideRouting[name]}` : ''}`),
|
|
105
105
|
"Büyük rehberler (page ~120 KB) section'sız çağrıda TAM METİN DÖNMEZ: giriş + bölüm listesi (outline) döner, truncated: true olur. Gövdeyi section ile bölüm bölüm alın.",
|
|
106
|
-
"Büyük gövdeyi (sayfa şeması, akış adımları, uç/workflow tanımı, patch op'ları) dosyaya yazıp bodyFile ile verin: gövde bağlamınızdan geçmez, satır içiyle aynı doğrulamadan geçer (page_*/flow_*/microservice_*/workflow_* create/update/patch). Uzun kod alanı ayrı dosyada durabilir: gövdede { \"$file\": \"./adim.js\" } o dosyanın METNİYLE değişir (çalıştırılmaz; yol bodyFile'ın klasörüne göre, GW-013). microservice_execute yanıt başlıkları varsayılan kısadır; ucun kendi başlığı için includeHeaders:'all'.",
|
|
106
|
+
"Büyük gövdeyi (sayfa şeması, akış adımları, uç/workflow tanımı, patch op'ları) dosyaya yazıp bodyFile ile verin: gövde bağlamınızdan geçmez, satır içiyle aynı doğrulamadan geçer (page_*/flow_*/microservice_*/workflow_* create/update/patch); microfunction kodu codeFile ile (.js/.py). Uzun kod alanı ayrı dosyada durabilir: gövdede { \"$file\": \"./adim.js\" } o dosyanın METNİYLE değişir (çalıştırılmaz; yol bodyFile'ın klasörüne göre, GW-013). microservice_execute yanıt başlıkları varsayılan kısadır; ucun kendi başlığı için includeHeaders:'all'.",
|
|
107
107
|
"Örnek gövdeler ve kural katalogları (ngapps://examples/<ad>, ngapps://rules/<ad>) istemci MCP resource okuyamıyorsa da bu araçla okunur: platform_guide({ resource: 'ngapps://rules/connect-data' }) — guide verilmez; dizin için 'ngapps://examples/index'."
|
|
108
108
|
].join('\n');
|
|
109
109
|
server.registerTool('platform_whoami', {
|
package/package.json
CHANGED