@deerdesign/deertools 1.2.1 → 1.2.3
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/commands.js +96 -1
- package/src/skill-commands.js +5 -2
- package/src/skills-login.js +248 -0
package/package.json
CHANGED
package/src/commands.js
CHANGED
|
@@ -41,10 +41,45 @@ async function cmdLogin(pos, flags) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
if (pos[0] === 'skills' || pos[0] === 'hub' || pos[0] === 'skill') {
|
|
45
|
+
out(`\n=== DeerSkills Hub ===`);
|
|
46
|
+
try {
|
|
47
|
+
const { loginSkillsBrowser, loginSkillsDirect } = require('./skills-login');
|
|
48
|
+
const token = pos[1] || flags.token;
|
|
49
|
+
let entry;
|
|
50
|
+
if (token) {
|
|
51
|
+
entry = await loginSkillsDirect(token);
|
|
52
|
+
} else {
|
|
53
|
+
entry = await loginSkillsBrowser({
|
|
54
|
+
port: flags.port ? Number(flags.port) : 0,
|
|
55
|
+
openBrowser: flags['no-open'] ? null : undefined,
|
|
56
|
+
log: out
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
out(`✓ Zalogowano do DeerSkills Hub. Klucz zapisany w credentials.json.`);
|
|
60
|
+
out(` Przegląd umiejętności: deertools skill list | Katalog: https://skill-garden-vault.lovable.app`);
|
|
61
|
+
return;
|
|
62
|
+
} catch (e) {
|
|
63
|
+
err(`✗ DeerSkills: ${e.message}`);
|
|
64
|
+
fail('Nie udało się zalogować do DeerSkills Hub.', 1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
44
68
|
const products = resolveProducts(pos[0]);
|
|
45
69
|
const scope = flags.write ? 'read write' : normalizeScope(flags.scope || 'read');
|
|
46
70
|
let failures = 0;
|
|
47
71
|
for (const product of products) {
|
|
72
|
+
if (product.id === 'skills') {
|
|
73
|
+
try {
|
|
74
|
+
const { loginSkillsBrowser } = require('./skills-login');
|
|
75
|
+
await loginSkillsBrowser({ port: flags.port ? Number(flags.port) : 0, openBrowser: flags['no-open'] ? null : undefined, log: out });
|
|
76
|
+
out(`✓ Zalogowano do DeerSkills Hub.`);
|
|
77
|
+
} catch (e) {
|
|
78
|
+
failures += 1;
|
|
79
|
+
err(`✗ DeerSkills: ${e.message}`);
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
48
83
|
out(`\n=== ${product.name} ===`);
|
|
49
84
|
try {
|
|
50
85
|
const entry = await login(product, { scope, port: flags.port ? Number(flags.port) : 0, clientName: flags.name, openBrowser: flags['no-open'] ? null : undefined, log: out });
|
|
@@ -58,6 +93,7 @@ async function cmdLogin(pos, flags) {
|
|
|
58
93
|
if (failures) fail(`Nie udało się zalogować do ${failures} z ${products.length} produktów.`, 1);
|
|
59
94
|
}
|
|
60
95
|
|
|
96
|
+
|
|
61
97
|
async function cmdLogout(pos) {
|
|
62
98
|
if (pos[0] === 'baselinker' || pos[0] === 'bl') {
|
|
63
99
|
out(credentials.removeProduct('baselinker') ? `✓ Wylogowano z BaseLinker (token usunięty lokalnie).` : `– BaseLinker: nie byłeś zalogowany.`);
|
|
@@ -296,8 +332,66 @@ async function cmdEstimate(pos, flags) {
|
|
|
296
332
|
const { detectAgents, installSkillContent } = require('./agent-detector');
|
|
297
333
|
const skillCommands = require('./skill-commands');
|
|
298
334
|
|
|
335
|
+
const BUNDLES = {
|
|
336
|
+
architekci: {
|
|
337
|
+
title: 'Architekt Wnętrz',
|
|
338
|
+
skills: ['rendermind', 'deer-catalog', 'deerdesign-spec-docx', 'docx', 'pdf'],
|
|
339
|
+
},
|
|
340
|
+
kosztorysy: {
|
|
341
|
+
title: 'Kosztorysant / Obmiarowiec',
|
|
342
|
+
skills: ['deer-kosztorys', 'deer-diag-robot', 'pdf', 'xlsx'],
|
|
343
|
+
},
|
|
344
|
+
logistyka: {
|
|
345
|
+
title: 'Logistyk / Magazyn',
|
|
346
|
+
skills: ['deertools-dostawy', 'deer-baselinker', 'deertools-supplier-hub'],
|
|
347
|
+
},
|
|
348
|
+
sprzedaz: {
|
|
349
|
+
title: 'Doradca Klienta / Sprzedaż',
|
|
350
|
+
skills: ['deer-doradca-docs', 'deer-presentation', 'deer-design-brand', 'pptx'],
|
|
351
|
+
},
|
|
352
|
+
it: {
|
|
353
|
+
title: 'Developer / Zarząd IT',
|
|
354
|
+
skills: ['deer-technical-documentation', 'deer-mobile-build', 'jira-zgloszenie-zamkniecie', 'roadmap-idea'],
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
async function cmdSkillBundle(bundleName, flags) {
|
|
359
|
+
if (!bundleName) {
|
|
360
|
+
out('\n=== Dostępne pakiety stanowiskowe (Starter Bundles) ===');
|
|
361
|
+
for (const [key, b] of Object.entries(BUNDLES)) {
|
|
362
|
+
out(`\n ▶ deertools skill bundle ${key}`);
|
|
363
|
+
out(` Dział: ${b.title}`);
|
|
364
|
+
out(` Skille: ${b.skills.join(', ')}`);
|
|
365
|
+
}
|
|
366
|
+
out('\nUżycie: deertools skill bundle <nazwa_pakietu>');
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const key = String(bundleName).toLowerCase().trim();
|
|
370
|
+
const bundle = BUNDLES[key];
|
|
371
|
+
if (!bundle) {
|
|
372
|
+
fail(`Nieznany pakiet „${bundleName}”. Dostępne: ${Object.keys(BUNDLES).join(', ')}.`, 2);
|
|
373
|
+
}
|
|
374
|
+
out(`\n=== Instalacja pakietu stanowiskowego: ${bundle.title} ===`);
|
|
375
|
+
out(`Skille do zainstalowania: ${bundle.skills.join(', ')}\n`);
|
|
376
|
+
for (const s of bundle.skills) {
|
|
377
|
+
try {
|
|
378
|
+
out(`Instaluję ${s}...`);
|
|
379
|
+
await skillCommands.cmdSkillInstall(s, flags, out, err);
|
|
380
|
+
} catch (e) {
|
|
381
|
+
err(`✗ Błąd instalacji ${s}: ${e.message}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
out(`\n✓ Pakiet „${bundle.title}” został pomyślnie skonfigurowany.`);
|
|
385
|
+
}
|
|
386
|
+
|
|
299
387
|
async function cmdSkill(pos, flags) {
|
|
300
388
|
const sub = pos[0] || 'install';
|
|
389
|
+
if (sub === 'login') {
|
|
390
|
+
return cmdLogin(['skills', pos[1]], flags);
|
|
391
|
+
}
|
|
392
|
+
if (sub === 'bundle') {
|
|
393
|
+
return cmdSkillBundle(pos[1], flags);
|
|
394
|
+
}
|
|
301
395
|
if (sub === 'list') {
|
|
302
396
|
return skillCommands.cmdSkillList(flags, out, err);
|
|
303
397
|
}
|
|
@@ -316,9 +410,10 @@ async function cmdSkill(pos, flags) {
|
|
|
316
410
|
if (sub === 'install') {
|
|
317
411
|
return skillCommands.cmdSkillInstall(pos[1], flags, out, err);
|
|
318
412
|
}
|
|
319
|
-
fail('Użycie:\n deertools skill list [--tag <tag>]\n deertools skill search <fraza>\n deertools skill install <slug[@version]>\n deertools skill push [katalog] [--version <x.y.z>]\n deertools skill update\n deertools skill promote <slug>', 2);
|
|
413
|
+
fail('Użycie:\n deertools skill login [token]\n deertools skill bundle <architekci|kosztorysy|logistyka|sprzedaz|it>\n deertools skill list [--tag <tag>]\n deertools skill search <fraza>\n deertools skill install <slug[@version]>\n deertools skill push [katalog] [--version <x.y.z>]\n deertools skill update\n deertools skill promote <slug>', 2);
|
|
320
414
|
}
|
|
321
415
|
|
|
416
|
+
|
|
322
417
|
async function cmdMcp(pos, flags) {
|
|
323
418
|
const sub = pos[0] || 'config';
|
|
324
419
|
const products = resolveProducts(pos[1]);
|
package/src/skill-commands.js
CHANGED
|
@@ -39,7 +39,7 @@ async function cmdSkillList(flags, out, err) {
|
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
const items = res.data?.skills || [];
|
|
42
|
+
const items = Array.isArray(res.data) ? res.data : (res.data?.skills || []);
|
|
43
43
|
if (flags.json) {
|
|
44
44
|
out(JSON.stringify(items, null, 2));
|
|
45
45
|
return;
|
|
@@ -97,7 +97,10 @@ async function cmdSkillInstall(target, flags, out, err) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
const skill = res.data.skill;
|
|
100
|
-
const versionData = res.data.version;
|
|
100
|
+
const versionData = res.data.selected_version || res.data.version || (res.data.versions && res.data.versions[0]);
|
|
101
|
+
if (!versionData) {
|
|
102
|
+
throw new Error(`Brak opublikowanej wersji dla skilla „${slug}”.`);
|
|
103
|
+
}
|
|
101
104
|
const agents = detectAgents({ dir: flags.dir, agent: flags.agent });
|
|
102
105
|
|
|
103
106
|
if (agents.length === 0) {
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const http = require('http');
|
|
3
|
+
const { exec } = require('child_process');
|
|
4
|
+
const { saveProduct } = require('./credentials');
|
|
5
|
+
|
|
6
|
+
const DEFAULT_API_BASE = 'https://ezspmlsnmxuhtzvwrnoj.supabase.co/functions/v1/skills-api';
|
|
7
|
+
const PORTAL_SETTINGS_URL = 'https://skill-garden-vault.lovable.app/settings';
|
|
8
|
+
|
|
9
|
+
function openBrowser(url) {
|
|
10
|
+
const cmd = process.platform === 'win32' ? `start "" "${url}"` : process.platform === 'darwin' ? `open "${url}"` : `xdg-open "${url}"`;
|
|
11
|
+
exec(cmd, () => {});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function verifySkillsToken(token, apiBase = DEFAULT_API_BASE) {
|
|
15
|
+
const url = `${apiBase.replace(/\/+$/, '')}/skills?limit=1`;
|
|
16
|
+
try {
|
|
17
|
+
const res = await fetch(url, {
|
|
18
|
+
headers: {
|
|
19
|
+
'x-api-key': token,
|
|
20
|
+
'Authorization': `Bearer ${token}`,
|
|
21
|
+
'User-Agent': 'deertools-cli',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
if (!res.ok && res.status !== 200) {
|
|
25
|
+
return { ok: false, error: `Serwer zwrócił status ${res.status}. Upewnij się, że klucz jest poprawny i aktywny.` };
|
|
26
|
+
}
|
|
27
|
+
const json = await res.json().catch(() => null);
|
|
28
|
+
return { ok: true, data: json };
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return { ok: false, error: `Nie udało się połączyć z serwerem DeerSkills Hub: ${e.message}` };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function renderSkillsLoginPage(error = null, success = false, email = null) {
|
|
35
|
+
return `<!DOCTYPE html>
|
|
36
|
+
<html lang="pl">
|
|
37
|
+
<head>
|
|
38
|
+
<meta charset="UTF-8">
|
|
39
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
40
|
+
<title>DeerTools · Logowanie do DeerSkills Hub</title>
|
|
41
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
42
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
43
|
+
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
44
|
+
<style>
|
|
45
|
+
:root {
|
|
46
|
+
--bg: #09090b;
|
|
47
|
+
--card-bg: rgba(24, 24, 27, 0.95);
|
|
48
|
+
--card-border: rgba(255, 255, 255, 0.1);
|
|
49
|
+
--primary: #C5A880;
|
|
50
|
+
--primary-hover: #d4ba94;
|
|
51
|
+
--text: #f4f4f5;
|
|
52
|
+
--text-muted: #a1a1aa;
|
|
53
|
+
--success: #10b981;
|
|
54
|
+
--danger: #ef4444;
|
|
55
|
+
}
|
|
56
|
+
* { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Plus Jakarta Sans', -apple-system, sans-serif; }
|
|
57
|
+
body {
|
|
58
|
+
background: var(--bg); color: var(--text); min-height: 100vh;
|
|
59
|
+
display: flex; align-items: center; justify-content: center; padding: 24px 16px;
|
|
60
|
+
}
|
|
61
|
+
.card {
|
|
62
|
+
background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 20px;
|
|
63
|
+
padding: 36px; max-width: 480px; width: 100%; box-shadow: 0 24px 60px rgba(0,0,0,0.6);
|
|
64
|
+
backdrop-filter: blur(16px); text-align: left;
|
|
65
|
+
}
|
|
66
|
+
.logo-badge { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
|
|
67
|
+
.badge {
|
|
68
|
+
background: rgba(197, 168, 128, 0.15); color: var(--primary);
|
|
69
|
+
padding: 4px 10px; border-radius: 999px; font-size: 12px; font-weight: 600;
|
|
70
|
+
border: 1px solid rgba(197, 168, 128, 0.3);
|
|
71
|
+
}
|
|
72
|
+
h1 { font-size: 22px; font-weight: 700; color: #fff; margin-bottom: 8px; letter-spacing: -0.5px; }
|
|
73
|
+
p { font-size: 14px; color: var(--text-muted); line-height: 1.5; margin-bottom: 20px; }
|
|
74
|
+
.info-box {
|
|
75
|
+
background: rgba(9, 9, 11, 0.7); border: 1px solid var(--card-border);
|
|
76
|
+
border-radius: 12px; padding: 14px 16px; font-size: 13px; color: var(--text-muted); margin-bottom: 20px;
|
|
77
|
+
line-height: 1.6;
|
|
78
|
+
}
|
|
79
|
+
.info-box a { color: var(--primary); text-decoration: none; font-weight: 600; }
|
|
80
|
+
.info-box a:hover { text-decoration: underline; }
|
|
81
|
+
.alert {
|
|
82
|
+
padding: 12px 14px; border-radius: 10px; font-size: 13px; margin-bottom: 20px; font-weight: 500;
|
|
83
|
+
}
|
|
84
|
+
.alert-danger { background: rgba(239, 68, 68, 0.15); border: 1px solid rgba(239, 68, 68, 0.3); color: #fca5a5; }
|
|
85
|
+
.alert-success { background: rgba(16, 185, 129, 0.15); border: 1px solid rgba(16, 185, 129, 0.3); color: #6ee7b7; text-align: center; }
|
|
86
|
+
.form-group { margin-bottom: 20px; }
|
|
87
|
+
label { display: block; font-size: 13px; font-weight: 600; color: var(--text); margin-bottom: 8px; }
|
|
88
|
+
input[type="password"], input[type="text"] {
|
|
89
|
+
width: 100%; background: rgba(9, 9, 11, 0.9); border: 1px solid var(--card-border);
|
|
90
|
+
border-radius: 10px; padding: 12px 16px; color: #fff; font-size: 14px; outline: none;
|
|
91
|
+
transition: all 0.2s; font-family: monospace;
|
|
92
|
+
}
|
|
93
|
+
input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(197, 168, 128, 0.2); }
|
|
94
|
+
button {
|
|
95
|
+
width: 100%; padding: 13px 20px; border-radius: 10px; font-size: 14px; font-weight: 700;
|
|
96
|
+
cursor: pointer; border: none; background: var(--primary); color: #000; transition: all 0.2s;
|
|
97
|
+
}
|
|
98
|
+
button:hover { background: var(--primary-hover); transform: translateY(-1px); }
|
|
99
|
+
</style>
|
|
100
|
+
</head>
|
|
101
|
+
<body>
|
|
102
|
+
<div class="card">
|
|
103
|
+
<div class="logo-badge">
|
|
104
|
+
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#C5A880" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5z"></path><path d="M2 17l10 5 10-5"></path><path d="M2 12l10 5 10-5"></path></svg>
|
|
105
|
+
<span class="badge">DeerSkills Hub Auth</span>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
${success ? `
|
|
109
|
+
<div class="alert alert-success">
|
|
110
|
+
<h2 style="font-size: 18px; margin-bottom: 6px; color: #fff;">✓ Zalogowano pomyślnie!</h2>
|
|
111
|
+
<p style="margin-bottom: 8px; color: inherit;">Dostęp do katalogu i repozytorium skills aktywny.</p>
|
|
112
|
+
<p style="font-size: 12px; margin-bottom: 0;">Klucz zapisany w <code>~/.deertools/credentials.json</code>.<br>Możesz zamknąć tę kartę i wrócić do terminala.</p>
|
|
113
|
+
</div>
|
|
114
|
+
` : `
|
|
115
|
+
<h1>Logowanie do DeerSkills Hub</h1>
|
|
116
|
+
<p>Wklej swój klucz personalny API, aby połączyć terminal i asystentów AI z centralnym katalogiem umiejętności Deer Design.</p>
|
|
117
|
+
|
|
118
|
+
<div class="info-box">
|
|
119
|
+
💡 <b>Gdzie pobrać klucz?</b><br>
|
|
120
|
+
Otwórz portal <a href="${PORTAL_SETTINGS_URL}" target="_blank">DeerSkills Hub → Ustawienia → Klucze API</a> i kliknij <b>Kopiuj</b> przy swoim kluczu.
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
${error ? `<div class="alert alert-danger">${error}</div>` : ''}
|
|
124
|
+
|
|
125
|
+
<form method="POST" action="/submit">
|
|
126
|
+
<div class="form-group">
|
|
127
|
+
<label for="token">Klucz API DeerSkills</label>
|
|
128
|
+
<input type="password" id="token" name="token" placeholder="Wklej klucz API (np. deer_skills_live_...)" autofocus required>
|
|
129
|
+
</div>
|
|
130
|
+
<button type="submit">Zaloguj i zapisz w CLI</button>
|
|
131
|
+
</form>
|
|
132
|
+
`}
|
|
133
|
+
</div>
|
|
134
|
+
</body>
|
|
135
|
+
</html>`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function loginSkillsBrowser(opts = {}) {
|
|
139
|
+
const timeoutMs = opts.timeoutMs || 5 * 60 * 1000;
|
|
140
|
+
const open = opts.openBrowser === undefined ? openBrowser : opts.openBrowser;
|
|
141
|
+
const log = opts.log || console.log;
|
|
142
|
+
|
|
143
|
+
return new Promise((resolve, reject) => {
|
|
144
|
+
let finished = false;
|
|
145
|
+
|
|
146
|
+
const server = http.createServer(async (req, res) => {
|
|
147
|
+
const parsedUrl = new URL(req.url, `http://${req.headers.host || '127.0.0.1'}`);
|
|
148
|
+
|
|
149
|
+
if (req.method === 'GET') {
|
|
150
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
151
|
+
res.end(renderSkillsLoginPage());
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (req.method === 'POST' && parsedUrl.pathname === '/submit') {
|
|
156
|
+
let body = '';
|
|
157
|
+
req.on('data', chunk => body += chunk);
|
|
158
|
+
req.on('end', async () => {
|
|
159
|
+
try {
|
|
160
|
+
const formData = new URLSearchParams(body);
|
|
161
|
+
const token = formData.get('token')?.trim();
|
|
162
|
+
if (!token) {
|
|
163
|
+
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
164
|
+
res.end(renderSkillsLoginPage('Proszę podać klucz API.'));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let test;
|
|
169
|
+
if (opts.verifyToken) {
|
|
170
|
+
test = await opts.verifyToken(token);
|
|
171
|
+
} else {
|
|
172
|
+
test = await verifySkillsToken(token);
|
|
173
|
+
}
|
|
174
|
+
if (!test.ok) {
|
|
175
|
+
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
176
|
+
res.end(renderSkillsLoginPage(test.error));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
const entry = {
|
|
182
|
+
key: token,
|
|
183
|
+
scope: 'read write',
|
|
184
|
+
logged_at: new Date().toISOString(),
|
|
185
|
+
source: 'file',
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
saveProduct('skills', entry);
|
|
189
|
+
|
|
190
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
191
|
+
res.end(renderSkillsLoginPage(null, true));
|
|
192
|
+
|
|
193
|
+
setTimeout(() => done(null, entry), 500);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
196
|
+
res.end(renderSkillsLoginPage(`Wystąpił błąd: ${err.message}`));
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
res.writeHead(404);
|
|
203
|
+
res.end('Not Found');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const timer = setTimeout(() => {
|
|
207
|
+
done(new Error('Upłynął czas oczekiwania na logowanie do DeerSkills (5 min).'));
|
|
208
|
+
}, timeoutMs);
|
|
209
|
+
|
|
210
|
+
function done(err, entry) {
|
|
211
|
+
if (finished) return;
|
|
212
|
+
finished = true;
|
|
213
|
+
clearTimeout(timer);
|
|
214
|
+
server.close();
|
|
215
|
+
if (err) reject(err); else resolve(entry);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
server.on('error', err => done(err));
|
|
219
|
+
server.listen(opts.port || 0, '127.0.0.1', () => {
|
|
220
|
+
const port = server.address().port;
|
|
221
|
+
const loginUrl = `http://127.0.0.1:${port}/`;
|
|
222
|
+
log(`Otwieram przeglądarkę pod adresem:\n${loginUrl}\nAby się zalogować, wklej swój klucz API z portalu DeerSkills Hub.`);
|
|
223
|
+
if (open) open(loginUrl);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function loginSkillsDirect(token) {
|
|
229
|
+
const test = await verifySkillsToken(token);
|
|
230
|
+
if (!test.ok) {
|
|
231
|
+
throw new Error(test.error);
|
|
232
|
+
}
|
|
233
|
+
const entry = {
|
|
234
|
+
key: token,
|
|
235
|
+
scope: 'read write',
|
|
236
|
+
logged_at: new Date().toISOString(),
|
|
237
|
+
source: 'file',
|
|
238
|
+
};
|
|
239
|
+
saveProduct('skills', entry);
|
|
240
|
+
return entry;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
module.exports = {
|
|
244
|
+
verifySkillsToken,
|
|
245
|
+
loginSkillsBrowser,
|
|
246
|
+
loginSkillsDirect,
|
|
247
|
+
PORTAL_SETTINGS_URL,
|
|
248
|
+
};
|