nothumanallowed 13.5.55 → 13.5.56
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/ui.mjs +55 -2
- package/src/constants.mjs +1 -1
- package/src/services/web-ui.mjs +167 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.5.
|
|
3
|
+
"version": "13.5.56",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/commands/ui.mjs
CHANGED
|
@@ -3966,6 +3966,46 @@ ${completedHeadings ? `## SECTIONS ALREADY WRITTEN (headings only):\n${completed
|
|
|
3966
3966
|
return;
|
|
3967
3967
|
}
|
|
3968
3968
|
|
|
3969
|
+
// GET /api/studio/webcraft/skills/:name → { skills: [{name, content}] }
|
|
3970
|
+
if (pathname.startsWith('/api/studio/webcraft/skills/') && method === 'GET') {
|
|
3971
|
+
const projName = decodeURIComponent(pathname.replace('/api/studio/webcraft/skills/', '')).replace(/[^a-zA-Z0-9_-]/g, '');
|
|
3972
|
+
const skillsDir = path.join(os.homedir(), '.nha', 'webcraft', projName, 'skills');
|
|
3973
|
+
const skills = [];
|
|
3974
|
+
if (fs.existsSync(skillsDir)) {
|
|
3975
|
+
for (const fname of fs.readdirSync(skillsDir)) {
|
|
3976
|
+
if (!fname.endsWith('.md')) continue;
|
|
3977
|
+
try {
|
|
3978
|
+
const content = fs.readFileSync(path.join(skillsDir, fname), 'utf8');
|
|
3979
|
+
skills.push({ name: fname.replace(/\.md$/, ''), content });
|
|
3980
|
+
} catch(_) {}
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
sendJSON(res, 200, { skills });
|
|
3984
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
3985
|
+
return;
|
|
3986
|
+
}
|
|
3987
|
+
|
|
3988
|
+
// POST /api/studio/webcraft/skills/:name { skills: [{name, content}] }
|
|
3989
|
+
if (pathname.startsWith('/api/studio/webcraft/skills/') && method === 'POST') {
|
|
3990
|
+
const projName = decodeURIComponent(pathname.replace('/api/studio/webcraft/skills/', '')).replace(/[^a-zA-Z0-9_-]/g, '');
|
|
3991
|
+
const body = await parseBody(req);
|
|
3992
|
+
const skills = body.skills || [];
|
|
3993
|
+
const skillsDir = path.join(os.homedir(), '.nha', 'webcraft', projName, 'skills');
|
|
3994
|
+
if (!fs.existsSync(skillsDir)) fs.mkdirSync(skillsDir, { recursive: true });
|
|
3995
|
+
// Remove all existing .md files, then write current set
|
|
3996
|
+
for (const fname of fs.readdirSync(skillsDir)) {
|
|
3997
|
+
if (fname.endsWith('.md')) fs.unlinkSync(path.join(skillsDir, fname));
|
|
3998
|
+
}
|
|
3999
|
+
for (const skill of skills) {
|
|
4000
|
+
if (!skill.name) continue;
|
|
4001
|
+
const safeName = skill.name.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
4002
|
+
fs.writeFileSync(path.join(skillsDir, safeName + '.md'), skill.content || '', 'utf8');
|
|
4003
|
+
}
|
|
4004
|
+
sendJSON(res, 200, { ok: true });
|
|
4005
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
4006
|
+
return;
|
|
4007
|
+
}
|
|
4008
|
+
|
|
3969
4009
|
// GET /api/studio/webcraft/projects/load/:name → { projectName, description, files[] }
|
|
3970
4010
|
if (pathname.startsWith('/api/studio/webcraft/projects/load/') && method === 'GET') {
|
|
3971
4011
|
const projName = decodeURIComponent(pathname.replace('/api/studio/webcraft/projects/load/', '')).replace(/[^a-zA-Z0-9_-]/g, '');
|
|
@@ -4409,6 +4449,20 @@ module.exports = { get, set, del, exists };
|
|
|
4409
4449
|
const agentMemoryPath = path.join(sandboxDir, 'webcraft-agent.md');
|
|
4410
4450
|
const agentMemory = fs.existsSync(agentMemoryPath) ? fs.readFileSync(agentMemoryPath, 'utf8') : '';
|
|
4411
4451
|
|
|
4452
|
+
// Load skills files from skills/ subfolder
|
|
4453
|
+
const skillsDir = path.join(sandboxDir, 'skills');
|
|
4454
|
+
let skillsContext = '';
|
|
4455
|
+
if (fs.existsSync(skillsDir)) {
|
|
4456
|
+
const skillFiles = fs.readdirSync(skillsDir).filter(f => f.endsWith('.md'));
|
|
4457
|
+
if (skillFiles.length > 0) {
|
|
4458
|
+
skillsContext = '\nSKILLS DEL PROGETTO:\n' + skillFiles.map(fname => {
|
|
4459
|
+
const skillName = fname.replace(/\.md$/, '');
|
|
4460
|
+
const content = fs.readFileSync(path.join(skillsDir, fname), 'utf8');
|
|
4461
|
+
return `--- SKILL: ${skillName} ---\n${content}`;
|
|
4462
|
+
}).join('\n\n') + '\n';
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
|
|
4412
4466
|
// Read all project files to give agent full context
|
|
4413
4467
|
const allFiles = [];
|
|
4414
4468
|
if (fs.existsSync(sandboxDir)) {
|
|
@@ -4466,8 +4520,7 @@ Il tuo lavoro e di correggere, migliorare ed espandere il codice del progetto sa
|
|
|
4466
4520
|
|
|
4467
4521
|
PROGETTO ATTIVO: ${projectName}
|
|
4468
4522
|
PERCORSO: ${sandboxDir}
|
|
4469
|
-
${agentMemory ? '\nMEMORIA PROGETTO:\n' + agentMemory + '\n' : ''}
|
|
4470
|
-
FILE DISPONIBILI:
|
|
4523
|
+
${agentMemory ? '\nMEMORIA PROGETTO:\n' + agentMemory + '\n' : ''}${skillsContext}FILE DISPONIBILI:
|
|
4471
4524
|
${fileList}
|
|
4472
4525
|
|
|
4473
4526
|
STRUMENTI A TUA DISPOSIZIONE (rispondi SOLO con JSON valido per le operazioni):
|
package/src/constants.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
|
|
8
|
-
export const VERSION = '13.5.
|
|
8
|
+
export const VERSION = '13.5.56';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -6252,6 +6252,10 @@ var wcChatRunning = false;
|
|
|
6252
6252
|
var wcChatAttachments = []; // [{name, mimeType, base64, size}]
|
|
6253
6253
|
var _wcAutoFixAttempts = 0;
|
|
6254
6254
|
var _wcAutoFixTimer = null;
|
|
6255
|
+
// Skills state
|
|
6256
|
+
var wcSkills = []; // [{name, content}] loaded from disk
|
|
6257
|
+
var wcSkillModal = null; // null | {mode:'edit'|'new', idx:number|null, name, content, generating}
|
|
6258
|
+
var _wcSkillsLoaded = false;
|
|
6255
6259
|
|
|
6256
6260
|
function wcEsc(s){return s?String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'):''}
|
|
6257
6261
|
|
|
@@ -6336,6 +6340,7 @@ function renderWebCraft(el) {
|
|
|
6336
6340
|
'<div id="wcFieldsList">'+authFieldsHtml+'</div>' +
|
|
6337
6341
|
'<div style="font-size:9px;color:var(--dim);margin-top:4px">'+t('wc_required_hint')+'</div>' +
|
|
6338
6342
|
'</div>' +
|
|
6343
|
+
wcSkillsPanelHtml() +
|
|
6339
6344
|
(wcState.running ?
|
|
6340
6345
|
'<div style="width:100%;padding:11px;background:var(--bg3);border:1px solid var(--border);border-radius:8px;color:var(--dim);font-size:12px;text-align:center">⏳ '+t('wc_generating')+'...</div>'
|
|
6341
6346
|
: '') +
|
|
@@ -6361,7 +6366,8 @@ function renderWebCraft(el) {
|
|
|
6361
6366
|
(wcMainTab === 'projects' ? wcProjectsPanelHtml() : editorHtml) +
|
|
6362
6367
|
'</div>' +
|
|
6363
6368
|
wcChatPanelHtml() +
|
|
6364
|
-
'</div>'
|
|
6369
|
+
'</div>' +
|
|
6370
|
+
wcSkillModalHtml();
|
|
6365
6371
|
}
|
|
6366
6372
|
|
|
6367
6373
|
function wcPickExample(i) {
|
|
@@ -6383,6 +6389,159 @@ function wcTabFiles() { wcRightTab = 'files'; renderWebCraft(document.getElement
|
|
|
6383
6389
|
function wcTabPreview() { wcRightTab = 'preview'; renderWebCraft(document.getElementById('content')); }
|
|
6384
6390
|
function wcOpenSandbox() { if (wcState.sandbox.port) window.open('http://127.0.0.1:' + wcState.sandbox.port, '_blank'); }
|
|
6385
6391
|
|
|
6392
|
+
// ── WebCraft Skills ───────────────────────────────────────────────────────────
|
|
6393
|
+
function wcSkillsPanelHtml() {
|
|
6394
|
+
var hasProj = wcState.projectName && wcState.generatedFiles.length > 0;
|
|
6395
|
+
// Load skills from server on first render if project active
|
|
6396
|
+
if (hasProj && !_wcSkillsLoaded) {
|
|
6397
|
+
_wcSkillsLoaded = true;
|
|
6398
|
+
fetch(API + '/api/studio/webcraft/skills/' + encodeURIComponent(wcState.projectName))
|
|
6399
|
+
.then(function(r){ return r.json(); })
|
|
6400
|
+
.then(function(d){ wcSkills = d.skills || []; renderWebCraft(document.getElementById('content')); })
|
|
6401
|
+
.catch(function(){});
|
|
6402
|
+
}
|
|
6403
|
+
var rows = wcSkills.map(function(s, si) {
|
|
6404
|
+
return '<div style="display:flex;align-items:center;gap:6px;padding:5px 0;border-bottom:1px solid var(--border)">' +
|
|
6405
|
+
'<span style="font-size:11px;color:var(--text);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">📋 '+wcEsc(s.name)+'</span>' +
|
|
6406
|
+
'<button onclick="wcOpenSkill('+si+')" title="Modifica" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:12px;padding:2px 4px">✎</button>' +
|
|
6407
|
+
'<button onclick="wcDeleteSkill('+si+')" title="Elimina" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:12px;padding:2px 4px">×</button>' +
|
|
6408
|
+
'</div>';
|
|
6409
|
+
}).join('');
|
|
6410
|
+
return '<div style="background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:14px">' +
|
|
6411
|
+
'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">' +
|
|
6412
|
+
'<div style="font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.8px">📚 Skills & Memoria</div>' +
|
|
6413
|
+
'<button onclick="wcNewSkill()" style="font-size:10px;padding:3px 8px;background:var(--bg3);border:1px solid var(--border2);border-radius:5px;color:var(--green);cursor:pointer">+ Aggiungi</button>' +
|
|
6414
|
+
'</div>' +
|
|
6415
|
+
(wcSkills.length > 0
|
|
6416
|
+
? '<div style="max-height:120px;overflow-y:auto">' + rows + '</div>'
|
|
6417
|
+
: '<div style="font-size:10px;color:var(--dim);font-style:italic">Nessuna skill. Aggiungine una per dare istruzioni persistenti.</div>'
|
|
6418
|
+
) +
|
|
6419
|
+
'</div>';
|
|
6420
|
+
}
|
|
6421
|
+
|
|
6422
|
+
function wcSkillModalHtml() {
|
|
6423
|
+
if (!wcSkillModal) return '';
|
|
6424
|
+
var m = wcSkillModal;
|
|
6425
|
+
var isNew = m.mode === 'new';
|
|
6426
|
+
return '<div onclick="wcCloseSkillModal(event)" style="position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:9999;display:flex;align-items:center;justify-content:center">' +
|
|
6427
|
+
'<div onclick="event.stopPropagation()" style="background:var(--bg2);border:1px solid var(--border);border-radius:14px;width:560px;max-width:95vw;max-height:85vh;display:flex;flex-direction:column;overflow:hidden">' +
|
|
6428
|
+
'<div style="padding:16px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px">' +
|
|
6429
|
+
'<span style="font-size:14px;font-weight:700;color:var(--text);flex:1">'+(isNew?'📚 Nuova Skill':'📚 Modifica Skill')+'</span>' +
|
|
6430
|
+
'<button onclick="wcCloseSkillModal()" style="background:none;border:none;color:var(--dim);font-size:18px;cursor:pointer;line-height:1">×</button>' +
|
|
6431
|
+
'</div>' +
|
|
6432
|
+
'<div style="padding:16px 20px;display:flex;flex-direction:column;gap:10px;flex:1;overflow-y:auto">' +
|
|
6433
|
+
'<div>' +
|
|
6434
|
+
'<div style="font-size:10px;color:var(--dim);margin-bottom:4px">NOME FILE (es. stripe.md, email-templates.md)</div>' +
|
|
6435
|
+
'<input id="wcSkillName" value="'+wcEsc(m.name||'')+'" placeholder="nome-skill.md" style="width:100%;padding:7px 10px;font-size:12px;border-radius:6px;border:1px solid var(--border2);background:var(--bg3);color:var(--text);box-sizing:border-box;font-family:var(--mono)">' +
|
|
6436
|
+
'</div>' +
|
|
6437
|
+
'<div style="background:var(--bg3);border:1px solid var(--border);border-radius:8px;padding:10px">' +
|
|
6438
|
+
'<div style="font-size:10px;color:var(--dim);margin-bottom:6px">🤖 GENERA CON AI — descrivi cosa deve contenere la skill</div>' +
|
|
6439
|
+
'<div style="display:flex;gap:8px">' +
|
|
6440
|
+
'<textarea id="wcSkillAiDesc" rows="2" placeholder="es. Istruzioni per integrare Stripe Checkout con Express. Includi pattern per webhook, error handling e test mode." style="flex:1;padding:7px 10px;font-size:11px;border-radius:6px;border:1px solid var(--border2);background:var(--bg2);color:var(--text);resize:none;font-family:inherit"></textarea>' +
|
|
6441
|
+
'<button onclick="wcGenerateSkill()" '+(m.generating?'disabled':'')+' style="padding:8px 12px;background:var(--green3);border:none;border-radius:6px;color:var(--bg);font-size:11px;font-weight:700;cursor:pointer;white-space:nowrap;align-self:flex-end">'+(m.generating?'⏳':'▶ Genera')+'</button>' +
|
|
6442
|
+
'</div>' +
|
|
6443
|
+
'</div>' +
|
|
6444
|
+
'<div>' +
|
|
6445
|
+
'<div style="font-size:10px;color:var(--dim);margin-bottom:4px">CONTENUTO (markdown)</div>' +
|
|
6446
|
+
'<textarea id="wcSkillContent" rows="12" placeholder="# Nome Skill'+String.fromCharCode(10)+String.fromCharCode(10)+'Scrivi qui le istruzioni, pattern o snippet..." style="width:100%;padding:8px 10px;font-size:11px;border-radius:6px;border:1px solid var(--border2);background:var(--bg3);color:var(--text);resize:vertical;box-sizing:border-box;font-family:var(--mono);line-height:1.6">'+wcEsc(m.content||'')+'</textarea>' +
|
|
6447
|
+
'</div>' +
|
|
6448
|
+
'</div>' +
|
|
6449
|
+
'<div style="padding:12px 20px;border-top:1px solid var(--border);display:flex;justify-content:flex-end;gap:8px">' +
|
|
6450
|
+
'<button onclick="wcCloseSkillModal()" style="padding:8px 16px;background:var(--bg3);border:1px solid var(--border2);border-radius:7px;color:var(--dim);font-size:12px;cursor:pointer">Annulla</button>' +
|
|
6451
|
+
'<button onclick="wcSaveSkill()" style="padding:8px 18px;background:var(--green3);border:none;border-radius:7px;color:var(--bg);font-size:12px;font-weight:700;cursor:pointer">✓ Salva</button>' +
|
|
6452
|
+
'</div>' +
|
|
6453
|
+
'</div>' +
|
|
6454
|
+
'</div>';
|
|
6455
|
+
}
|
|
6456
|
+
|
|
6457
|
+
function wcNewSkill() {
|
|
6458
|
+
wcSkillModal = { mode: 'new', idx: null, name: '', content: '', generating: false };
|
|
6459
|
+
renderWebCraft(document.getElementById('content'));
|
|
6460
|
+
}
|
|
6461
|
+
|
|
6462
|
+
function wcOpenSkill(si) {
|
|
6463
|
+
var s = wcSkills[si];
|
|
6464
|
+
if (!s) return;
|
|
6465
|
+
wcSkillModal = { mode: 'edit', idx: si, name: s.name, content: s.content, generating: false };
|
|
6466
|
+
renderWebCraft(document.getElementById('content'));
|
|
6467
|
+
}
|
|
6468
|
+
|
|
6469
|
+
function wcCloseSkillModal(e) {
|
|
6470
|
+
if (e && e.target !== e.currentTarget) return;
|
|
6471
|
+
wcSkillModal = null;
|
|
6472
|
+
renderWebCraft(document.getElementById('content'));
|
|
6473
|
+
}
|
|
6474
|
+
|
|
6475
|
+
async function wcGenerateSkill() {
|
|
6476
|
+
var descEl = document.getElementById('wcSkillAiDesc');
|
|
6477
|
+
var nameEl = document.getElementById('wcSkillName');
|
|
6478
|
+
var desc = (descEl ? descEl.value : '').trim();
|
|
6479
|
+
if (!desc) { alert('Descrivi prima cosa deve contenere la skill.'); return; }
|
|
6480
|
+
wcSkillModal.generating = true;
|
|
6481
|
+
wcSkillModal.name = nameEl ? nameEl.value : wcSkillModal.name;
|
|
6482
|
+
renderWebCraft(document.getElementById('content'));
|
|
6483
|
+
try {
|
|
6484
|
+
var r = await fetch(API + '/api/studio/webcraft', {
|
|
6485
|
+
method: 'POST',
|
|
6486
|
+
headers: {'Content-Type':'application/json'},
|
|
6487
|
+
body: JSON.stringify({
|
|
6488
|
+
system: 'Sei un esperto di sviluppo web fullstack. Genera un file Markdown di "skill" per il WebCraft Agent di NotHumanAllowed. Il file deve contenere istruzioni, pattern di codice, best practice e snippet pronti per essere usati dal modello AI come contesto persistente. Scrivi SOLO il contenuto Markdown, senza spiegazioni esterne.',
|
|
6489
|
+
user: 'Progetto: ' + wcState.projectName + String.fromCharCode(10) + 'Stack: Express.js, PostgreSQL, JWT auth, BEM CSS' + String.fromCharCode(10) + String.fromCharCode(10) + 'Genera la skill: ' + desc,
|
|
6490
|
+
max_tokens: 2048
|
|
6491
|
+
})
|
|
6492
|
+
});
|
|
6493
|
+
if (r.ok) {
|
|
6494
|
+
var d = await r.json();
|
|
6495
|
+
wcSkillModal.content = d.text || '';
|
|
6496
|
+
wcSkillModal.generating = false;
|
|
6497
|
+
// Auto-suggest name if empty
|
|
6498
|
+
if (!wcSkillModal.name && desc.length > 0) {
|
|
6499
|
+
wcSkillModal.name = desc.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 30) + '.md';
|
|
6500
|
+
}
|
|
6501
|
+
}
|
|
6502
|
+
} catch(e) {
|
|
6503
|
+
wcSkillModal.generating = false;
|
|
6504
|
+
}
|
|
6505
|
+
renderWebCraft(document.getElementById('content'));
|
|
6506
|
+
}
|
|
6507
|
+
|
|
6508
|
+
async function wcSaveSkill() {
|
|
6509
|
+
var nameEl = document.getElementById('wcSkillName');
|
|
6510
|
+
var contentEl = document.getElementById('wcSkillContent');
|
|
6511
|
+
var name = (nameEl ? nameEl.value : '').trim();
|
|
6512
|
+
var content = contentEl ? contentEl.value : '';
|
|
6513
|
+
if (!name) { alert('Inserisci un nome per la skill.'); return; }
|
|
6514
|
+
if (!name.endsWith('.md')) name = name + '.md';
|
|
6515
|
+
var skill = { name: name, content: content };
|
|
6516
|
+
if (wcSkillModal.mode === 'edit' && wcSkillModal.idx !== null) {
|
|
6517
|
+
wcSkills[wcSkillModal.idx] = skill;
|
|
6518
|
+
} else {
|
|
6519
|
+
wcSkills.push(skill);
|
|
6520
|
+
}
|
|
6521
|
+
wcSkillModal = null;
|
|
6522
|
+
// Persist to server
|
|
6523
|
+
await wcPersistSkills();
|
|
6524
|
+
renderWebCraft(document.getElementById('content'));
|
|
6525
|
+
}
|
|
6526
|
+
|
|
6527
|
+
async function wcDeleteSkill(si) {
|
|
6528
|
+
if (!confirm('Eliminare la skill "' + wcSkills[si].name + '"?')) return;
|
|
6529
|
+
wcSkills.splice(si, 1);
|
|
6530
|
+
await wcPersistSkills();
|
|
6531
|
+
renderWebCraft(document.getElementById('content'));
|
|
6532
|
+
}
|
|
6533
|
+
|
|
6534
|
+
async function wcPersistSkills() {
|
|
6535
|
+
if (!wcState.projectName) return;
|
|
6536
|
+
try {
|
|
6537
|
+
await fetch(API + '/api/studio/webcraft/skills/' + encodeURIComponent(wcState.projectName), {
|
|
6538
|
+
method: 'POST',
|
|
6539
|
+
headers: {'Content-Type':'application/json'},
|
|
6540
|
+
body: JSON.stringify({ skills: wcSkills })
|
|
6541
|
+
});
|
|
6542
|
+
} catch(_) {}
|
|
6543
|
+
}
|
|
6544
|
+
|
|
6386
6545
|
// ── WebCraft Agent Chat Panel ─────────────────────────────────────────────
|
|
6387
6546
|
function wcChatPanelHtml() {
|
|
6388
6547
|
var hasProject = wcState.projectName && wcState.generatedFiles.length > 0;
|
|
@@ -6739,6 +6898,13 @@ async function wcLoadProject(pi) {
|
|
|
6739
6898
|
var cr = await fetch(API + '/api/studio/webcraft/projects/chat/load/' + encodeURIComponent(wcState.projectName));
|
|
6740
6899
|
if (cr.ok) { var cd = await cr.json(); wcChat = cd.chat || []; }
|
|
6741
6900
|
} catch(_) { wcChat = []; }
|
|
6901
|
+
// Load skills for this project
|
|
6902
|
+
_wcSkillsLoaded = false;
|
|
6903
|
+
wcSkills = [];
|
|
6904
|
+
try {
|
|
6905
|
+
var sr = await fetch(API + '/api/studio/webcraft/skills/' + encodeURIComponent(wcState.projectName));
|
|
6906
|
+
if (sr.ok) { var sd = await sr.json(); wcSkills = sd.skills || []; _wcSkillsLoaded = true; }
|
|
6907
|
+
} catch(_) {}
|
|
6742
6908
|
renderWebCraft(document.getElementById('content'));
|
|
6743
6909
|
wcScrollChatToBottom();
|
|
6744
6910
|
}
|