sap-adt-mcp 0.8.45 → 0.8.47
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/package.json +1 -1
- package/src/panel.js +396 -37
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sap-adt-mcp",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.47",
|
|
4
4
|
"mcpName": "io.github.yzonur/sap-adt-mcp",
|
|
5
5
|
"description": "MCP server giving Claude live access to SAP systems via ADT (ABAP Development Tools) REST. Read source, search, run syntax checks, and edit ABAP objects from any MCP-compatible client.",
|
|
6
6
|
"type": "module",
|
package/src/panel.js
CHANGED
|
@@ -33,6 +33,181 @@ const PANEL_TOOLS = [
|
|
|
33
33
|
{ name: "adt_get_dump", label: "Dump Detayı", cat: "Runtime" },
|
|
34
34
|
];
|
|
35
35
|
|
|
36
|
+
// --- Friendly form layer (panel-only) ---------------------------------------
|
|
37
|
+
// The MCP inputSchemas are written for an agent: terse keys, ADT type codes,
|
|
38
|
+
// regex. The panel re-presents them for a human. None of this touches the
|
|
39
|
+
// agent-facing schemas — it's an overlay shipped in /meta and consumed by the
|
|
40
|
+
// page. Per field: { label, help, advanced (collapse), default, placeholder,
|
|
41
|
+
// options:[{value,label}] (renders a dropdown) }. Object-type dropdowns use the
|
|
42
|
+
// tools' own friendly aliases (normalizeType accepts them), so they stay correct.
|
|
43
|
+
const OBJECT_TYPE_OPTIONS = [
|
|
44
|
+
{ value: "", label: "(seçiniz)" },
|
|
45
|
+
{ value: "class", label: "Sınıf (CLAS)" },
|
|
46
|
+
{ value: "program", label: "Program (PROG)" },
|
|
47
|
+
{ value: "interface", label: "Arayüz (INTF)" },
|
|
48
|
+
{ value: "function", label: "Fonksiyon modülü (FUGR/FF)" },
|
|
49
|
+
{ value: "functiongroup", label: "Fonksiyon grubu (FUGR)" },
|
|
50
|
+
{ value: "include", label: "Include (INCL)" },
|
|
51
|
+
{ value: "table", label: "Tablo (TABL)" },
|
|
52
|
+
{ value: "structure", label: "Yapı / Structure (TABL)" },
|
|
53
|
+
{ value: "dataelement", label: "Veri elemanı (DTEL)" },
|
|
54
|
+
{ value: "domain", label: "Domain (DOMA)" },
|
|
55
|
+
{ value: "cds", label: "CDS View (DDLS)" },
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
// objectType filter on quick-search wants the ADT node-type code, not an alias.
|
|
59
|
+
const SEARCH_TYPE_OPTIONS = [
|
|
60
|
+
{ value: "", label: "Tümü" },
|
|
61
|
+
{ value: "CLAS/OC", label: "Sınıf" },
|
|
62
|
+
{ value: "PROG/P", label: "Program" },
|
|
63
|
+
{ value: "INTF/OI", label: "Arayüz" },
|
|
64
|
+
{ value: "FUGR/F", label: "Fonksiyon grubu" },
|
|
65
|
+
{ value: "TABL/DT", label: "Tablo" },
|
|
66
|
+
{ value: "DTEL/DE", label: "Veri elemanı" },
|
|
67
|
+
{ value: "DOMA/DD", label: "Domain" },
|
|
68
|
+
{ value: "DDLS/DF", label: "CDS View" },
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
const CLASS_INCLUDE_OPTIONS = [
|
|
72
|
+
{ value: "", label: "main (varsayılan)" },
|
|
73
|
+
{ value: "definitions", label: "definitions" },
|
|
74
|
+
{ value: "implementations", label: "implementations" },
|
|
75
|
+
{ value: "macros", label: "macros" },
|
|
76
|
+
{ value: "testclasses", label: "testclasses" },
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
// intro: a friendly, non-technical one-liner per tool (replaces the agent-facing
|
|
80
|
+
// description on the card). fields: per-field overlay.
|
|
81
|
+
const TOOL_HINTS = {
|
|
82
|
+
adt_list_systems: { intro: "Tanımlı SAP sistemlerini ve varsayılanı gösterir." },
|
|
83
|
+
adt_ping: { intro: "Seçili sisteme bağlantıyı ve oturumu test eder." },
|
|
84
|
+
adt_search_objects: {
|
|
85
|
+
intro: "Repository'de isme göre obje arar. İsmin bir kısmını ve '*' joker karakterini kullanın.",
|
|
86
|
+
fields: {
|
|
87
|
+
query: { label: "Aranacak isim", placeholder: "örn: ZCL_MUSTERI*", help: "'*' joker karakter olarak kullanılabilir." },
|
|
88
|
+
objectType: { label: "Obje türü", options: SEARCH_TYPE_OPTIONS, help: "Belirli bir türle sınırla; boş = tümü." },
|
|
89
|
+
maxResults: { label: "Kaç sonuç", default: 50, advanced: true },
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
adt_grep_source: {
|
|
93
|
+
intro: "Kaynak kod İÇİNDE metin arar (isimde değil). Bir paket, transport ya da obje listesi seçin.",
|
|
94
|
+
fields: {
|
|
95
|
+
pattern: { label: "Aranan metin", placeholder: "örn: CALL FUNCTION", help: "Düz metin yazın; ileri düzey için regex (Gelişmiş)." },
|
|
96
|
+
package: { label: "Paket", placeholder: "örn: ZFLEET" },
|
|
97
|
+
transport: { label: "Transport", advanced: true, placeholder: "örn: E4DK900123" },
|
|
98
|
+
objects: { label: "Obje listesi (JSON)", advanced: true },
|
|
99
|
+
recursive: { label: "Alt paketlere de in", advanced: true },
|
|
100
|
+
flags: { label: "Regex flag'leri", advanced: true, help: "Boş = harf duyarsız ('i'). '' yazıp kapatamazsınız; teknik." },
|
|
101
|
+
prefix: { label: "Alt paket öneki", advanced: true },
|
|
102
|
+
maxPackages: { label: "Maks. paket", advanced: true },
|
|
103
|
+
maxObjects: { label: "Maks. obje", advanced: true },
|
|
104
|
+
maxMatches: { label: "Maks. eşleşme", advanced: true },
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
adt_browse_package: {
|
|
108
|
+
intro: "Bir paketin doğrudan içeriğini (tek seviye) listeler.",
|
|
109
|
+
fields: { package: { label: "Paket", placeholder: "örn: ZLOCAL" } },
|
|
110
|
+
},
|
|
111
|
+
adt_where_used: {
|
|
112
|
+
intro: "Bir objenin nerelerde kullanıldığını listeler.",
|
|
113
|
+
fields: {
|
|
114
|
+
object: { label: "Obje adı", placeholder: "örn: ZCL_MUSTERI" },
|
|
115
|
+
type: { label: "Obje türü", options: OBJECT_TYPE_OPTIONS },
|
|
116
|
+
group: { label: "Fonksiyon grubu", advanced: true, help: "Sadece FM/FUGR include için." },
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
adt_get_source: {
|
|
120
|
+
intro: "Bir objenin kaynak kodunu getirir.",
|
|
121
|
+
fields: {
|
|
122
|
+
object: { label: "Obje adı", placeholder: "örn: ZCL_MUSTERI" },
|
|
123
|
+
type: { label: "Obje türü", options: OBJECT_TYPE_OPTIONS },
|
|
124
|
+
include: { label: "Sınıf include'u", options: CLASS_INCLUDE_OPTIONS, advanced: true },
|
|
125
|
+
group: { label: "Fonksiyon grubu", advanced: true },
|
|
126
|
+
onlyMethod: { label: "Sadece bu metod", advanced: true, placeholder: "örn: CONSTRUCTOR" },
|
|
127
|
+
firstLine: { label: "İlk satır", advanced: true },
|
|
128
|
+
lastLine: { label: "Son satır", advanced: true },
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
adt_read_table: {
|
|
132
|
+
intro: "Veritabanına salt-okunur SELECT çalıştırır (sadece SELECT).",
|
|
133
|
+
fields: {
|
|
134
|
+
query: { label: "SELECT sorgusu", placeholder: "SELECT matnr, matkl FROM mara WHERE matnr LIKE 'M%'" },
|
|
135
|
+
maxRows: { label: "Maks. satır", default: 100, advanced: true },
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
adt_run_atc: {
|
|
139
|
+
intro: "Seçili objelerde ABAP Test Cockpit (ATC) kontrolü çalıştırır.",
|
|
140
|
+
fields: {
|
|
141
|
+
objects: { label: "Objeler (JSON liste)", placeholder: '[{"name":"ZCL_X","type":"class"}]', help: 'Her öğe { "name": "...", "type": "..." }.' },
|
|
142
|
+
checkVariant: { label: "Kontrol varyantı", advanced: true, help: "Boş = DEFAULT." },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
adt_list_inactive_objects: {
|
|
146
|
+
intro: "Düzenlenmiş ama henüz aktive edilmemiş objeleri listeler.",
|
|
147
|
+
},
|
|
148
|
+
adt_list_transports: {
|
|
149
|
+
intro: "Görünür transport isteklerini listeler.",
|
|
150
|
+
fields: {
|
|
151
|
+
user: { label: "Kullanıcı (sahibi)", placeholder: "boş = bağlantı kullanıcısı" },
|
|
152
|
+
status: {
|
|
153
|
+
label: "Durum",
|
|
154
|
+
options: [
|
|
155
|
+
{ value: "", label: "Değiştirilebilir (varsayılan)" },
|
|
156
|
+
{ value: "modifiable", label: "Değiştirilebilir" },
|
|
157
|
+
{ value: "released", label: "Serbest bırakılmış" },
|
|
158
|
+
{ value: "all", label: "Tümü" },
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
targets: { label: "Hedef sistemler", advanced: true },
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
adt_list_dumps: {
|
|
165
|
+
intro: "ST22 kısa dökümlerini (short dumps) listeler.",
|
|
166
|
+
fields: {
|
|
167
|
+
user: { label: "Kullanıcı", placeholder: "örn: OYILMAZ" },
|
|
168
|
+
from: { label: "Başlangıç tarihi", placeholder: "YYYYMMDD, örn: 20260513" },
|
|
169
|
+
to: { label: "Bitiş tarihi", placeholder: "YYYYMMDD", advanced: true },
|
|
170
|
+
host: { label: "Sunucu (host)", advanced: true },
|
|
171
|
+
maxResults: { label: "Kaç sonuç", default: 20, advanced: true },
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
adt_get_dump: {
|
|
175
|
+
intro: "Tek bir kısa dökümün detayını id ile getirir.",
|
|
176
|
+
fields: {
|
|
177
|
+
dumpId: { label: "Dump id", help: "adt_list_dumps çıktısındaki id." },
|
|
178
|
+
chapters: { label: "Bölümler (JSON liste)", advanced: true },
|
|
179
|
+
full: { label: "Ham metni de ekle", advanced: true, help: "Büyük (100KB+)." },
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Turkish column/field labels for the result tables. Unknowns are humanized.
|
|
185
|
+
const COLUMN_LABELS = {
|
|
186
|
+
name: "İsim",
|
|
187
|
+
type: "Tür",
|
|
188
|
+
description: "Açıklama",
|
|
189
|
+
object: "Obje",
|
|
190
|
+
line: "Satır",
|
|
191
|
+
text: "Metin",
|
|
192
|
+
uri: "URI",
|
|
193
|
+
package: "Paket",
|
|
194
|
+
host: "Host",
|
|
195
|
+
user: "Kullanıcı",
|
|
196
|
+
client: "Mandant",
|
|
197
|
+
readOnly: "Salt-okunur",
|
|
198
|
+
isDefault: "Varsayılan",
|
|
199
|
+
priority: "Öncelik",
|
|
200
|
+
checkTitle: "Kontrol",
|
|
201
|
+
messageTitle: "Mesaj",
|
|
202
|
+
message: "Mesaj",
|
|
203
|
+
timestamp: "Zaman",
|
|
204
|
+
program: "Program",
|
|
205
|
+
runtimeError: "Hata",
|
|
206
|
+
id: "Id",
|
|
207
|
+
hits: "Eşleşme",
|
|
208
|
+
status: "Durum",
|
|
209
|
+
};
|
|
210
|
+
|
|
36
211
|
function timingSafeEqualStr(a, b) {
|
|
37
212
|
const ba = Buffer.from(String(a));
|
|
38
213
|
const bb = Buffer.from(String(b));
|
|
@@ -82,11 +257,14 @@ function buildView({ tools, handlers, config, version }) {
|
|
|
82
257
|
for (const item of PANEL_TOOLS) {
|
|
83
258
|
const def = tools.find((t) => t.name === item.name);
|
|
84
259
|
if (!def || typeof handlers[item.name] !== "function") continue; // tool not present
|
|
260
|
+
const hint = TOOL_HINTS[item.name] ?? {};
|
|
85
261
|
descriptors.push({
|
|
86
262
|
name: def.name,
|
|
87
263
|
label: item.label,
|
|
88
264
|
cat: item.cat,
|
|
89
265
|
description: def.description ?? "",
|
|
266
|
+
intro: hint.intro ?? null,
|
|
267
|
+
fields: hint.fields ?? {},
|
|
90
268
|
schema: def.inputSchema ?? { type: "object", properties: {}, required: [] },
|
|
91
269
|
});
|
|
92
270
|
}
|
|
@@ -96,6 +274,7 @@ function buildView({ tools, handlers, config, version }) {
|
|
|
96
274
|
defaultSystem: config.defaultSystem ?? null,
|
|
97
275
|
readOnly: Boolean(config.readOnly),
|
|
98
276
|
tools: descriptors,
|
|
277
|
+
columnLabels: COLUMN_LABELS,
|
|
99
278
|
};
|
|
100
279
|
return { allow, meta, html: renderHtml() };
|
|
101
280
|
}
|
|
@@ -319,6 +498,40 @@ function renderHtml() {
|
|
|
319
498
|
pre.out:empty { display:none; }
|
|
320
499
|
.muted { color:#717a90; }
|
|
321
500
|
#boot { padding:40px 20px; color:#8b93a7; }
|
|
501
|
+
/* friendly form */
|
|
502
|
+
.field .help { display:block; color:#717a90; font-size:10.5px; margin-top:3px; }
|
|
503
|
+
details.adv { margin:4px 0 8px; border-top:1px dashed #2a3142; padding-top:6px; }
|
|
504
|
+
details.adv > summary { cursor:pointer; color:#8b93a7; font-size:11px; list-style:none; user-select:none; }
|
|
505
|
+
details.adv > summary::before { content:"▸ "; }
|
|
506
|
+
details.adv[open] > summary::before { content:"▾ "; }
|
|
507
|
+
/* rendered results */
|
|
508
|
+
.res { margin-top:10px; }
|
|
509
|
+
.res:empty { display:none; }
|
|
510
|
+
.summary { display:flex; flex-wrap:wrap; gap:6px; margin-bottom:8px; }
|
|
511
|
+
.chip { font-size:10.5px; padding:2px 8px; border-radius:999px; background:#1b2030; color:#aeb6c9; }
|
|
512
|
+
.chip b { color:#e6e8ee; font-weight:600; }
|
|
513
|
+
.chip.ok { background:#13361f; color:#5fd38a; }
|
|
514
|
+
.chip.bad { background:#3a1d1d; color:#f0a3a3; }
|
|
515
|
+
.errbox { background:#2a1414; border:1px solid #5a2a2a; border-radius:7px; padding:10px;
|
|
516
|
+
color:#f0a3a3; font-size:12px; }
|
|
517
|
+
.errbox .st { font-weight:600; color:#ffd0d0; }
|
|
518
|
+
.blk { margin:8px 0; }
|
|
519
|
+
.blk > .blktitle { font-size:11px; color:#9aa3b8; margin-bottom:4px; text-transform:uppercase; letter-spacing:.05em; }
|
|
520
|
+
table.grid { width:100%; border-collapse:collapse; font-size:11.5px; display:block; overflow:auto; max-height:360px; }
|
|
521
|
+
table.grid th, table.grid td { border:1px solid #232838; padding:4px 7px; text-align:left;
|
|
522
|
+
vertical-align:top; white-space:pre-wrap; word-break:break-word; max-width:340px; }
|
|
523
|
+
table.grid th { background:#171c28; color:#9aa3b8; position:sticky; top:0; font-weight:600; }
|
|
524
|
+
table.grid tr:nth-child(even) td { background:#10131b; }
|
|
525
|
+
.code { background:#0b0d13; border:1px solid #202635; border-radius:7px; padding:10px;
|
|
526
|
+
max-height:340px; overflow:auto; white-space:pre-wrap; word-break:break-word;
|
|
527
|
+
font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:11.5px; color:#cdd3e0; }
|
|
528
|
+
.chapter { margin:8px 0; }
|
|
529
|
+
.chapter > .chtitle { font-size:11.5px; color:#cda; font-weight:600; margin-bottom:3px; }
|
|
530
|
+
details.raw { margin-top:10px; }
|
|
531
|
+
details.raw > summary { cursor:pointer; color:#717a90; font-size:11px; }
|
|
532
|
+
details.raw pre { margin:6px 0 0; background:#0b0d13; border:1px solid #202635; border-radius:7px;
|
|
533
|
+
padding:10px; max-height:300px; overflow:auto; white-space:pre-wrap; word-break:break-word;
|
|
534
|
+
font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:11px; color:#8b93a7; }
|
|
322
535
|
</style>
|
|
323
536
|
</head>
|
|
324
537
|
<body>
|
|
@@ -347,49 +560,64 @@ async function call(name, args){
|
|
|
347
560
|
return r.json();
|
|
348
561
|
}
|
|
349
562
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
563
|
+
let COLUMN_LABELS = {};
|
|
564
|
+
const CODE_KEYS = new Set(["source","result","raw","rawtext","metadataxml","runresponse","resultxml"]);
|
|
565
|
+
|
|
566
|
+
function humanize(k){
|
|
567
|
+
return String(k).replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ")
|
|
568
|
+
.replace(/^./,c=>c.toUpperCase());
|
|
569
|
+
}
|
|
570
|
+
function labelFor(k){ return COLUMN_LABELS[k] || humanize(k); }
|
|
571
|
+
function el(tag, cls, txt){ const e=document.createElement(tag); if(cls) e.className=cls; if(txt!=null) e.textContent=txt; return e; }
|
|
572
|
+
function short(v, n){ const s=String(v); return s.length>n ? s.slice(0,n)+"…" : s; }
|
|
573
|
+
function isPlainObject(v){ return v && typeof v==="object" && !Array.isArray(v); }
|
|
574
|
+
|
|
575
|
+
// ----- friendly form ---------------------------------------------------------
|
|
576
|
+
function fieldFor(key, prop, required, hint){
|
|
577
|
+
hint=hint||{};
|
|
578
|
+
const wrap=el("div","field");
|
|
579
|
+
const lab=el("label",null,hint.label||key);
|
|
580
|
+
if(required){ const s=el("span","req"," *"); lab.appendChild(s); }
|
|
355
581
|
wrap.appendChild(lab);
|
|
356
582
|
let input;
|
|
357
|
-
const t
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
input.
|
|
583
|
+
const t=prop.type;
|
|
584
|
+
const options = hint.options || (Array.isArray(prop.enum) ? prop.enum.map(v=>({value:v,label:v})) : null);
|
|
585
|
+
if(options){
|
|
586
|
+
input=document.createElement("select");
|
|
587
|
+
if(!hint.options){ const b=el("option",null,"(varsayılan)"); b.value=""; input.appendChild(b); }
|
|
588
|
+
for(const o of options){ const op=el("option",null,o.label); op.value=o.value; input.appendChild(op); }
|
|
589
|
+
} else if(t==="boolean"){
|
|
590
|
+
input=document.createElement("input"); input.type="checkbox"; input.style.width="auto";
|
|
361
591
|
} else if(t==="integer"||t==="number"){
|
|
362
592
|
input=document.createElement("input"); input.type="number";
|
|
363
593
|
if(prop.minimum!=null) input.min=prop.minimum;
|
|
364
594
|
if(prop.maximum!=null) input.max=prop.maximum;
|
|
365
595
|
} else if(t==="array"||t==="object"){
|
|
366
596
|
input=document.createElement("textarea");
|
|
367
|
-
input.placeholder="JSON, örn: "+(t==="array"?'[{"name":"...","type":"..."}]':'{}');
|
|
368
|
-
} else if(Array.isArray(prop.enum)){
|
|
369
|
-
input=document.createElement("select");
|
|
370
|
-
const blank=document.createElement("option"); blank.value=""; blank.textContent="(varsayılan)";
|
|
371
|
-
input.appendChild(blank);
|
|
372
|
-
for(const v of prop.enum){ const o=document.createElement("option"); o.value=v; o.textContent=v; input.appendChild(o); }
|
|
597
|
+
input.placeholder=hint.placeholder || ("JSON, örn: "+(t==="array"?'[{"name":"...","type":"..."}]':'{}'));
|
|
373
598
|
} else {
|
|
374
599
|
input=document.createElement("input"); input.type="text";
|
|
600
|
+
if(hint.placeholder) input.placeholder=hint.placeholder;
|
|
375
601
|
}
|
|
376
602
|
input.dataset.key=key; input.dataset.jtype=t||"string";
|
|
377
|
-
if(
|
|
603
|
+
if(hint.default!=null){ if(input.type==="checkbox") input.checked=!!hint.default; else input.value=String(hint.default); }
|
|
604
|
+
const tip=hint.help||prop.description; if(tip) input.title=tip;
|
|
378
605
|
wrap.appendChild(input);
|
|
606
|
+
if(hint.help){ wrap.appendChild(el("span","help",hint.help)); }
|
|
379
607
|
return wrap;
|
|
380
608
|
}
|
|
381
609
|
|
|
382
610
|
function collect(card){
|
|
383
611
|
const args={};
|
|
384
|
-
for(const
|
|
385
|
-
const k=
|
|
386
|
-
if(
|
|
387
|
-
const raw=
|
|
612
|
+
for(const node of card.querySelectorAll("[data-key]")){
|
|
613
|
+
const k=node.dataset.key, t=node.dataset.jtype;
|
|
614
|
+
if(node.type==="checkbox"){ if(node.checked) args[k]=true; continue; }
|
|
615
|
+
const raw=node.value.trim();
|
|
388
616
|
if(raw==="") continue;
|
|
389
617
|
if(t==="integer"){ args[k]=parseInt(raw,10); }
|
|
390
618
|
else if(t==="number"){ args[k]=Number(raw); }
|
|
391
619
|
else if(t==="array"||t==="object"){
|
|
392
|
-
try { args[k]=JSON.parse(raw); } catch(e){ throw new Error(k+": geçersiz JSON"); }
|
|
620
|
+
try { args[k]=JSON.parse(raw); } catch(e){ throw new Error((k)+": geçersiz JSON"); }
|
|
393
621
|
}
|
|
394
622
|
else args[k]=raw;
|
|
395
623
|
}
|
|
@@ -397,37 +625,167 @@ function collect(card){
|
|
|
397
625
|
}
|
|
398
626
|
|
|
399
627
|
function buildCard(tool, getSystem){
|
|
400
|
-
const card=
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
628
|
+
const card=el("section","card");
|
|
629
|
+
card.appendChild(el("div","cat",tool.cat));
|
|
630
|
+
card.appendChild(el("h3",null,tool.label));
|
|
631
|
+
card.appendChild(el("div","desc",tool.intro||tool.description));
|
|
404
632
|
|
|
405
633
|
const props=tool.schema.properties||{};
|
|
406
634
|
const req=new Set(tool.schema.required||[]);
|
|
635
|
+
const hints=tool.fields||{};
|
|
636
|
+
const adv=el("details","adv"); adv.appendChild(el("summary",null,"Gelişmiş ayarlar"));
|
|
637
|
+
let advCount=0;
|
|
407
638
|
for(const key of Object.keys(props)){
|
|
408
|
-
if(key==="system") continue; //
|
|
409
|
-
|
|
639
|
+
if(key==="system") continue; // global system selector handles it
|
|
640
|
+
const hint=hints[key]||{};
|
|
641
|
+
const f=fieldFor(key, props[key], req.has(key), hint);
|
|
642
|
+
if(hint.advanced){ adv.appendChild(f); advCount++; } else card.appendChild(f);
|
|
410
643
|
}
|
|
411
|
-
|
|
412
|
-
|
|
644
|
+
if(advCount>0) card.appendChild(adv);
|
|
645
|
+
|
|
646
|
+
const btn=el("button","run","Çalıştır"); card.appendChild(btn);
|
|
647
|
+
const out=el("div","res"); card.appendChild(out);
|
|
413
648
|
|
|
414
649
|
btn.addEventListener("click", async ()=>{
|
|
415
650
|
let args;
|
|
416
|
-
try { args=collect(card); } catch(e){ out
|
|
417
|
-
const sys=getSystem();
|
|
418
|
-
if(sys) args.system=sys;
|
|
651
|
+
try { args=collect(card); } catch(e){ showError(out, e.message); return; }
|
|
652
|
+
const sys=getSystem(); if(sys) args.system=sys;
|
|
419
653
|
btn.disabled=true; const old=btn.textContent; btn.textContent="Çalışıyor…";
|
|
420
|
-
out.
|
|
654
|
+
out.textContent="";
|
|
421
655
|
try {
|
|
422
656
|
const res=await call(tool.name, args);
|
|
423
|
-
out.
|
|
424
|
-
|
|
425
|
-
} catch(e){ out.className="out err"; out.textContent=String(e.message||e); }
|
|
657
|
+
renderResult(out, tool.name, res);
|
|
658
|
+
} catch(e){ showError(out, String(e.message||e)); }
|
|
426
659
|
finally { btn.disabled=false; btn.textContent=old; }
|
|
427
660
|
});
|
|
428
661
|
return card;
|
|
429
662
|
}
|
|
430
663
|
|
|
664
|
+
// ----- result rendering ------------------------------------------------------
|
|
665
|
+
function showError(out, msg){ out.innerHTML=""; const b=el("div","errbox"); b.appendChild(el("span","st","Hata")); b.appendChild(document.createTextNode(" — "+msg)); out.appendChild(b); }
|
|
666
|
+
|
|
667
|
+
function renderResult(out, tool, res){
|
|
668
|
+
out.innerHTML="";
|
|
669
|
+
const raw=(res&&res.text)||"";
|
|
670
|
+
let data;
|
|
671
|
+
try { data=JSON.parse(raw); }
|
|
672
|
+
catch(e){ out.appendChild(el("div","code",raw||"(boş yanıt)")); return; }
|
|
673
|
+
|
|
674
|
+
if((res&&res.isError) || (isPlainObject(data)&&data.ok===false)){
|
|
675
|
+
renderErrorObject(out, data);
|
|
676
|
+
appendRaw(out, data);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
if(!isPlainObject(data)){ out.appendChild(el("div","code",raw)); return; }
|
|
680
|
+
renderObject(out, data);
|
|
681
|
+
appendRaw(out, data);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function renderErrorObject(out, data){
|
|
685
|
+
const b=el("div","errbox");
|
|
686
|
+
const err=data.error||{};
|
|
687
|
+
const msg = err.message || err.localizedMessage || (typeof err==="string"?err:null)
|
|
688
|
+
|| err.raw || data.parseError || "İstek başarısız.";
|
|
689
|
+
const st = data.status!=null ? ("HTTP "+data.status) : "Hata";
|
|
690
|
+
b.appendChild(el("span","st",st));
|
|
691
|
+
b.appendChild(document.createTextNode(" — "+short(msg, 1200)));
|
|
692
|
+
out.appendChild(b);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function renderObject(out, data){
|
|
696
|
+
const scalars=[], tables=[], scalarArrays=[], maps=[], codes=[];
|
|
697
|
+
for(const [k,v] of Object.entries(data)){
|
|
698
|
+
if(v==null) continue;
|
|
699
|
+
const lk=k.toLowerCase();
|
|
700
|
+
if(CODE_KEYS.has(lk) || (typeof v==="string" && (v.indexOf("\\n")>=0 || v.length>160))){ codes.push([k,v]); }
|
|
701
|
+
else if(Array.isArray(v)){
|
|
702
|
+
if(v.length && isPlainObject(v[0])) tables.push([k,v]);
|
|
703
|
+
else if(v.length) scalarArrays.push([k,v]);
|
|
704
|
+
}
|
|
705
|
+
else if(isPlainObject(v)) maps.push([k,v]);
|
|
706
|
+
else scalars.push([k,v]); // string/number/boolean
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if(scalars.length){
|
|
710
|
+
const s=el("div","summary");
|
|
711
|
+
for(const [k,v] of scalars){
|
|
712
|
+
const c=el("span","chip"+(typeof v==="boolean"?(v?" ok":" bad"):""));
|
|
713
|
+
const b=el("b",null,labelFor(k)+": ");
|
|
714
|
+
c.appendChild(b);
|
|
715
|
+
c.appendChild(document.createTextNode(typeof v==="boolean"?(v?"evet":"hayır"):short(v,80)));
|
|
716
|
+
s.appendChild(c);
|
|
717
|
+
}
|
|
718
|
+
out.appendChild(s);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
for(const [k,arr] of tables){ out.appendChild(blockTitle(labelFor(k)+" ("+arr.length+")")); out.appendChild(makeTable(arr)); }
|
|
722
|
+
|
|
723
|
+
for(const [k,arr] of scalarArrays){
|
|
724
|
+
const blk=el("div","blk"); blk.appendChild(el("div","blktitle",labelFor(k)+" ("+arr.length+")"));
|
|
725
|
+
blk.appendChild(el("div","code", arr.map(x=>String(x)).join(", "))); out.appendChild(blk);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
for(const [k,obj] of maps){
|
|
729
|
+
const vals=Object.values(obj);
|
|
730
|
+
if(vals.length && vals.every(x=>typeof x==="string")){
|
|
731
|
+
// chapter-style sections (e.g. dump chapters)
|
|
732
|
+
const blk=el("div","blk"); blk.appendChild(el("div","blktitle",labelFor(k)));
|
|
733
|
+
for(const [sk,sv] of Object.entries(obj)){
|
|
734
|
+
const ch=el("div","chapter"); ch.appendChild(el("div","chtitle",labelFor(sk)));
|
|
735
|
+
ch.appendChild(el("div","code",short(sv,8000))); blk.appendChild(ch);
|
|
736
|
+
}
|
|
737
|
+
out.appendChild(blk);
|
|
738
|
+
} else if(vals.length && vals.every(x=>typeof x==="number")){
|
|
739
|
+
// histogram (e.g. byPriority)
|
|
740
|
+
const blk=el("div","blk"); blk.appendChild(el("div","blktitle",labelFor(k)));
|
|
741
|
+
const s=el("div","summary");
|
|
742
|
+
for(const [sk,sv] of Object.entries(obj)){ const c=el("span","chip"); c.appendChild(el("b",null,labelFor(sk)+": ")); c.appendChild(document.createTextNode(String(sv))); s.appendChild(c); }
|
|
743
|
+
blk.appendChild(s); out.appendChild(blk);
|
|
744
|
+
} else {
|
|
745
|
+
const d=el("details","raw"); d.appendChild(el("summary",null,labelFor(k)));
|
|
746
|
+
d.appendChild(el("pre",null,JSON.stringify(obj,null,2))); out.appendChild(d);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
for(const [k,v] of codes){
|
|
751
|
+
const blk=el("div","blk"); blk.appendChild(el("div","blktitle",labelFor(k)));
|
|
752
|
+
blk.appendChild(el("div","code",short(v,20000))); out.appendChild(blk);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
if(!scalars.length && !tables.length && !scalarArrays.length && !maps.length && !codes.length){
|
|
756
|
+
out.appendChild(el("div","code","(boş yanıt)"));
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function blockTitle(t){ return el("div","blktitle",t); }
|
|
761
|
+
|
|
762
|
+
function makeTable(rows){
|
|
763
|
+
const cols=[]; const seen=new Set();
|
|
764
|
+
for(const r of rows){ for(const k of Object.keys(r)){ if(!seen.has(k)){ seen.add(k); cols.push(k); } } }
|
|
765
|
+
const table=el("table","grid");
|
|
766
|
+
const thead=document.createElement("thead"); const htr=document.createElement("tr");
|
|
767
|
+
for(const c of cols) htr.appendChild(el("th",null,labelFor(c)));
|
|
768
|
+
thead.appendChild(htr); table.appendChild(thead);
|
|
769
|
+
const tbody=document.createElement("tbody");
|
|
770
|
+
for(const r of rows){
|
|
771
|
+
const tr=document.createElement("tr");
|
|
772
|
+
for(const c of cols){
|
|
773
|
+
const v=r[c];
|
|
774
|
+
const cell = v==null ? "" : (typeof v==="object" ? JSON.stringify(v) : String(v));
|
|
775
|
+
tr.appendChild(el("td",null,short(cell,300)));
|
|
776
|
+
}
|
|
777
|
+
tbody.appendChild(tr);
|
|
778
|
+
}
|
|
779
|
+
table.appendChild(tbody);
|
|
780
|
+
return table;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function appendRaw(out, data){
|
|
784
|
+
const d=el("details","raw"); d.appendChild(el("summary",null,"Ham JSON"));
|
|
785
|
+
d.appendChild(el("pre",null,JSON.stringify(data,null,2)));
|
|
786
|
+
out.appendChild(d);
|
|
787
|
+
}
|
|
788
|
+
|
|
431
789
|
(async function init(){
|
|
432
790
|
let meta;
|
|
433
791
|
try {
|
|
@@ -438,10 +796,11 @@ function buildCard(tool, getSystem){
|
|
|
438
796
|
$("#boot").textContent="Panel'e bağlanılamadı: "+e.message+" — token geçersiz ya da session kapandı.";
|
|
439
797
|
return;
|
|
440
798
|
}
|
|
799
|
+
COLUMN_LABELS = meta.columnLabels || {};
|
|
441
800
|
$("#ver").textContent="v"+meta.version;
|
|
442
801
|
if(meta.readOnly) $("#globalro").style.display="";
|
|
443
802
|
const sel=$("#system");
|
|
444
|
-
for(const s of meta.systems){ const o=
|
|
803
|
+
for(const s of meta.systems){ const o=el("option",null,s); o.value=s; sel.appendChild(o); }
|
|
445
804
|
if(meta.defaultSystem) sel.value=meta.defaultSystem;
|
|
446
805
|
const getSystem=()=>sel.value||"";
|
|
447
806
|
|