@appaflytech/zeyrek-ai-mcp 0.0.28 → 0.0.30

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.
Files changed (201) hide show
  1. package/.env.example +15 -0
  2. package/README.md +5 -5
  3. package/dist/config.js +37 -0
  4. package/dist/index.js +2 -0
  5. package/dist/knowledge/access.js +802 -0
  6. package/dist/knowledge/backendConstraints.js +18 -1
  7. package/dist/knowledge/case.js +41 -0
  8. package/dist/knowledge/componentCatalog.js +494 -34
  9. package/dist/knowledge/componentEvents.js +132 -7
  10. package/dist/knowledge/connectDataBind.js +819 -0
  11. package/dist/knowledge/connectDataDefinition.js +341 -0
  12. package/dist/knowledge/connectDataExecute.js +93 -0
  13. package/dist/knowledge/connectDataRules.js +128 -0
  14. package/dist/knowledge/decision.js +121 -0
  15. package/dist/knowledge/expressionPrelude.js +59 -0
  16. package/dist/knowledge/layoutSummary.js +140 -2
  17. package/dist/knowledge/microserviceParameters.js +58 -0
  18. package/dist/knowledge/mining.js +94 -0
  19. package/dist/knowledge/mobileComponentCatalog.js +859 -0
  20. package/dist/knowledge/mobileComponentEvents.js +239 -0
  21. package/dist/knowledge/normalize.js +236 -67
  22. package/dist/knowledge/outlines.js +36 -0
  23. package/dist/knowledge/platform/lint.js +385 -0
  24. package/dist/knowledge/platform/mobile.js +142 -0
  25. package/dist/knowledge/platform/mobileLayoutLint.js +324 -0
  26. package/dist/knowledge/platform/mobileRules.js +231 -0
  27. package/dist/knowledge/platform/mobileSpacing.js +82 -0
  28. package/dist/knowledge/platform/mobileTabs.js +108 -0
  29. package/dist/knowledge/platform/nativeBoxModel.js +44 -0
  30. package/dist/knowledge/platform/profile.js +25 -0
  31. package/dist/knowledge/platform/resolve.js +122 -0
  32. package/dist/knowledge/platform/web.js +39 -0
  33. package/dist/knowledge/policy.js +18 -4
  34. package/dist/knowledge/process.js +510 -0
  35. package/dist/knowledge/reservedTables.js +79 -0
  36. package/dist/knowledge/rowFilter.js +43 -0
  37. package/dist/knowledge/rules.js +319 -21
  38. package/dist/knowledge/schemaGuard.js +11 -0
  39. package/dist/knowledge/styleResolve.js +50 -4
  40. package/dist/knowledge/styleResolveNative.js +527 -0
  41. package/dist/knowledge/themeResolve.js +3 -3
  42. package/dist/observability/trace.js +6 -3
  43. package/dist/platform/argumentAliases.js +128 -0
  44. package/dist/platform/bodyFile.js +103 -0
  45. package/dist/platform/capture/actions.js +333 -0
  46. package/dist/platform/capture/browser.js +1 -0
  47. package/dist/platform/{screenshot.js → capture/capture.js} +105 -519
  48. package/dist/platform/capture/console-log.js +122 -0
  49. package/dist/platform/capture/index.js +11 -0
  50. package/dist/platform/capture/readiness.js +103 -0
  51. package/dist/platform/capture/summary.js +77 -0
  52. package/dist/platform/capture/target.js +5 -0
  53. package/dist/platform/capture/targets/studio-mobile.js +15 -0
  54. package/dist/platform/capture/targets/studio-web.js +5 -0
  55. package/dist/platform/diagnostics.js +10 -4
  56. package/dist/platform/domSummary.js +1 -1
  57. package/dist/platform/errors.js +162 -10
  58. package/dist/platform/http.js +29 -8
  59. package/dist/platform/impersonation.js +45 -0
  60. package/dist/platform/jsonArray.js +24 -0
  61. package/dist/platform/responses.js +39 -5
  62. package/dist/platform/schemaReadMemory.js +60 -0
  63. package/dist/platform/secrets.js +17 -1
  64. package/dist/platform/strictArguments.js +68 -12
  65. package/dist/platform/tokenVault.js +187 -4
  66. package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
  67. package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
  68. package/dist/resources/examples/bpm-satinalma.json +376 -0
  69. package/dist/resources/examples/connect-data-convert-verify.json +107 -0
  70. package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
  71. package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
  72. package/dist/resources/examples/connect-data-query-params.json +50 -0
  73. package/dist/resources/examples/connect-data-table-entity.json +147 -0
  74. package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
  75. package/dist/resources/examples/mobile-app-shell-drawer.json +243 -0
  76. package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
  77. package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
  78. package/dist/resources/examples/mobile-form-screen.json +365 -0
  79. package/dist/resources/examples/mobile-list-detail-form.json +672 -0
  80. package/dist/resources/examples/page-detail-profile.json +2 -1
  81. package/dist/resources/examples/page-dynamic-form.json +3 -2
  82. package/dist/resources/examples/page-flex-sections.json +1 -1
  83. package/dist/resources/examples/page-html-embed.json +1 -1
  84. package/dist/resources/examples/workflow-basic.json +8 -2
  85. package/dist/resources/guides/access.md +796 -0
  86. package/dist/resources/guides/bpm.md +686 -0
  87. package/dist/resources/guides/case.md +153 -0
  88. package/dist/resources/guides/component.md +91 -4
  89. package/dist/resources/guides/decision.md +185 -0
  90. package/dist/resources/guides/entity.md +50 -8
  91. package/dist/resources/guides/expressions.md +14 -5
  92. package/dist/resources/guides/functions.md +1 -1
  93. package/dist/resources/guides/getting-started.md +38 -1
  94. package/dist/resources/guides/layout.md +11 -7
  95. package/dist/resources/guides/microservice.md +81 -2
  96. package/dist/resources/guides/middleware.md +44 -2
  97. package/dist/resources/guides/mobile-layout.md +193 -0
  98. package/dist/resources/guides/mobile.md +495 -0
  99. package/dist/resources/guides/page.md +454 -75
  100. package/dist/resources/guides/theme.md +8 -2
  101. package/dist/resources/guides/translation.md +1 -1
  102. package/dist/resources/guides/workflow.md +58 -1
  103. package/dist/resources/read.js +41 -0
  104. package/dist/resources/rules.js +11 -5
  105. package/dist/resources/schema-fingerprint.json +7 -5
  106. package/dist/resources/schemas/AccessExplanationDto.json +144 -0
  107. package/dist/resources/schemas/AccessMatrixDto.json +170 -0
  108. package/dist/resources/schemas/AccessPermissionDto.json +57 -0
  109. package/dist/resources/schemas/AccessPolicyDto.json +223 -0
  110. package/dist/resources/schemas/AccessResourceDto.json +86 -0
  111. package/dist/resources/schemas/AppSessionDto.json +53 -0
  112. package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
  113. package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
  114. package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
  115. package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
  116. package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
  117. package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
  118. package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
  119. package/dist/resources/schemas/CreateEntityCommand.json +118 -0
  120. package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
  121. package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
  122. package/dist/resources/schemas/CreateQueryCommand.json +4 -0
  123. package/dist/resources/schemas/CreateWorkflowCommand.json +143 -1
  124. package/dist/resources/schemas/DataJobDto.json +6 -1
  125. package/dist/resources/schemas/DecisionDto.json +302 -0
  126. package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
  127. package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
  128. package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
  129. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +21 -1
  130. package/dist/resources/schemas/MicroserviceDto.json +15 -3
  131. package/dist/resources/schemas/OrganizationProjectDto.json +335 -0
  132. package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
  133. package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
  134. package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
  135. package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
  136. package/dist/resources/schemas/PreviewWorkflowCommand.json +22 -1
  137. package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
  138. package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
  139. package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
  140. package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
  141. package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
  142. package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
  143. package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
  144. package/dist/resources/schemas/StepDto.json +22 -1
  145. package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
  146. package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
  147. package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
  148. package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
  149. package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
  150. package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
  151. package/dist/resources/schemas/UpdateViewCommand.json +0 -2
  152. package/dist/resources/schemas/UpdateWorkflowCommand.json +143 -1
  153. package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
  154. package/dist/server.js +27 -1
  155. package/dist/tools/access.js +369 -2
  156. package/dist/tools/accessOperations.js +135 -0
  157. package/dist/tools/accessPolicies.js +170 -0
  158. package/dist/tools/accessRuntime.js +203 -0
  159. package/dist/tools/applications.js +60 -3
  160. package/dist/tools/bpm.js +343 -0
  161. package/dist/tools/bpmGovernance.js +266 -0
  162. package/dist/tools/bpmOperations.js +230 -0
  163. package/dist/tools/bpmReporting.js +154 -0
  164. package/dist/tools/bpmRouting.js +150 -0
  165. package/dist/tools/bpmRuntime.js +396 -0
  166. package/dist/tools/case.js +178 -0
  167. package/dist/tools/caseRuntime.js +89 -0
  168. package/dist/tools/common.js +7 -0
  169. package/dist/tools/components.js +118 -14
  170. package/dist/tools/connectData.js +449 -0
  171. package/dist/tools/connectors.js +193 -0
  172. package/dist/tools/datasources.js +3 -3
  173. package/dist/tools/decisions.js +206 -0
  174. package/dist/tools/dependencies.js +1 -1
  175. package/dist/tools/deployments.js +2 -2
  176. package/dist/tools/designSchemaPatch.js +34 -9
  177. package/dist/tools/dtos.js +36 -5
  178. package/dist/tools/entities.js +79 -12
  179. package/dist/tools/externalComponents.js +26 -5
  180. package/dist/tools/flows.js +122 -14
  181. package/dist/tools/folders.js +1 -1
  182. package/dist/tools/imports.js +5 -2
  183. package/dist/tools/layouts.js +50 -17
  184. package/dist/tools/merges.js +1 -1
  185. package/dist/tools/microservices.js +77 -25
  186. package/dist/tools/mobile.js +221 -0
  187. package/dist/tools/modules.js +48 -0
  188. package/dist/tools/organizationStructure.js +137 -0
  189. package/dist/tools/pages.js +465 -98
  190. package/dist/tools/platformInfo.js +27 -8
  191. package/dist/tools/profileWrite.js +92 -0
  192. package/dist/tools/projects.js +71 -9
  193. package/dist/tools/rows.js +69 -13
  194. package/dist/tools/schemaOps.js +61 -1
  195. package/dist/tools/screenWrite.js +37 -8
  196. package/dist/tools/screens.js +95 -5
  197. package/dist/tools/templates.js +26 -1
  198. package/dist/tools/translations.js +1 -1
  199. package/dist/tools/variables.js +11 -5
  200. package/dist/tools/workflows.js +10 -5
  201. package/package.json +2 -2
@@ -1,5 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  import { diagnosticsParam, runWithDiagnostics } from './diagnostics.js';
3
+ import { applyArgumentAliases, closestArgument, runWithArgumentNotes } from './argumentAliases.js';
4
+ import { BODY_FILE_ARGUMENTS, bodyFileParam, loadBodyFile } from './bodyFile.js';
5
+ import { PlatformApiError } from './errors.js';
6
+ import { logError } from '../observability/trace.js';
7
+ /** Doğrulanmış argümanlara takılan çeviri notları (`ArgumentNotes`); JSON'a ve platform isteğine girmez. */
8
+ const ARGUMENT_NOTES = Symbol('argumentNotes');
3
9
  /**
4
10
  * `writes`/`updates` açıklamalı ama KAYIT yazmayan tool'lar: çalıştırma, önizleme, dışa aktarma, indirme,
5
11
  * bağlantı testi. Yanıtlarında yazma tanılaması (silentlyDropped) yoktur; parametre yalnız tool listesini
@@ -25,25 +31,75 @@ function isRawShape(value) {
25
31
  * olmayan bir sorgu kusuruna yönlendirdi. Tek tek tool'lara alan eklemek sınıfı kapatmaz; sessizlik
26
32
  * burada, SDK'ya şema verilmeden önce kaldırılır.
27
33
  */
28
- export function rejectUnknownArguments(server) {
34
+ export function rejectUnknownArguments(server, platform) {
29
35
  const registerTool = server.registerTool.bind(server);
36
+ // Zorunlu gövde argümanı `bodyFile` açıkken şemada isteğe bağlı yayınlanır (yoksa şemaya uyan istemci
37
+ // gövdeyi satır içinde yollamak zorunda kalır ve `bodyFile` "birlikte verilemez" ile reddedilirdi);
38
+ // zorunluluk dosya okunduktan sonra burada denetlenir.
39
+ const requiredBodies = new Map();
40
+ // g-07/g-08: `bodyFile` ve takma ad tablosu SDK'nın strict doğrulamasından ÖNCE koşmalı — sonra
41
+ // koşsa yanlış ad zaten reddedilmiş, dosyadan gelen gövde doğrulanmamış olur. SDK doğrulamayı
42
+ // `validateToolInput` içinde yapar; çeviri orada uygulanır ve notlar doğrulanmış argümanlara
43
+ // sembol anahtarla takılır.
44
+ if (platform) {
45
+ const validator = server;
46
+ const validate = validator.validateToolInput.bind(server);
47
+ validator.validateToolInput = async (tool, args, name) => {
48
+ try {
49
+ const loaded = await loadBodyFile(name, args, platform.config);
50
+ const requiredBody = requiredBodies.get(name);
51
+ if (requiredBody && loaded.args?.[requiredBody] === undefined) {
52
+ throw new PlatformApiError(`${name}: '${requiredBody}' zorunlu — satır içinde ya da bodyFile ile (.json dosyası) verin. İstek gönderilmedi.`, 400);
53
+ }
54
+ const { args: aliased, applied } = await applyArgumentAliases(name, loaded.args, { platform, shape: tool.inputSchema?.shape ?? {} });
55
+ 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
+ }
59
+ catch (error) {
60
+ // Doğrulamadan önceki çeviri (dosya, takma ad) `handle`'ın dışında koşar: hata burada günlüğe yazılır.
61
+ if (error instanceof PlatformApiError)
62
+ logError(error);
63
+ throw error;
64
+ }
65
+ };
66
+ }
30
67
  server.registerTool = ((name, config, callback) => {
31
68
  if (!isRawShape(config.inputSchema))
32
69
  return registerTool(name, config, callback);
33
- // Yazma tool'ları tanılama kipini alır; parametre handler'a ULAŞMAZ (gövdeyi `...rest` ile
34
- // platforma geçiren tool'larda sızardı), yalnız toolResult'ın okuduğu bağlama konur.
35
- const writesDiagnostics = config.annotations?.readOnlyHint === false && config.annotations.destructiveHint !== true && !NON_WRITING_TOOLS.has(name);
36
- const shape = writesDiagnostics ? { ...config.inputSchema, diagnostics: diagnosticsParam } : config.inputSchema;
37
- const run = writesDiagnostics
38
- ? ((args, extra) => {
39
- const { diagnostics, ...rest } = args ?? {};
40
- return runWithDiagnostics(diagnostics === 'full' ? 'full' : 'compact', () => callback(rest, extra));
41
- })
42
- : callback;
70
+ // Tanılama notları her tool'da compact döner (g-08: okuma ve çalıştırma yanıtında da 8 not +
71
+ // "N not daha"). `diagnostics` parametresi yalnız kayıt yazan tool'lara, microservice_execute'a ve
72
+ // *_preview'a açılır — ötekilerde tool listesini büyütmesin diye varsayılan kullanılır. Parametre
73
+ // handler'a ULAŞMAZ (gövdeyi `...rest` ile platforma geçiren tool'larda sızardı), yalnız
74
+ // toolResult'ın okuduğu bağlama konur.
75
+ const writesRecords = config.annotations?.readOnlyHint === false && config.annotations.destructiveHint !== true && !NON_WRITING_TOOLS.has(name);
76
+ const exposesDiagnostics = writesRecords || name === 'microservice_execute' || name.endsWith('_preview');
77
+ const bodyArgument = BODY_FILE_ARGUMENTS[name];
78
+ const bodySchema = bodyArgument ? config.inputSchema[bodyArgument] : undefined;
79
+ if (bodyArgument && bodySchema && !bodySchema.isOptional())
80
+ requiredBodies.set(name, bodyArgument);
81
+ const shape = {
82
+ ...config.inputSchema,
83
+ ...(bodyArgument && bodySchema ? { [bodyArgument]: bodySchema.optional(), bodyFile: bodyFileParam(bodyArgument) } : {}),
84
+ ...(exposesDiagnostics ? { diagnostics: diagnosticsParam } : {})
85
+ };
86
+ const invoke = (args, extra) => {
87
+ const { diagnostics, ...rest } = exposesDiagnostics ? args ?? {} : { ...args, diagnostics: undefined };
88
+ return runWithDiagnostics(diagnostics === 'full' ? 'full' : 'compact', () => callback(exposesDiagnostics ? rest : args, extra), exposesDiagnostics);
89
+ };
90
+ const run = (args, extra) => {
91
+ const { [ARGUMENT_NOTES]: notes, ...rest } = args ?? {};
92
+ return runWithArgumentNotes(notes ?? { aliasesApplied: [] }, () => invoke(rest, extra));
93
+ };
43
94
  const accepted = Object.keys(shape).join(', ');
95
+ const suggestions = (keys) => keys
96
+ .map(key => ({ key, near: closestArgument(key, Object.keys(shape)) }))
97
+ .filter(entry => entry.near !== undefined)
98
+ .map(entry => ` '${entry.key}' → '${entry.near}' mi demek istediniz?`)
99
+ .join('');
44
100
  const inputSchema = z.object(shape, {
45
101
  errorMap: (issue, context) => issue.code === 'unrecognized_keys'
46
- ? { message: `${name} şu üst düzey argümanları tanımıyor: ${issue.keys.map(key => `'${key}'`).join(', ')}. Tanınmayan alan sessizce yok sayılmaz — çağrı hiç gönderilmedi. Bu tool'un okuduğu alanlar: ${accepted}.` }
102
+ ? { message: `${name} şu üst düzey argümanları tanımıyor: ${issue.keys.map(key => `'${key}'`).join(', ')}. Tanınmayan alan sessizce yok sayılmaz — çağrı hiç gönderilmedi. Bu tool'un okuduğu alanlar: ${accepted}.${suggestions(issue.keys)}` }
47
103
  : { message: context.defaultError }
48
104
  }).strict();
49
105
  return registerTool(name, { ...config, inputSchema }, run);
@@ -1,5 +1,8 @@
1
1
  import { PlatformApiError } from './errors.js';
2
2
  import { randomBytes } from 'node:crypto';
3
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+ import { logStep } from '../observability/trace.js';
3
6
  /**
4
7
  * `page_screenshot`/`page_runtime_get` açıklaması "jetonu uygulamanın kendi giriş ucundan alın
5
8
  * (`microservice_execute`)" diyor, ama gizli alan maskesi `access_token`'ı `***` yapıyor ve o yol
@@ -9,10 +12,19 @@ import { randomBytes } from 'node:crypto';
9
12
  * Kasa bu çelişkiyi jetonu bağlama HİÇ sokmadan çözer: maskelenen JWT gateway sürecinde tutulur,
10
13
  * yanıta yalnız kısa ömürlü bir referans (`tok_…`) döner ve kimlik isteyen tool'lar referansı
11
14
  * çözer. Jeton ne yanıtta ne günlükte açık görünür.
15
+ *
16
+ * g-09 (`plans/mobil-plantcare-acigi`, K9): kasa süreç sınırını aşar. `.env` `MCP_STATE_DIR`
17
+ * verilmişse kayıtlar `token-vault.json`'a (0600) yazılır ve süreç açılışta onu okur; süre
18
+ * `MCP_TOKEN_TTL_MINUTES`'tan gelir (verilmezse eski 15 dk). Dosya YALNIZ lokal hedefte yazılır —
19
+ * uzak bir org'un jetonu diske inmez. Test kullanıcısının (`asUser: 'test:<ad>'`) jetonu da burada,
20
+ * etiketiyle tutulur; aynı kullanıcı TTL boyunca yeniden giriş yapmaz.
12
21
  */
13
- const ttlMs = 15 * 60 * 1000;
22
+ const legacyTtlMinutes = 15;
14
23
  const maxEntries = 64;
15
24
  const vault = new Map();
25
+ let ttlMs = legacyTtlMinutes * 60 * 1000;
26
+ let vaultFile;
27
+ let warnedRemote = false;
16
28
  export const tokenRefPattern = /^tok_[0-9a-f]{16}$/;
17
29
  /** Üç base64url parçalı JWT — parola ya da API anahtarı bu şekle uymaz. */
18
30
  export function looksLikeJwt(value) {
@@ -30,29 +42,132 @@ function sweep(now) {
30
42
  vault.delete(oldest.value);
31
43
  }
32
44
  }
33
- export function rememberToken(token) {
45
+ function isLocalTarget(baseUrl) {
46
+ try {
47
+ const host = new URL(baseUrl).hostname;
48
+ return host === 'localhost' || host === '127.0.0.1' || host === '[::1]' || host.endsWith('.localhost');
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ }
54
+ /**
55
+ * Aynı dosyayı birden çok gateway süreci paylaşır (her Claude Code oturumu kendi stdio sürecini açar).
56
+ * Yazma "son yazan kazanır" olursa bir sürecin kaydı ötekinin yazmasıyla silinirdi: yazmadan önce ve
57
+ * bellekte olmayan referans istenince dosya okunur, süresi dolmamış yabancı kayıtlar belleğe katılır.
58
+ */
59
+ function mergeFromFile() {
60
+ if (!vaultFile)
61
+ return;
62
+ try {
63
+ const stored = JSON.parse(readFileSync(vaultFile, 'utf8'));
64
+ const now = Date.now();
65
+ for (const [ref, entry] of Object.entries(stored.entries ?? {})) {
66
+ if (!vault.has(ref) && tokenRefPattern.test(ref) && typeof entry.token === 'string' && typeof entry.expires === 'number' && entry.expires > now) {
67
+ vault.set(ref, { token: entry.token, expires: entry.expires, ...(typeof entry.label === 'string' ? { label: entry.label } : {}) });
68
+ }
69
+ }
70
+ }
71
+ catch {
72
+ // dosya yok ya da okunamadı: bellekteki kasa geçerli
73
+ }
74
+ }
75
+ function persist() {
76
+ if (!vaultFile)
77
+ return;
78
+ mergeFromFile();
79
+ sweep(Date.now());
80
+ try {
81
+ const temporary = `${vaultFile}.${process.pid}.tmp`;
82
+ mkdirSync(dirname(vaultFile), { recursive: true, mode: 0o700 });
83
+ writeFileSync(temporary, JSON.stringify({ entries: Object.fromEntries(vault) }), { mode: 0o600 });
84
+ renameSync(temporary, vaultFile);
85
+ }
86
+ catch (error) {
87
+ logStep('jeton kasası ✕', `kasa dosyası yazılamadı (${vaultFile}) — jetonlar bu süreçte bellekte kalır`, [error instanceof Error ? error.message : String(error)]);
88
+ }
89
+ }
90
+ /**
91
+ * Süreç açılışında bir kez çağrılır (`index.ts`); testler yeniden başlamayı `clearTokenVault` +
92
+ * bu çağrıyla benzetir. Dosyadaki süresi dolmuş kayıtlar yüklenmez ve dosya onlarsız yeniden yazılır.
93
+ */
94
+ export function configureTokenVault(config) {
95
+ ttlMs = (config.tokenTtlMinutes ?? legacyTtlMinutes) * 60 * 1000;
96
+ vaultFile = undefined;
97
+ if (!config.stateDirectory)
98
+ return;
99
+ if (!isLocalTarget(config.orgApiBaseUrl)) {
100
+ if (!warnedRemote) {
101
+ warnedRemote = true;
102
+ logStep('jeton kasası', `ORG_API_BASE_URL lokal değil — kasa dosyaya YAZILMAZ, jetonlar yalnız süreç belleğinde tutulur (MCP_STATE_DIR yok sayıldı)`);
103
+ }
104
+ return;
105
+ }
106
+ vaultFile = join(config.stateDirectory, 'token-vault.json');
107
+ persist();
108
+ }
109
+ /** Kasanın geçerli ömrü (dakika) — yanıt notları sabit bir süre yazmasın. */
110
+ export const tokenTtlMinutes = () => Math.round(ttlMs / 60000);
111
+ export function rememberToken(token, label) {
34
112
  const now = Date.now();
35
113
  sweep(now);
36
114
  for (const [ref, entry] of vault) {
37
115
  if (entry.token === token) {
38
116
  entry.expires = now + ttlMs;
117
+ if (label !== undefined)
118
+ entry.label = label;
119
+ persist();
39
120
  return ref;
40
121
  }
41
122
  }
42
123
  const ref = `tok_${randomBytes(8).toString('hex')}`;
43
- vault.set(ref, { token, expires: now + ttlMs });
124
+ vault.set(ref, { token, expires: now + ttlMs, ...(label !== undefined ? { label } : {}) });
125
+ persist();
44
126
  return ref;
45
127
  }
46
128
  export function resolveToken(reference) {
129
+ if (!vault.has(reference))
130
+ mergeFromFile();
47
131
  const entry = vault.get(reference);
48
132
  if (!entry)
49
133
  return undefined;
50
134
  if (entry.expires <= Date.now()) {
51
135
  vault.delete(reference);
136
+ persist();
52
137
  return undefined;
53
138
  }
54
139
  return entry.token;
55
140
  }
141
+ /**
142
+ * Jetonun kendi süresi (`exp`) kasanın TTL'inden kısa olabilir: uygulamanın jetonu 15 dk yaşarken kasa
143
+ * 60 dk tutuyordu ve süresi dolmuş jetonla çekilen kare sessizce çıkış yapmış sayfayı gösterirdi.
144
+ * 30 sn pay bırakılır; `exp` okunamayan jeton geçerli sayılır (karar uygulamanındır).
145
+ */
146
+ const jwtExpired = (token) => {
147
+ try {
148
+ const payload = JSON.parse(Buffer.from(token.split('.')[1] ?? '', 'base64url').toString('utf8'));
149
+ return typeof payload.exp === 'number' && payload.exp * 1000 <= Date.now() + 30_000;
150
+ }
151
+ catch {
152
+ return false;
153
+ }
154
+ };
155
+ function labeledToken(label) {
156
+ const find = () => {
157
+ const now = Date.now();
158
+ for (const entry of vault.values()) {
159
+ if (entry.label === label && entry.expires > now && !jwtExpired(entry.token))
160
+ return entry.token;
161
+ }
162
+ return undefined;
163
+ };
164
+ const found = find();
165
+ if (found || !vaultFile)
166
+ return found;
167
+ mergeFromFile();
168
+ return find();
169
+ }
170
+ /** Bellekteki kasayı boşaltır (süreç yeniden başlamasının benzetimi); dosyaya dokunmaz. */
56
171
  export function clearTokenVault() {
57
172
  vault.clear();
58
173
  }
@@ -66,8 +181,76 @@ export function resolveIdentityToken(value) {
66
181
  return value;
67
182
  const token = resolveToken(value);
68
183
  if (token === undefined) {
69
- throw new PlatformApiError(`'${value}' referansı geçersiz ya da süresi dolmuş (15 dk). Uygulamanın giriş ucunu microservice_execute ile yeniden çağırıp yanıttaki tokenRefs değerini kullanın.`, 400);
184
+ throw new PlatformApiError(`'${value}' referansı geçersiz ya da süresi dolmuş (${Math.round(ttlMs / 60000)} dk). Tercih edilen yol asUser: 'test:<ad>' — gateway kök .env'deki test kullanıcısıyla kendisi giriş yapar. Yoksa uygulamanın giriş ucunu microservice_execute ile yeniden çağırıp yanıttaki tokenRefs değerini kullanın.`, 400);
185
+ }
186
+ return token;
187
+ }
188
+ /**
189
+ * Giriş yanıtındaki erişim jetonu: yanıt birden çok JWT taşıyabilir (erişim + yenileme). Anahtarı
190
+ * `access` içeren önce, sonra `refresh` içermeyen, en son ilk bulunan — yenileme jetonuyla istek atmak
191
+ * her uçta 401 demekti.
192
+ */
193
+ function accessJwt(value) {
194
+ const found = [];
195
+ const collect = (node, key, depth) => {
196
+ if (depth > 12)
197
+ return;
198
+ if (looksLikeJwt(node)) {
199
+ found.push({ key, token: node });
200
+ return;
201
+ }
202
+ if (Array.isArray(node))
203
+ node.forEach(entry => collect(entry, key, depth + 1));
204
+ else if (node && typeof node === 'object')
205
+ Object.entries(node).forEach(([name, entry]) => collect(entry, name, depth + 1));
206
+ };
207
+ collect(value, '', 0);
208
+ return (found.find(entry => /access/i.test(entry.key)) ?? found.find(entry => !/refresh/i.test(entry.key)) ?? found[0])?.token;
209
+ }
210
+ /**
211
+ * g-09: `asUser` üç şekil alır — ham JWT, `tok_…` referansı ve `test:<ad>`. Üçüncüsünde gateway kök
212
+ * `.env`'deki `MCP_TEST_USER_<AD>` satırının tarif ettiği giriş ucunu (uygulamanın kendi uç'u)
213
+ * anonim çağırır, yanıttaki ilk JWT'yi kasaya etiketiyle koyar ve TTL boyunca yeniden kullanır.
214
+ * Parola ne yanıta, ne günlüğe (`hideRequestBody`), ne hata metnine girer; satırın değeri hiçbir
215
+ * mesajda gösterilmez. AI `.env`'i düzenlemez: tanımsız kullanıcı, insanın ekleyeceği değişkenin
216
+ * adıyla reddedilir.
217
+ */
218
+ export async function resolveIdentity(value, platform, project, environment) {
219
+ if (value === undefined || !value.startsWith('test:'))
220
+ return resolveIdentityToken(value);
221
+ const name = value.slice('test:'.length).trim().toLowerCase();
222
+ const variable = `MCP_TEST_USER_${name.toUpperCase()}`;
223
+ const label = `test:${name}@${platform.resolveProject(project)}/${environment ?? platform.config.defaultEnvironment}`;
224
+ const cached = labeledToken(label);
225
+ if (cached)
226
+ return cached;
227
+ const raw = platform.config.testUsers[name];
228
+ if (!raw) {
229
+ const defined = Object.keys(platform.config.testUsers);
230
+ throw new PlatformApiError(`Test kullanıcısı '${name}' tanımlı değil. Kök .env'e insan ekler: ${variable}={"application":"<uygulama slug>","microservice":"<giriş ucu slug>","body":{…giriş gövdesi…}} (biçim: docs/ortam.md §Test kullanıcıları); gateway yeniden başlatılır. AI .env'i düzenlemez. Tanımlı olanlar: ${defined.length > 0 ? defined.join(', ') : '(yok)'}.`, 400);
231
+ }
232
+ let login;
233
+ try {
234
+ login = JSON.parse(raw);
235
+ }
236
+ catch {
237
+ throw new PlatformApiError(`${variable} JSON olarak okunamadı (değer gösterilmez). Biçim: {"application":"…","microservice":"…","body":{…}} — docs/ortam.md §Test kullanıcıları.`, 400);
238
+ }
239
+ if (typeof login.application !== 'string' || typeof login.microservice !== 'string') {
240
+ throw new PlatformApiError(`${variable} 'application' ve 'microservice' alanlarını taşımalı (değer gösterilmez) — docs/ortam.md §Test kullanıcıları.`, 400);
241
+ }
242
+ const endpoint = `${login.application}/${login.microservice}`;
243
+ const response = await platform.tenantRaw(typeof login.method === 'string' ? login.method : 'POST', project, `/m/${endpoint}`, {
244
+ environment,
245
+ body: login.body,
246
+ anonymous: true,
247
+ hideRequestBody: true
248
+ });
249
+ const token = accessJwt(response.body);
250
+ if (!token) {
251
+ throw new PlatformApiError(`Test kullanıcısı '${name}' girişi jeton döndürmedi: ${endpoint} HTTP ${response.httpStatus}. Kök .env'deki ${variable} satırını (uç ve gövde) ve kullanıcının uygulamada var olduğunu kontrol edin; gövde ve kimlik bilgisi gösterilmez.`, 400);
70
252
  }
253
+ rememberToken(token, label);
71
254
  return token;
72
255
  }
73
256
  /**
@@ -0,0 +1,294 @@
1
+ {
2
+ "_meta": {
3
+ "synthetic": true,
4
+ "note": "S-1: satır olayıyla başlar, yöneticiye bildirim, atanan görevi completeOn ile bitirir, kontrol bölgesinde birim yöneticisi onaylar; ret back-to:iade ile görevi geri açar (writeBack durum=acik). process_definition_create(definition) ile yazılır; zeyrek-backend ProcessRunnerTests.S1 ile birebir."
5
+ },
6
+ "definition": {
7
+ "slug": "gorev-kontrol",
8
+ "name": "Görev kontrol",
9
+ "description": "Atanan görevi yapar, birim yöneticisi kontrol edip onaylar; ret görevi atanana geri döndürür.",
10
+ "analysis": {
11
+ "purpose": {
12
+ "en": "Work assigned to a person should not be closed without a check; a manager confirms that it was done properly.",
13
+ "tr": "Bir kişiye atanan iş kontrol edilmeden kapanmasın; yönetici işin doğru yapıldığını onaylar."
14
+ },
15
+ "summary": {
16
+ "en": "When a task is created with an assignee, their manager is told and the assignee does the work. The manager then checks it: an approval closes the task, a rejection sends it back to the assignee with the reason.",
17
+ "tr": "Atanan kişisi olan bir görev oluşunca yöneticisine haber verilir ve atanan kişi işi yapar. Ardından yönetici kontrol eder: onay görevi kapatır, ret gerekçesiyle birlikte görevi atanana geri gönderir."
18
+ }
19
+ },
20
+ "subject": {
21
+ "entity": "gorevler"
22
+ },
23
+ "singleton": "per-subject",
24
+ "withdrawPolicy": "before-first-decision",
25
+ "onSubjectDeleted": "block",
26
+ "history": "full",
27
+ "sod": [
28
+ "not-initiator"
29
+ ],
30
+ "nodes": [
31
+ {
32
+ "id": "baslangic",
33
+ "type": "start",
34
+ "name": "Görev oluşturuldu",
35
+ "description": "Görevler tablosunda satır oluşunca süreç başlar.",
36
+ "analysis": {
37
+ "what": {
38
+ "en": "The process starts by itself when a new task is saved.",
39
+ "tr": "Yeni bir görev kaydedildiğinde süreç kendiliğinden başlar."
40
+ },
41
+ "data": {
42
+ "en": "Reads the new task record; it starts only when the task has an assignee.",
43
+ "tr": "Yeni görev kaydını okur; yalnız görevin atanan kişisi varsa başlar."
44
+ },
45
+ "why": {
46
+ "en": "Every assigned task should be followed from the moment it exists.",
47
+ "tr": "Atanan her görev var olduğu andan itibaren takip edilsin diye."
48
+ },
49
+ "example": {
50
+ "en": "The task \"Prepare the Q3 customer report\" is saved with Selin as the assignee, so the process starts.",
51
+ "tr": "\"3. çeyrek müşteri raporunu hazırla\" görevi Selin'e atanmış olarak kaydedilir ve süreç başlar."
52
+ }
53
+ },
54
+ "trigger": "row-event",
55
+ "entity": "gorevler",
56
+ "operation": "create",
57
+ "condition": "row.atanan_id != null"
58
+ },
59
+ {
60
+ "id": "bildir",
61
+ "type": "notify",
62
+ "name": "Yöneticiye bildir",
63
+ "description": "Atananın birim yöneticisine yeni görev bildirimi gider.",
64
+ "analysis": {
65
+ "what": {
66
+ "en": "The assignee's unit manager gets an inbox message about the new task.",
67
+ "tr": "Atanan kişinin birim yöneticisine yeni görev için kutu mesajı gider."
68
+ },
69
+ "data": {
70
+ "en": "Uses the assignee of the task to find the manager and the task title for the message.",
71
+ "tr": "Yöneticiyi bulmak için görevin atanan kişisini, mesaj için görevin başlığını kullanır."
72
+ },
73
+ "why": {
74
+ "en": "The manager will approve the work later, so they should know about it from the start.",
75
+ "tr": "İşi sonunda yönetici onaylayacak; baştan haberi olsun diye."
76
+ },
77
+ "example": {
78
+ "en": "Burak, Selin's manager, sees \"New task: Prepare the Q3 customer report\" in his inbox.",
79
+ "tr": "Selin'in yöneticisi Burak, kutusunda \"Yeni görev: 3. çeyrek müşteri raporunu hazırla\" mesajını görür."
80
+ }
81
+ },
82
+ "to": "unit-manager of subject-field:atanan_id",
83
+ "channels": [
84
+ "inbox"
85
+ ],
86
+ "template": {
87
+ "subject": {
88
+ "tr": "Yeni görev"
89
+ },
90
+ "body": {
91
+ "tr": "{{row.baslik}} görevi atandı."
92
+ }
93
+ }
94
+ },
95
+ {
96
+ "id": "gorevi-yap",
97
+ "type": "user-task",
98
+ "resultKey": "gorevi_yap",
99
+ "name": "Görevi yap",
100
+ "description": "Atanan görevi yapar; satırı tamamlandı yapınca görev biter.",
101
+ "analysis": {
102
+ "what": {
103
+ "en": "The assignee does the work and marks the task as done.",
104
+ "tr": "Atanan kişi işi yapar ve görevi tamamlandı olarak işaretler."
105
+ },
106
+ "data": {
107
+ "en": "Waits until the status of the task becomes \"done\"; the assignee has three days.",
108
+ "tr": "Görevin durumu \"tamamlandı\" olana kadar bekler; atanan kişinin üç günü vardır."
109
+ },
110
+ "why": {
111
+ "en": "This is the actual work; everything after it is the check.",
112
+ "tr": "Asıl iş budur; sonrasındaki her şey kontrol içindir."
113
+ },
114
+ "example": {
115
+ "en": "Selin prepares the report within three days and sets the task to done.",
116
+ "tr": "Selin raporu üç gün içinde hazırlar ve görevi tamamlandı yapar."
117
+ }
118
+ },
119
+ "assignee": "subject-field:atanan_id",
120
+ "sla": "P3D",
121
+ "completeOn": {
122
+ "trigger": "row-event",
123
+ "operation": "update",
124
+ "condition": "row.durum === 'tamamlandi'"
125
+ }
126
+ },
127
+ {
128
+ "id": "kontrol-bolgesi",
129
+ "type": "region",
130
+ "name": "Kontrol zinciri",
131
+ "description": "Uygulama yöneticisinin uyarlayabildiği onay zinciri.",
132
+ "analysis": {
133
+ "what": {
134
+ "en": "A group of approval steps that the application manager can adjust, for example by adding a second approver.",
135
+ "tr": "Uygulama yöneticisinin uyarlayabildiği onay adımları grubu; örneğin ikinci bir onaycı eklenebilir."
136
+ },
137
+ "data": {
138
+ "en": "Allows at most three steps, only approvals and notifications, assigned to managers, roles or positions.",
139
+ "tr": "En çok üç adıma izin verir; yalnız onay ve bildirim, yalnız yöneticiye, role ya da pozisyona atanır."
140
+ },
141
+ "why": {
142
+ "en": "Each unit checks work differently; the chain can change without redrawing the process.",
143
+ "tr": "Her birim işi farklı kontrol eder; zincir süreci baştan çizmeden değişebilsin diye."
144
+ },
145
+ "example": {
146
+ "en": "For the sales unit the chain has one step: Burak's check. Another unit could add a second approver here.",
147
+ "tr": "Satış birimi için zincirde tek adım var: Burak'ın kontrolü. Başka bir birim buraya ikinci bir onaycı ekleyebilir."
148
+ }
149
+ },
150
+ "editable": "open",
151
+ "limits": {
152
+ "maxSteps": 3,
153
+ "nodeTypes": [
154
+ "approval",
155
+ "notify"
156
+ ],
157
+ "assigneeRules": [
158
+ "unit-manager",
159
+ "parent-unit-manager",
160
+ "role",
161
+ "position"
162
+ ]
163
+ },
164
+ "nodes": [
165
+ {
166
+ "id": "kontrol-onay",
167
+ "type": "approval",
168
+ "resultKey": "kontrol_onay",
169
+ "name": "Kontrol ve onay",
170
+ "description": "Birim yöneticisi görevi kontrol eder.",
171
+ "analysis": {
172
+ "what": {
173
+ "en": "The assignee's unit manager checks the work and approves or rejects it.",
174
+ "tr": "Atanan kişinin birim yöneticisi işi kontrol eder; onaylar ya da reddeder."
175
+ },
176
+ "data": {
177
+ "en": "Looks at the completed task. A rejection needs a comment and sends the task back; the manager has two days.",
178
+ "tr": "Tamamlanan göreve bakar. Ret için yorum zorunludur ve görev geri gönderilir; yöneticinin iki günü vardır."
179
+ },
180
+ "why": {
181
+ "en": "A second person confirms the quality of the work before it is closed.",
182
+ "tr": "İş kapanmadan önce kalitesini ikinci bir kişi onaylasın diye."
183
+ },
184
+ "example": {
185
+ "en": "Burak opens the report, notices the missing chart and rejects it with \"Add the regional chart\"; the second time he approves it.",
186
+ "tr": "Burak raporu açar, eksik grafiği fark eder ve \"Bölge grafiğini ekle\" yorumuyla reddeder; ikinci seferde onaylar."
187
+ }
188
+ },
189
+ "editable": "params",
190
+ "editableParams": [
191
+ "assignee",
192
+ "sla"
193
+ ],
194
+ "assignee": "unit-manager of subject-field:atanan_id",
195
+ "policy": "any",
196
+ "commentRequired": "outcome === 'rejected'",
197
+ "onReject": "back-to:iade",
198
+ "sla": "P2D"
199
+ }
200
+ ],
201
+ "edges": []
202
+ },
203
+ {
204
+ "id": "iade",
205
+ "type": "notify",
206
+ "name": "Göreve iade",
207
+ "description": "Ret gerekçesi atanana bildirilir, satır yeniden açılır.",
208
+ "analysis": {
209
+ "what": {
210
+ "en": "The assignee is told that the task came back, and the task is opened again.",
211
+ "tr": "Atanan kişiye görevin geri döndüğü bildirilir ve görev yeniden açılır."
212
+ },
213
+ "data": {
214
+ "en": "Uses the task title for the message and sets the status of the task back to \"open\".",
215
+ "tr": "Mesaj için görevin başlığını kullanır, görevin durumunu yeniden \"açık\" yapar."
216
+ },
217
+ "why": {
218
+ "en": "The assignee has to know what to fix, and the task must be workable again.",
219
+ "tr": "Atanan kişi neyi düzelteceğini bilmeli, görev yeniden üzerinde çalışılabilir olmalı."
220
+ },
221
+ "example": {
222
+ "en": "Selin gets \"The task was returned for correction\" with Burak's comment, and the task is open again.",
223
+ "tr": "Selin'e Burak'ın yorumuyla \"Görev düzeltme için iade edildi\" mesajı gelir ve görev yeniden açılır."
224
+ }
225
+ },
226
+ "to": "subject-field:atanan_id",
227
+ "channels": [
228
+ "inbox"
229
+ ],
230
+ "template": {
231
+ "subject": {
232
+ "tr": "Görev iade edildi"
233
+ },
234
+ "body": {
235
+ "tr": "{{row.baslik}} görevi düzeltme için iade edildi."
236
+ }
237
+ },
238
+ "writeBack": {
239
+ "durum": "'acik'"
240
+ }
241
+ },
242
+ {
243
+ "id": "onaylandi",
244
+ "type": "end",
245
+ "name": "Onaylandı",
246
+ "description": "Görev onaylandı, satır kapatılır.",
247
+ "analysis": {
248
+ "what": {
249
+ "en": "The process ends with the task approved.",
250
+ "tr": "Süreç görev onaylanmış olarak biter."
251
+ },
252
+ "data": {
253
+ "en": "Sets the status of the task to \"approved\".",
254
+ "tr": "Görevin durumunu \"onaylandı\" yapar."
255
+ },
256
+ "why": {
257
+ "en": "The task record shows that the work is finished and checked.",
258
+ "tr": "Görev kaydı işin bittiğini ve kontrol edildiğini göstersin diye."
259
+ },
260
+ "example": {
261
+ "en": "After Burak's approval the Q3 report task shows \"approved\".",
262
+ "tr": "Burak onaylayınca 3. çeyrek rapor görevi \"onaylandı\" olarak görünür."
263
+ }
264
+ },
265
+ "outcome": "approved",
266
+ "writeBack": {
267
+ "durum": "'onaylandi'"
268
+ }
269
+ }
270
+ ],
271
+ "edges": [
272
+ [
273
+ "baslangic",
274
+ "bildir"
275
+ ],
276
+ [
277
+ "bildir",
278
+ "gorevi-yap"
279
+ ],
280
+ [
281
+ "gorevi-yap",
282
+ "kontrol-bolgesi"
283
+ ],
284
+ [
285
+ "kontrol-bolgesi",
286
+ "onaylandi"
287
+ ],
288
+ [
289
+ "iade",
290
+ "gorevi-yap"
291
+ ]
292
+ ]
293
+ }
294
+ }