@deerdesign/deertools 1.2.26 → 1.2.31
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/README.md +55 -62
- package/package.json +4 -3
- package/src/baselinker-commands.js +21 -94
- package/src/cli.js +29 -8
- package/src/commands.js +169 -138
- package/src/completion.js +24 -18
- package/src/embedding.js +59 -0
- package/src/rendermind/cli-commands.js +31 -0
- package/src/rendermind/client.js +393 -0
- package/src/rendermind/mcp-server.js +305 -0
- package/src/skill-commands.js +420 -24
- package/src/skills-hub.js +78 -2
- package/src/supplier/cli-commands.js +32 -0
- package/src/supplier/mcp-server.js +360 -0
- package/src/tui.js +24 -39
package/src/skills-hub.js
CHANGED
|
@@ -5,8 +5,11 @@ const crypto = require('crypto');
|
|
|
5
5
|
const credentials = require('./credentials');
|
|
6
6
|
const { fetchText } = require('./http');
|
|
7
7
|
const { parseFrontmatterName } = require('./openapi');
|
|
8
|
+
const { compareSemver } = require('./updater');
|
|
8
9
|
|
|
9
10
|
const DEFAULT_API_BASE = 'https://ezspmlsnmxuhtzvwrnoj.supabase.co/functions/v1/skills-api';
|
|
11
|
+
const DEFAULT_SUPABASE_URL = 'https://ezspmlsnmxuhtzvwrnoj.supabase.co';
|
|
12
|
+
const DEFAULT_ANON_KEY = 'sb_publishable_y8iokK-V2X6A1HZb94g_pw_Le0bm3f2';
|
|
10
13
|
const SECRET_PATTERNS = [
|
|
11
14
|
{ name: 'DeerTools Live API Key', regex: /deer_[a-z]+_live_[A-Za-z0-9_]{20,}/g },
|
|
12
15
|
{ name: 'GitHub Token', regex: /gh[pousr]_[A-Za-z0-9_]{30,}/g },
|
|
@@ -86,6 +89,67 @@ async function listSkills(params = {}) {
|
|
|
86
89
|
return hubRequest(`/skills${qs ? `?${qs}` : ''}`);
|
|
87
90
|
}
|
|
88
91
|
|
|
92
|
+
function getSupabaseUrl() {
|
|
93
|
+
return process.env.DEERTOOLS_SUPABASE_URL || DEFAULT_SUPABASE_URL;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getSupabaseAnonKey() {
|
|
97
|
+
return process.env.DEERTOOLS_SUPABASE_KEY || DEFAULT_ANON_KEY;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Serwerowe wyszukiwanie wektorowo-hybrydowe (PostgreSQL pgvector + trigram + keyword boost).
|
|
102
|
+
*/
|
|
103
|
+
async function searchSkillsHybrid({ queryText = '', queryEmbedding = null, threshold = 0.05, count = 50 } = {}) {
|
|
104
|
+
const base = getSupabaseUrl().replace(/\/+$/, '');
|
|
105
|
+
const url = `${base}/rest/v1/rpc/match_skills_hybrid`;
|
|
106
|
+
const anonKey = getSupabaseAnonKey();
|
|
107
|
+
const userKey = getApiKey();
|
|
108
|
+
|
|
109
|
+
const body = {
|
|
110
|
+
query_text: String(queryText || ''),
|
|
111
|
+
query_embedding: queryEmbedding || null,
|
|
112
|
+
match_threshold: threshold,
|
|
113
|
+
match_count: count,
|
|
114
|
+
};
|
|
115
|
+
if (userKey) {
|
|
116
|
+
body.caller_api_key = userKey;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const headers = {
|
|
120
|
+
'Content-Type': 'application/json',
|
|
121
|
+
'apikey': anonKey,
|
|
122
|
+
'Authorization': `Bearer ${anonKey}`,
|
|
123
|
+
'User-Agent': 'deertools-cli',
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const res = await fetch(url, {
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers,
|
|
130
|
+
body: JSON.stringify(body),
|
|
131
|
+
});
|
|
132
|
+
let data = null;
|
|
133
|
+
try {
|
|
134
|
+
data = await res.json();
|
|
135
|
+
} catch (_) {
|
|
136
|
+
data = null;
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
ok: res.ok,
|
|
140
|
+
status: res.status,
|
|
141
|
+
data,
|
|
142
|
+
};
|
|
143
|
+
} catch (err) {
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
status: 500,
|
|
147
|
+
data: null,
|
|
148
|
+
error: err.message,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
89
153
|
async function getSkill(slug, version = null) {
|
|
90
154
|
const ver = typeof version === 'object' && version !== null ? version.version : version;
|
|
91
155
|
const qs = ver ? `?version=${encodeURIComponent(ver)}` : '';
|
|
@@ -272,6 +336,7 @@ async function pushSkill(skillDir, { version = null, bump = 'patch', changelog =
|
|
|
272
336
|
let title = slug;
|
|
273
337
|
let description = '';
|
|
274
338
|
let frontmatterTags = [];
|
|
339
|
+
let frontmatterVersion = null;
|
|
275
340
|
if (skillContent.startsWith('---')) {
|
|
276
341
|
const parts = skillContent.split('---', 2);
|
|
277
342
|
if (parts.length >= 2) {
|
|
@@ -282,6 +347,8 @@ async function pushSkill(skillDir, { version = null, bump = 'patch', changelog =
|
|
|
282
347
|
}
|
|
283
348
|
const descMatch = parts[1].match(/^description:\s*(.+)$/m);
|
|
284
349
|
if (descMatch) description = descMatch[1].trim();
|
|
350
|
+
const verMatch = parts[1].match(/^version:\s*['"]?([0-9]+\.[0-9]+(\.[0-9]+)?(-[a-zA-Z0-9.]+)?)/im);
|
|
351
|
+
if (verMatch) frontmatterVersion = verMatch[1].trim();
|
|
285
352
|
frontmatterTags = extractFrontmatterTags(parts[1]);
|
|
286
353
|
}
|
|
287
354
|
}
|
|
@@ -301,9 +368,15 @@ async function pushSkill(skillDir, { version = null, bump = 'patch', changelog =
|
|
|
301
368
|
try {
|
|
302
369
|
const existing = await getSkill(slug);
|
|
303
370
|
const latestVer = existing?.data?.skill?.latest_version;
|
|
304
|
-
|
|
371
|
+
if (frontmatterVersion && latestVer && compareSemver(frontmatterVersion, latestVer) > 0) {
|
|
372
|
+
targetVersion = frontmatterVersion;
|
|
373
|
+
} else if (frontmatterVersion && !latestVer) {
|
|
374
|
+
targetVersion = frontmatterVersion;
|
|
375
|
+
} else {
|
|
376
|
+
targetVersion = latestVer ? bumpVersion(latestVer, bump) : (frontmatterVersion || '1.0.0');
|
|
377
|
+
}
|
|
305
378
|
} catch (_) {
|
|
306
|
-
targetVersion = '1.0.0';
|
|
379
|
+
targetVersion = frontmatterVersion || '1.0.0';
|
|
307
380
|
}
|
|
308
381
|
}
|
|
309
382
|
|
|
@@ -462,8 +535,11 @@ async function submitReview(slug) {
|
|
|
462
535
|
module.exports = {
|
|
463
536
|
getApiBase,
|
|
464
537
|
getApiKey,
|
|
538
|
+
getSupabaseUrl,
|
|
539
|
+
getSupabaseAnonKey,
|
|
465
540
|
scanForSecrets,
|
|
466
541
|
listSkills,
|
|
542
|
+
searchSkillsHybrid,
|
|
467
543
|
getSkill,
|
|
468
544
|
pushSkill,
|
|
469
545
|
bumpVersion,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { runSupplierMcpServer } = require('./mcp-server');
|
|
3
|
+
|
|
4
|
+
function out(msg) { process.stdout.write(msg + '\n'); }
|
|
5
|
+
|
|
6
|
+
async function cmdSupplier(pos) {
|
|
7
|
+
const sub = pos[0];
|
|
8
|
+
|
|
9
|
+
if (sub === 'mcp') {
|
|
10
|
+
return runSupplierMcpServer();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
out(`Użycie: deertools supplier mcp
|
|
14
|
+
|
|
15
|
+
Zgodnie ze standardem architektonicznym DeerTools CLI:
|
|
16
|
+
- Operacje biznesowe na rabatach, narzutach i dostawcach wykonuje Agent AI wyłącznie przez serwer MCP (deer-supplier).
|
|
17
|
+
- Dostępne narzędzia MCP:
|
|
18
|
+
* supplier_list_suppliers
|
|
19
|
+
* supplier_get_discounts
|
|
20
|
+
* supplier_get_markups
|
|
21
|
+
* supplier_get_sales_discounts
|
|
22
|
+
* supplier_evaluate_batch
|
|
23
|
+
* supplier_list_rules
|
|
24
|
+
* supplier_list_taxonomy
|
|
25
|
+
* supplier_list_configurations
|
|
26
|
+
|
|
27
|
+
Aby zainstalować serwer MCP w Cursor / Antigravity, uruchom:
|
|
28
|
+
deertools mcp install
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { cmdSupplier };
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const readline = require('readline');
|
|
3
|
+
const credentials = require('../credentials');
|
|
4
|
+
const { getProduct } = require('../registry');
|
|
5
|
+
|
|
6
|
+
const PROTOCOL_VERSION = '2024-11-05';
|
|
7
|
+
const SERVER_INFO = { name: 'deer-supplier-mcp', version: '1.0.0' };
|
|
8
|
+
const SUPPLIER_API_BASE = 'https://hubnxvzxdgocbtmkfdsz.supabase.co/functions/v1/public-api';
|
|
9
|
+
|
|
10
|
+
function getSupplierKey() {
|
|
11
|
+
const p = getProduct('supplier');
|
|
12
|
+
const cred = credentials.getKey(p);
|
|
13
|
+
if (cred && cred.key) return cred.key;
|
|
14
|
+
if (process.env.SUPPLIER_API_KEY) return process.env.SUPPLIER_API_KEY;
|
|
15
|
+
if (process.env.DEERTOOLS_SUPPLIER_KEY) return process.env.DEERTOOLS_SUPPLIER_KEY;
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function ensureSupplierAuth() {
|
|
20
|
+
const key = getSupplierKey();
|
|
21
|
+
if (!key) {
|
|
22
|
+
throw new Error('Brak klucza API do Supplier Hub. Zaloguj się poleceniem: deertools login supplier');
|
|
23
|
+
}
|
|
24
|
+
return key;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function callSupplierApi(method, path, { query = {}, body = undefined } = {}) {
|
|
28
|
+
const apiKey = ensureSupplierAuth();
|
|
29
|
+
const url = new URL(SUPPLIER_API_BASE + path);
|
|
30
|
+
for (const [k, v] of Object.entries(query)) {
|
|
31
|
+
if (v !== undefined && v !== null && v !== '') {
|
|
32
|
+
url.searchParams.set(k, String(v));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const headers = {
|
|
36
|
+
'x-api-key': apiKey,
|
|
37
|
+
'Accept': 'application/json'
|
|
38
|
+
};
|
|
39
|
+
let payload;
|
|
40
|
+
if (body !== undefined) {
|
|
41
|
+
headers['Content-Type'] = 'application/json';
|
|
42
|
+
payload = JSON.stringify(body);
|
|
43
|
+
}
|
|
44
|
+
const res = await fetch(url.toString(), { method, headers, body: payload });
|
|
45
|
+
const text = await res.text();
|
|
46
|
+
let json = null;
|
|
47
|
+
try { json = JSON.parse(text); } catch (_) {}
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
throw new Error(`Supplier Hub API (${res.status}): ${(json && json.error) || text}`);
|
|
50
|
+
}
|
|
51
|
+
return json !== null ? json : text;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const SUPPLIER_TOOLS = [
|
|
55
|
+
{
|
|
56
|
+
name: 'supplier_list_suppliers',
|
|
57
|
+
description: 'Wyszukuje dostawców dla producenta i lokalizacji (kod pocztowy, miasto lub województwo) w DeerTools Supplier Hub.',
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
manufacturer: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
description: 'Nazwa, external_id lub UUID producenta (np. "Flügger", "LAUFEN", "Roca").'
|
|
64
|
+
},
|
|
65
|
+
postal_code: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
description: 'Kod pocztowy w formacie XX-XXX (np. "00-001", "30-001").'
|
|
68
|
+
},
|
|
69
|
+
city: {
|
|
70
|
+
type: 'string',
|
|
71
|
+
description: 'Nazwa miasta/gminy (np. "Warszawa", "Kraków").'
|
|
72
|
+
},
|
|
73
|
+
voivodeship: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
description: 'Nazwa województwa.'
|
|
76
|
+
},
|
|
77
|
+
pick: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
enum: ['random', 'first', 'all'],
|
|
80
|
+
default: 'all',
|
|
81
|
+
description: 'Sposób zwracania dopasowanych dostawców (domyślnie all).'
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
required: ['manufacturer']
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'supplier_get_discounts',
|
|
89
|
+
description: 'Dopasowuje aktywne rabaty zakupowe w Supplier Hub dla producenta, kolekcji, kategorii lub produktu.',
|
|
90
|
+
inputSchema: {
|
|
91
|
+
type: 'object',
|
|
92
|
+
properties: {
|
|
93
|
+
manufacturer: { type: 'string', description: 'Nazwa, external_id lub UUID producenta.' },
|
|
94
|
+
supplier: { type: 'string', description: 'Opcjonalna nazwa lub UUID dostawcy.' },
|
|
95
|
+
collection: { type: 'string', description: 'Opcjonalna nazwa kolekcji.' },
|
|
96
|
+
category: { type: 'string', description: 'Opcjonalna nazwa kategorii.' },
|
|
97
|
+
tag: { type: 'string', description: 'Opcjonalny tag produktu.' },
|
|
98
|
+
product_id: { type: 'string', description: 'Opcjonalny identyfikator lub SKU produktu.' },
|
|
99
|
+
best_only: { type: 'boolean', default: false, description: 'Czy zwrócić wyłącznie najwyżej dopasowany rabat.' }
|
|
100
|
+
},
|
|
101
|
+
required: ['manufacturer']
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'supplier_get_markups',
|
|
106
|
+
description: 'Dopasowuje narzuty cenowe w Supplier Hub dla producenta, kolekcji, kategorii lub produktu.',
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: 'object',
|
|
109
|
+
properties: {
|
|
110
|
+
manufacturer: { type: 'string', description: 'Nazwa, external_id lub UUID producenta.' },
|
|
111
|
+
supplier: { type: 'string', description: 'Opcjonalna nazwa lub UUID dostawcy.' },
|
|
112
|
+
collection: { type: 'string', description: 'Opcjonalna nazwa kolekcji.' },
|
|
113
|
+
category: { type: 'string', description: 'Opcjonalna nazwa kategorii.' },
|
|
114
|
+
tag: { type: 'string', description: 'Opcjonalny tag produktu.' },
|
|
115
|
+
product_id: { type: 'string', description: 'Opcjonalny identyfikator lub SKU produktu.' },
|
|
116
|
+
best_only: { type: 'boolean', default: false, description: 'Czy zwrócić wyłącznie najlepiej dopasowany narzut.' }
|
|
117
|
+
},
|
|
118
|
+
required: ['manufacturer']
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'supplier_get_sales_discounts',
|
|
123
|
+
description: 'Dopasowuje aktywne promocje sprzedaży w Supplier Hub dla producenta, kolekcji lub produktu.',
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: 'object',
|
|
126
|
+
properties: {
|
|
127
|
+
manufacturer: { type: 'string', description: 'Nazwa, external_id lub UUID producenta.' },
|
|
128
|
+
supplier: { type: 'string', description: 'Opcjonalna nazwa lub UUID dostawcy.' },
|
|
129
|
+
collection: { type: 'string', description: 'Opcjonalna nazwa kolekcji.' },
|
|
130
|
+
category: { type: 'string', description: 'Opcjonalna nazwa kategorii.' },
|
|
131
|
+
tag: { type: 'string', description: 'Opcjonalny tag produktu.' },
|
|
132
|
+
product_id: { type: 'string', description: 'Opcjonalny identyfikator lub SKU produktu.' },
|
|
133
|
+
best_only: { type: 'boolean', default: false, description: 'Czy zwrócić wyłącznie najlepiej dopasowaną promocję.' }
|
|
134
|
+
},
|
|
135
|
+
required: ['manufacturer']
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'supplier_evaluate_batch',
|
|
140
|
+
description: 'Wsadowe dopasowanie rabatów zakupowych, narzutów lub promocji sprzedaży dla maksymalnie 500 produktów jednego producenta.',
|
|
141
|
+
inputSchema: {
|
|
142
|
+
type: 'object',
|
|
143
|
+
properties: {
|
|
144
|
+
type: {
|
|
145
|
+
type: 'string',
|
|
146
|
+
enum: ['discounts', 'markups', 'sales-discounts'],
|
|
147
|
+
default: 'discounts',
|
|
148
|
+
description: 'Typ wyliczanych reguł: discounts (rabaty zakupowe), markups (narzuty), sales-discounts (promocje).'
|
|
149
|
+
},
|
|
150
|
+
manufacturer: { type: 'string', description: 'Nazwa, external_id lub UUID producenta.' },
|
|
151
|
+
best_only: { type: 'boolean', default: true, description: 'Czy zwracać tylko najlepsze dopasowanie per pozycja.' },
|
|
152
|
+
items: {
|
|
153
|
+
type: 'array',
|
|
154
|
+
description: 'Lista pozycji do przeliczenia (każda z unikalnym kluczem key).',
|
|
155
|
+
items: {
|
|
156
|
+
type: 'object',
|
|
157
|
+
properties: {
|
|
158
|
+
key: { type: 'string' },
|
|
159
|
+
product_id: { type: 'string' },
|
|
160
|
+
collection: { type: 'string' },
|
|
161
|
+
category: { type: 'string' },
|
|
162
|
+
tag: { type: 'string' }
|
|
163
|
+
},
|
|
164
|
+
required: ['key']
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
required: ['manufacturer', 'items']
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'supplier_list_rules',
|
|
173
|
+
description: 'Pobiera listę reguł cenowych (discounts, sales-discounts lub markups) wraz z warunkami.',
|
|
174
|
+
inputSchema: {
|
|
175
|
+
type: 'object',
|
|
176
|
+
properties: {
|
|
177
|
+
type: {
|
|
178
|
+
type: 'string',
|
|
179
|
+
enum: ['discounts', 'markups', 'sales-discounts'],
|
|
180
|
+
default: 'discounts',
|
|
181
|
+
description: 'Typ reguł cenowych.'
|
|
182
|
+
},
|
|
183
|
+
limit: { type: 'number', default: 50, description: 'Limit wyników (domyślnie 50).' },
|
|
184
|
+
include_conditions: { type: 'boolean', default: true, description: 'Czy dołączyć warunki reguł.' }
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: 'supplier_list_taxonomy',
|
|
190
|
+
description: 'Wyszukuje pozycje taksonomii w Supplier Hub (collections, categories lub tags).',
|
|
191
|
+
inputSchema: {
|
|
192
|
+
type: 'object',
|
|
193
|
+
properties: {
|
|
194
|
+
type: {
|
|
195
|
+
type: 'string',
|
|
196
|
+
enum: ['collections', 'categories', 'tags'],
|
|
197
|
+
default: 'collections',
|
|
198
|
+
description: 'Typ taksonomii.'
|
|
199
|
+
},
|
|
200
|
+
search: { type: 'string', description: 'Fraza wyszukiwania.' },
|
|
201
|
+
limit: { type: 'number', default: 50, description: 'Limit wyników.' }
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: 'supplier_list_configurations',
|
|
207
|
+
description: 'Pobiera konfiguracje powiązań dostawca–producent z Supplier Hub.',
|
|
208
|
+
inputSchema: {
|
|
209
|
+
type: 'object',
|
|
210
|
+
properties: {}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
];
|
|
214
|
+
|
|
215
|
+
async function handleMcpMessage(msg) {
|
|
216
|
+
if (!msg || typeof msg !== 'object') return null;
|
|
217
|
+
const { id, method, params } = msg;
|
|
218
|
+
|
|
219
|
+
if (method === 'initialize') {
|
|
220
|
+
return {
|
|
221
|
+
jsonrpc: '2.0',
|
|
222
|
+
id,
|
|
223
|
+
result: {
|
|
224
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
225
|
+
capabilities: { tools: {} },
|
|
226
|
+
serverInfo: SERVER_INFO
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (method === 'notifications/initialized' || method === 'initialized') {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (method === 'ping') {
|
|
236
|
+
return { jsonrpc: '2.0', id, result: {} };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (method === 'tools/list') {
|
|
240
|
+
return {
|
|
241
|
+
jsonrpc: '2.0',
|
|
242
|
+
id,
|
|
243
|
+
result: { tools: SUPPLIER_TOOLS }
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (method === 'tools/call') {
|
|
248
|
+
const toolName = params?.name;
|
|
249
|
+
const args = params?.arguments || {};
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
let data;
|
|
253
|
+
if (toolName === 'supplier_list_suppliers') {
|
|
254
|
+
data = await callSupplierApi('GET', '/suppliers', {
|
|
255
|
+
query: {
|
|
256
|
+
manufacturer: args.manufacturer,
|
|
257
|
+
postal_code: args.postal_code,
|
|
258
|
+
city: args.city,
|
|
259
|
+
voivodeship: args.voivodeship,
|
|
260
|
+
pick: args.pick || 'all'
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
} else if (toolName === 'supplier_get_discounts') {
|
|
264
|
+
data = await callSupplierApi('GET', '/discounts', { query: args });
|
|
265
|
+
} else if (toolName === 'supplier_get_markups') {
|
|
266
|
+
data = await callSupplierApi('GET', '/markups', { query: args });
|
|
267
|
+
} else if (toolName === 'supplier_get_sales_discounts') {
|
|
268
|
+
data = await callSupplierApi('GET', '/sales-discounts', { query: args });
|
|
269
|
+
} else if (toolName === 'supplier_evaluate_batch') {
|
|
270
|
+
const type = args.type || 'discounts';
|
|
271
|
+
data = await callSupplierApi('POST', `/${type}/evaluate`, {
|
|
272
|
+
body: {
|
|
273
|
+
manufacturer: args.manufacturer,
|
|
274
|
+
best_only: args.best_only !== false,
|
|
275
|
+
items: args.items
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
} else if (toolName === 'supplier_list_rules') {
|
|
279
|
+
const type = args.type || 'discounts';
|
|
280
|
+
data = await callSupplierApi('GET', `/rules/${type}`, {
|
|
281
|
+
query: {
|
|
282
|
+
limit: args.limit || 50,
|
|
283
|
+
include_conditions: args.include_conditions !== false
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
} else if (toolName === 'supplier_list_taxonomy') {
|
|
287
|
+
const type = args.type || 'collections';
|
|
288
|
+
data = await callSupplierApi('GET', `/taxonomy/${type}`, {
|
|
289
|
+
query: { search: args.search, limit: args.limit || 50 }
|
|
290
|
+
});
|
|
291
|
+
} else if (toolName === 'supplier_list_configurations') {
|
|
292
|
+
data = await callSupplierApi('GET', '/supplier-configurations');
|
|
293
|
+
} else {
|
|
294
|
+
return {
|
|
295
|
+
jsonrpc: '2.0',
|
|
296
|
+
id,
|
|
297
|
+
error: { code: -32601, message: `Nieznane narzędzie: ${toolName}` }
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
jsonrpc: '2.0',
|
|
303
|
+
id,
|
|
304
|
+
result: {
|
|
305
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }]
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
} catch (e) {
|
|
309
|
+
return {
|
|
310
|
+
jsonrpc: '2.0',
|
|
311
|
+
id,
|
|
312
|
+
error: { code: -32000, message: e.message }
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
jsonrpc: '2.0',
|
|
319
|
+
id,
|
|
320
|
+
error: { code: -32601, message: `Nieobsługiwana metoda: ${method}` }
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function runSupplierMcpServer() {
|
|
325
|
+
return new Promise((resolve) => {
|
|
326
|
+
const rl = readline.createInterface({
|
|
327
|
+
input: process.stdin,
|
|
328
|
+
output: process.stdout,
|
|
329
|
+
terminal: false
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
rl.on('line', async (line) => {
|
|
333
|
+
const trimmed = line.trim();
|
|
334
|
+
if (!trimmed) return;
|
|
335
|
+
try {
|
|
336
|
+
const msg = JSON.parse(trimmed);
|
|
337
|
+
const resp = await handleMcpMessage(msg);
|
|
338
|
+
if (resp) {
|
|
339
|
+
process.stdout.write(JSON.stringify(resp) + '\n');
|
|
340
|
+
}
|
|
341
|
+
} catch (e) {
|
|
342
|
+
process.stdout.write(JSON.stringify({
|
|
343
|
+
jsonrpc: '2.0',
|
|
344
|
+
id: null,
|
|
345
|
+
error: { code: -32700, message: `Parse error: ${e.message}` }
|
|
346
|
+
}) + '\n');
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
rl.on('close', () => resolve());
|
|
351
|
+
process.on('SIGINT', () => { rl.close(); resolve(); });
|
|
352
|
+
process.on('SIGTERM', () => { rl.close(); resolve(); });
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
module.exports = {
|
|
357
|
+
SUPPLIER_TOOLS,
|
|
358
|
+
handleMcpMessage,
|
|
359
|
+
runSupplierMcpServer
|
|
360
|
+
};
|
package/src/tui.js
CHANGED
|
@@ -66,67 +66,52 @@ ${c.bold}${c.green} ____ _____ _
|
|
|
66
66
|
| | | |/ _ \\/ _ \\ '__| | |/ _ \\ / _ \\ | / __|
|
|
67
67
|
| |_| | __/ __/ | | | (_) | (_) || \\__ \\
|
|
68
68
|
|____/ \\___|\\___|_| |_|\\___/ \\___(_)_|___/${c.reset}
|
|
69
|
-
${c.dim}
|
|
69
|
+
${c.dim}Identity, MCP & Skills Hub Manager — Deer Design${c.reset}
|
|
70
70
|
\n`);
|
|
71
71
|
|
|
72
|
-
const choice = await selectMenu('
|
|
73
|
-
{ label: '1.
|
|
74
|
-
{ label: '2.
|
|
75
|
-
{ label: '3.
|
|
76
|
-
{ label: '4.
|
|
77
|
-
{ label: '5.
|
|
78
|
-
{ label: '6.
|
|
79
|
-
{ label: '7.
|
|
80
|
-
{ label: '8.
|
|
81
|
-
{ label: '9. Historia wykonanych operacji (Audit log)', value: 'audit' },
|
|
72
|
+
const choice = await selectMenu('Wybierz operację administracyjną:', [
|
|
73
|
+
{ label: '1. Status logowania i sesji (Whoami)', value: 'whoami' },
|
|
74
|
+
{ label: '2. Zaloguj do systemów Deer Design (OAuth PKCE)', value: 'login' },
|
|
75
|
+
{ label: '3. Zainstaluj serwery MCP dla Cursor / Antigravity', value: 'mcp_install' },
|
|
76
|
+
{ label: '4. Synchronizuj skille z DeerSkills Hub (Skill Sync)', value: 'skill_sync' },
|
|
77
|
+
{ label: '5. Przeglądaj skille w DeerSkills Hub (Skill List)', value: 'skill_list' },
|
|
78
|
+
{ label: '6. Platformy agentów AI na komputerze (Agent List)', value: 'agent_list' },
|
|
79
|
+
{ label: '7. Diagnostyka środowiska i połączeń (Doctor)', value: 'doctor' },
|
|
80
|
+
{ label: '8. Historia operacji (Audit log)', value: 'audit' },
|
|
82
81
|
]);
|
|
83
82
|
|
|
84
|
-
if (choice === '
|
|
85
|
-
|
|
86
|
-
rl.question('\nPodaj metraż mieszkania w m² (np. 64): ', (ans) => {
|
|
87
|
-
rl.close();
|
|
88
|
-
const area = Number(ans) || 50;
|
|
89
|
-
dispatch(['estimate', 'calculate', String(area)], {});
|
|
90
|
-
});
|
|
83
|
+
if (choice === 'whoami') {
|
|
84
|
+
await dispatch(['whoami'], {});
|
|
91
85
|
return;
|
|
92
86
|
}
|
|
93
87
|
|
|
94
|
-
if (choice === '
|
|
95
|
-
|
|
96
|
-
rl.question('\nSzukana fraza (np. bateria, płytka): ', (ans) => {
|
|
97
|
-
rl.close();
|
|
98
|
-
dispatch(['catalog', 'search', ans || 'bateria'], {});
|
|
99
|
-
});
|
|
88
|
+
if (choice === 'login') {
|
|
89
|
+
await dispatch(['login', 'all'], {});
|
|
100
90
|
return;
|
|
101
91
|
}
|
|
102
92
|
|
|
103
|
-
if (choice === '
|
|
104
|
-
await dispatch(['
|
|
93
|
+
if (choice === 'mcp_install') {
|
|
94
|
+
await dispatch(['mcp', 'install'], {});
|
|
105
95
|
return;
|
|
106
96
|
}
|
|
107
97
|
|
|
108
|
-
if (choice === '
|
|
109
|
-
await dispatch(['
|
|
98
|
+
if (choice === 'skill_sync') {
|
|
99
|
+
await dispatch(['skill', 'sync'], {});
|
|
110
100
|
return;
|
|
111
101
|
}
|
|
112
102
|
|
|
113
|
-
if (choice === '
|
|
114
|
-
await dispatch(['
|
|
103
|
+
if (choice === 'skill_list') {
|
|
104
|
+
await dispatch(['skill', 'list'], {});
|
|
115
105
|
return;
|
|
116
106
|
}
|
|
117
107
|
|
|
118
|
-
if (choice === '
|
|
119
|
-
await dispatch(['
|
|
108
|
+
if (choice === 'agent_list') {
|
|
109
|
+
await dispatch(['agent', 'list'], {});
|
|
120
110
|
return;
|
|
121
111
|
}
|
|
122
112
|
|
|
123
|
-
if (choice === '
|
|
124
|
-
await dispatch(['
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (choice === 'mcp_install') {
|
|
129
|
-
await dispatch(['mcp', 'install'], {});
|
|
113
|
+
if (choice === 'doctor') {
|
|
114
|
+
await dispatch(['doctor'], {});
|
|
130
115
|
return;
|
|
131
116
|
}
|
|
132
117
|
|