fraim-framework 2.0.196 → 2.0.198
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/dist/src/ai-hub/brand-store.js +232 -0
- package/dist/src/ai-hub/server.js +91 -1
- package/dist/src/cli/doctor/checks/fraim-mcp-diagnostics.js +283 -0
- package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +224 -18
- package/dist/src/cli/utils/org-pack-sync.js +35 -5
- package/dist/src/first-run/types.js +1 -1
- package/dist/src/local-mcp-server/learning-context-builder.js +41 -0
- package/package.json +1 -1
- package/public/ai-hub/index.html +17 -1
- package/public/ai-hub/script.js +441 -21
- package/public/ai-hub/styles.css +67 -0
- package/public/first-run/script.js +98 -25
- package/public/first-run/styles.css +34 -8
package/public/ai-hub/styles.css
CHANGED
|
@@ -1891,6 +1891,22 @@ img.coach-employee-avatar { object-fit: cover; border-radius: 4px; }
|
|
|
1891
1891
|
|
|
1892
1892
|
/* Agent install panel */
|
|
1893
1893
|
#agent-install-panel { margin-top: 10px; }
|
|
1894
|
+
.hub-agent-setup-panel {
|
|
1895
|
+
width: min(560px, 100%);
|
|
1896
|
+
margin: 16px 0;
|
|
1897
|
+
padding: 14px;
|
|
1898
|
+
border: 1px solid var(--line);
|
|
1899
|
+
border-radius: 8px;
|
|
1900
|
+
background: var(--surface);
|
|
1901
|
+
box-shadow: var(--shadow);
|
|
1902
|
+
text-align: left;
|
|
1903
|
+
}
|
|
1904
|
+
.cp-agent-install-panel {
|
|
1905
|
+
margin: 0 14px 12px;
|
|
1906
|
+
padding: 12px;
|
|
1907
|
+
border-top: 1px solid var(--line);
|
|
1908
|
+
background: var(--surface);
|
|
1909
|
+
}
|
|
1894
1910
|
.install-panel-heading {
|
|
1895
1911
|
font-size: 12px;
|
|
1896
1912
|
font-weight: 600;
|
|
@@ -1899,6 +1915,12 @@ img.coach-employee-avatar { object-fit: cover; border-radius: 4px; }
|
|
|
1899
1915
|
letter-spacing: .04em;
|
|
1900
1916
|
margin-bottom: 6px;
|
|
1901
1917
|
}
|
|
1918
|
+
.install-panel-copy {
|
|
1919
|
+
margin: 0 0 8px;
|
|
1920
|
+
max-width: none;
|
|
1921
|
+
font-size: 13px;
|
|
1922
|
+
color: var(--muted);
|
|
1923
|
+
}
|
|
1902
1924
|
.install-row {
|
|
1903
1925
|
display: flex;
|
|
1904
1926
|
align-items: center;
|
|
@@ -4409,6 +4431,51 @@ body.hub-shell { display: flex; flex-direction: column; height: 100vh; overflow:
|
|
|
4409
4431
|
.nav-right { height: 44px; display: flex; align-items: center; }
|
|
4410
4432
|
.avatar-btn { margin-left: 8px; }
|
|
4411
4433
|
|
|
4434
|
+
/* ════════════════ Issue #744 — Hub cobranding ════════════════ */
|
|
4435
|
+
/* Company brand lockup at the far left of the top nav (before the tabs).
|
|
4436
|
+
Sits in the Electron drag region; interactive children opt out. */
|
|
4437
|
+
.hub-brand { display: inline-flex; align-items: center; gap: 9px; height: 44px; padding: 0 4px 0 14px; flex-shrink: 0; -webkit-app-region: drag; }
|
|
4438
|
+
.hub-brand[hidden] { display: none; }
|
|
4439
|
+
.hub-brand-logo { width: 22px; height: 22px; border-radius: 6px; display: block; flex-shrink: 0; box-shadow: 0 0 0 1px var(--line); overflow: hidden; }
|
|
4440
|
+
.hub-brand-logo img, .hub-brand-logo svg { width: 100%; height: 100%; object-fit: contain; display: block; border-radius: 6px; }
|
|
4441
|
+
/* R1.6: a normal name shows in full; a very long one caps and ellipsizes so it
|
|
4442
|
+
never wraps or pushes the tabs off-screen. */
|
|
4443
|
+
.hub-brand-name { font-size: 14px; font-weight: 650; color: var(--text); letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 220px; }
|
|
4444
|
+
.hub-brand-divider { width: 1px; height: 20px; background: var(--line); margin: 0 6px 0 14px; flex-shrink: 0; }
|
|
4445
|
+
.hub-brand-divider[hidden] { display: none; }
|
|
4446
|
+
/* FRAIM co-mark on the right, before the avatar (co-branding, not white-label). */
|
|
4447
|
+
.hub-cobrand { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted); margin: 0 10px 0 0; white-space: nowrap; -webkit-app-region: no-drag; }
|
|
4448
|
+
.hub-cobrand[hidden] { display: none; }
|
|
4449
|
+
.hub-cobrand-mark { width: 17px; height: 17px; border-radius: 5px; display: block; }
|
|
4450
|
+
/* Very narrow windows (the Hub is desktop/Electron-first, but keep the dense nav
|
|
4451
|
+
from forcing horizontal page scroll): drop the co-mark and the company name,
|
|
4452
|
+
keeping the company logo mark + tabs. */
|
|
4453
|
+
@media (max-width: 560px) {
|
|
4454
|
+
.hub-cobrand { display: none; }
|
|
4455
|
+
.hub-brand-name { display: none; }
|
|
4456
|
+
.hub-brand { padding: 0 2px 0 10px; }
|
|
4457
|
+
.hub-brand-divider { margin: 0 4px 0 8px; }
|
|
4458
|
+
.hub-tab { padding: 10px 12px; }
|
|
4459
|
+
}
|
|
4460
|
+
|
|
4461
|
+
/* Brand editor (Company tab → Brand accordion). */
|
|
4462
|
+
.brand-editor { padding: 4px 2px 2px; }
|
|
4463
|
+
.brand-field { margin: 0 0 16px; }
|
|
4464
|
+
.brand-field > label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 6px; color: var(--text); }
|
|
4465
|
+
.brand-field .brand-hint { font-size: 12px; color: var(--muted); margin-top: 5px; }
|
|
4466
|
+
.brand-field .brand-hint.warn { color: var(--warn); }
|
|
4467
|
+
.brand-logo-drop { display: flex; align-items: center; gap: 14px; border: 1px dashed color-mix(in srgb, var(--accent) 40%, var(--line)); border-radius: 12px; padding: 14px; background: var(--accent-soft); cursor: pointer; }
|
|
4468
|
+
.brand-logo-drop .blp { width: 44px; height: 44px; border-radius: 10px; box-shadow: 0 0 0 1px var(--line); flex-shrink: 0; overflow: hidden; background: var(--surface); display: flex; align-items: center; justify-content: center; }
|
|
4469
|
+
.brand-logo-drop .blp svg, .brand-logo-drop .blp img { width: 100%; height: 100%; object-fit: contain; }
|
|
4470
|
+
.brand-logo-drop .blp-txt { font-size: 12.5px; color: var(--text); }
|
|
4471
|
+
.brand-swatch-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
|
|
4472
|
+
.brand-swatch { width: 34px; height: 34px; border-radius: 9px; cursor: pointer; box-shadow: 0 0 0 1px var(--line); border: none; padding: 0; }
|
|
4473
|
+
.brand-swatch.sel { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent); }
|
|
4474
|
+
.brand-color-hex { width: 96px; }
|
|
4475
|
+
.brand-editor-actions { display: flex; gap: 10px; margin-top: 6px; align-items: center; }
|
|
4476
|
+
.brand-preview { border: 1px solid var(--line); border-radius: 12px; overflow: hidden; margin-top: 14px; }
|
|
4477
|
+
.brand-preview-cap { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); padding: 10px 14px; background: var(--bg); border-bottom: 1px solid var(--line); }
|
|
4478
|
+
|
|
4412
4479
|
/* ── New-project: folder picker row ── */
|
|
4413
4480
|
.np-folder-row { display:flex; gap:8px; }
|
|
4414
4481
|
.np-folder-row input { flex:1; }
|
|
@@ -79,6 +79,19 @@
|
|
|
79
79
|
return setupResult && Array.isArray(setupResult.configuredSurfaces) ? setupResult.configuredSurfaces : [];
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
function readyAgentLabels() {
|
|
83
|
+
const labels = new Set();
|
|
84
|
+
for (const agent of readyAgents()) labels.add(agent.label);
|
|
85
|
+
for (const surface of configuredSurfaces()) {
|
|
86
|
+
if (surface && surface.name) labels.add(surface.name);
|
|
87
|
+
}
|
|
88
|
+
return Array.from(labels);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function slugifyAgentName(name) {
|
|
92
|
+
return String(name || '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
93
|
+
}
|
|
94
|
+
|
|
82
95
|
function isAgentReady(opt) {
|
|
83
96
|
const installState = state.session && state.session.state && state.session.state.agentInstalls
|
|
84
97
|
? state.session.state.agentInstalls[opt.id]
|
|
@@ -208,38 +221,54 @@
|
|
|
208
221
|
h.textContent = 'Locally installed AI agents';
|
|
209
222
|
pane.appendChild(h);
|
|
210
223
|
|
|
211
|
-
const anyDetected = detectedAgentCount() > 0;
|
|
212
224
|
const p = document.createElement('p');
|
|
213
225
|
p.className = 'pane-copy';
|
|
214
|
-
p.textContent =
|
|
215
|
-
? '
|
|
216
|
-
: '
|
|
226
|
+
p.textContent = detectedAgentCount() > 0
|
|
227
|
+
? 'At least one local AI agent is ready. You can add more agents or continue to configure FRAIM.'
|
|
228
|
+
: 'No problem, we will install AI agents next.';
|
|
217
229
|
pane.appendChild(p);
|
|
218
230
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
231
|
+
const readyLabels = readyAgentLabels();
|
|
232
|
+
const readyList = document.createElement('div');
|
|
233
|
+
readyList.className = readyLabels.length > 0 ? 'ready-strip' : 'ready-strip ready-strip--muted';
|
|
234
|
+
readyList.setAttribute('data-testid', 'ready-agents');
|
|
235
|
+
readyList.textContent = readyLabels.length > 0
|
|
236
|
+
? `Already installed: ${readyLabels.join(', ')}`
|
|
237
|
+
: 'Already installed: none detected yet';
|
|
238
|
+
pane.appendChild(readyList);
|
|
239
|
+
|
|
240
|
+
const catalogHeading = document.createElement('h3');
|
|
241
|
+
catalogHeading.className = 'agent-section-heading';
|
|
242
|
+
catalogHeading.textContent = 'Supported AI tools and IDEs';
|
|
243
|
+
pane.appendChild(catalogHeading);
|
|
244
|
+
|
|
245
|
+
const catalogCopy = document.createElement('p');
|
|
246
|
+
catalogCopy.className = 'pane-copy';
|
|
247
|
+
catalogCopy.textContent = 'FRAIM can configure every supported local agent or IDE it detects. Download links stay available for tools not installed yet.';
|
|
248
|
+
pane.appendChild(catalogCopy);
|
|
222
249
|
|
|
223
250
|
const list = document.createElement('ul');
|
|
224
251
|
list.className = 'agent-list';
|
|
225
|
-
list.setAttribute('data-testid', 'agent-list');
|
|
226
|
-
|
|
227
|
-
for (const agent of
|
|
252
|
+
list.setAttribute('data-testid', 'supported-agent-list');
|
|
253
|
+
const supportedAgents = (state.session && state.session.supportedAgents) || [];
|
|
254
|
+
for (const agent of supportedAgents) {
|
|
255
|
+
const slug = slugifyAgentName(agent.name);
|
|
256
|
+
const detected = agent.detected || readyLabels.includes(agent.name);
|
|
228
257
|
const li = document.createElement('li');
|
|
229
|
-
li.className = 'agent-row' + (
|
|
230
|
-
li.setAttribute('data-testid', `agent-row-${
|
|
258
|
+
li.className = 'agent-row' + (detected ? ' agent-row--detected' : '');
|
|
259
|
+
li.setAttribute('data-testid', `supported-agent-row-${slug}`);
|
|
231
260
|
|
|
232
261
|
const nameSpan = document.createElement('span');
|
|
233
262
|
nameSpan.className = 'agent-row__name';
|
|
234
263
|
nameSpan.textContent = agent.name;
|
|
264
|
+
li.appendChild(nameSpan);
|
|
235
265
|
|
|
236
266
|
const actionSpan = document.createElement('span');
|
|
237
267
|
actionSpan.className = 'agent-row__action';
|
|
238
|
-
|
|
239
|
-
if (agent.detected) {
|
|
268
|
+
if (detected) {
|
|
240
269
|
const badge = document.createElement('span');
|
|
241
270
|
badge.className = 'agent-badge agent-badge--installed';
|
|
242
|
-
badge.setAttribute('data-testid', `agent-installed-${
|
|
271
|
+
badge.setAttribute('data-testid', `supported-agent-installed-${slug}`);
|
|
243
272
|
badge.textContent = 'Installed';
|
|
244
273
|
actionSpan.appendChild(badge);
|
|
245
274
|
} else if (agent.downloadUrl) {
|
|
@@ -249,23 +278,20 @@
|
|
|
249
278
|
link.target = '_blank';
|
|
250
279
|
link.rel = 'noopener noreferrer';
|
|
251
280
|
link.textContent = 'Download';
|
|
252
|
-
link.setAttribute('data-testid', `agent-download-${
|
|
281
|
+
link.setAttribute('data-testid', `supported-agent-download-${slug}`);
|
|
253
282
|
actionSpan.appendChild(link);
|
|
283
|
+
} else {
|
|
284
|
+
const unavailable = document.createElement('span');
|
|
285
|
+
unavailable.className = 'agent-row__muted';
|
|
286
|
+
unavailable.textContent = 'Manual setup';
|
|
287
|
+
actionSpan.appendChild(unavailable);
|
|
254
288
|
}
|
|
255
|
-
|
|
256
|
-
li.appendChild(nameSpan);
|
|
257
289
|
li.appendChild(actionSpan);
|
|
258
290
|
list.appendChild(li);
|
|
259
291
|
}
|
|
260
|
-
|
|
261
292
|
pane.appendChild(list);
|
|
262
293
|
|
|
263
|
-
const
|
|
264
|
-
otherNote.className = 'pane-copy';
|
|
265
|
-
otherNote.innerHTML = 'Using a different tool? Run <code>npx fraim add-ide</code> from your project directory after setup.';
|
|
266
|
-
pane.appendChild(otherNote);
|
|
267
|
-
|
|
268
|
-
const rescanBtn = button('Rescan installed tools', 'ghost');
|
|
294
|
+
const rescanBtn = button('Rescan supported tools', 'ghost');
|
|
269
295
|
rescanBtn.setAttribute('data-testid', 'rescan-agents');
|
|
270
296
|
rescanBtn.addEventListener('click', async () => {
|
|
271
297
|
rescanBtn.disabled = true;
|
|
@@ -277,6 +303,53 @@
|
|
|
277
303
|
});
|
|
278
304
|
pane.appendChild(rescanBtn);
|
|
279
305
|
|
|
306
|
+
const hubHeading = document.createElement('h3');
|
|
307
|
+
hubHeading.className = 'agent-section-heading';
|
|
308
|
+
hubHeading.textContent = 'Hub-ready CLI agents';
|
|
309
|
+
pane.appendChild(hubHeading);
|
|
310
|
+
|
|
311
|
+
const hubCopy = document.createElement('p');
|
|
312
|
+
hubCopy.className = 'pane-copy';
|
|
313
|
+
hubCopy.textContent = 'FRAIM Hub runs jobs through CLI agents. Set up at least one of these if you plan to launch jobs in Hub.';
|
|
314
|
+
pane.appendChild(hubCopy);
|
|
315
|
+
|
|
316
|
+
const grid = document.createElement('div');
|
|
317
|
+
grid.className = 'agent-grid';
|
|
318
|
+
const agentOptions = state.session.agentOptions || [];
|
|
319
|
+
for (const opt of agentOptions) {
|
|
320
|
+
const card = document.createElement('div');
|
|
321
|
+
card.className = 'user-type-card recruit-card';
|
|
322
|
+
card.setAttribute('data-agent-id', opt.id);
|
|
323
|
+
card.setAttribute('data-testid', `agent-card-${opt.id}`);
|
|
324
|
+
const agentReady = isAgentReady(opt);
|
|
325
|
+
|
|
326
|
+
const title = document.createElement('strong');
|
|
327
|
+
title.className = 'card-title';
|
|
328
|
+
title.textContent = opt.label;
|
|
329
|
+
|
|
330
|
+
const desc = document.createElement('p');
|
|
331
|
+
desc.className = 'card-desc';
|
|
332
|
+
desc.textContent = agentReady
|
|
333
|
+
? 'Already installed and ready for FRAIM.'
|
|
334
|
+
: `Available to set up: install, sign in, and verify ${opt.label}.`;
|
|
335
|
+
|
|
336
|
+
const action = button(agentReady ? 'Ready' : 'Set up', 'secondary');
|
|
337
|
+
action.disabled = agentReady;
|
|
338
|
+
action.setAttribute('data-testid', `install-${opt.id}`);
|
|
339
|
+
action.addEventListener('click', () => openAgentModal(opt));
|
|
340
|
+
|
|
341
|
+
card.appendChild(title);
|
|
342
|
+
card.appendChild(desc);
|
|
343
|
+
card.appendChild(action);
|
|
344
|
+
grid.appendChild(card);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const byoa = document.createElement('div');
|
|
348
|
+
byoa.className = 'user-type-card recruit-card byoa-card';
|
|
349
|
+
byoa.innerHTML = '<strong class="card-title">Bring Your Own Agent</strong><p class="card-desc">Use Cursor, Windsurf, Kiro, VS Code, or another supported local AI tool. If you install a new agent later and want FRAIM to use it, run this command from your project.</p><div class="cmd-block">npx fraim add-ide</div>';
|
|
350
|
+
grid.appendChild(byoa);
|
|
351
|
+
pane.appendChild(grid);
|
|
352
|
+
|
|
280
353
|
const done = button('Next', 'primary');
|
|
281
354
|
done.setAttribute('data-testid', 'done-installing-agents');
|
|
282
355
|
done.disabled = detectedAgentCount() < 1;
|
|
@@ -531,15 +531,22 @@ body {
|
|
|
531
531
|
gap: 12px;
|
|
532
532
|
}
|
|
533
533
|
|
|
534
|
-
|
|
534
|
+
.agent-section-heading {
|
|
535
|
+
margin: 8px 0 -6px;
|
|
536
|
+
font-size: 13px;
|
|
537
|
+
font-weight: 700;
|
|
538
|
+
color: var(--text);
|
|
539
|
+
}
|
|
540
|
+
|
|
535
541
|
.agent-list {
|
|
536
542
|
list-style: none;
|
|
537
|
-
margin: 0
|
|
543
|
+
margin: 0;
|
|
538
544
|
padding: 0;
|
|
539
545
|
border: 1px solid var(--line);
|
|
540
546
|
border-radius: 10px;
|
|
541
547
|
overflow: hidden;
|
|
542
548
|
}
|
|
549
|
+
|
|
543
550
|
.agent-row {
|
|
544
551
|
display: flex;
|
|
545
552
|
align-items: center;
|
|
@@ -549,32 +556,51 @@ body {
|
|
|
549
556
|
background: var(--surface);
|
|
550
557
|
gap: 12px;
|
|
551
558
|
}
|
|
559
|
+
|
|
552
560
|
.agent-row:last-child { border-bottom: 0; }
|
|
553
561
|
.agent-row--detected { background: var(--accent-soft); }
|
|
562
|
+
|
|
554
563
|
.agent-row__name {
|
|
564
|
+
min-width: 0;
|
|
565
|
+
flex: 1;
|
|
555
566
|
font-size: 14px;
|
|
556
567
|
font-weight: 500;
|
|
557
568
|
color: var(--text);
|
|
558
|
-
flex: 1;
|
|
559
569
|
}
|
|
570
|
+
|
|
560
571
|
.agent-row--detected .agent-row__name { color: var(--accent-strong); }
|
|
561
|
-
|
|
562
|
-
.agent-
|
|
572
|
+
|
|
573
|
+
.agent-row__action {
|
|
574
|
+
flex: 0 0 auto;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.agent-row__muted {
|
|
563
578
|
font-size: 12px;
|
|
564
|
-
|
|
579
|
+
color: var(--muted);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.agent-badge {
|
|
583
|
+
display: inline-flex;
|
|
584
|
+
align-items: center;
|
|
585
|
+
min-height: 24px;
|
|
565
586
|
padding: 3px 10px;
|
|
566
|
-
border-radius:
|
|
587
|
+
border-radius: 999px;
|
|
588
|
+
font-size: 12px;
|
|
589
|
+
font-weight: 700;
|
|
567
590
|
}
|
|
591
|
+
|
|
568
592
|
.agent-badge--installed {
|
|
569
593
|
background: var(--accent);
|
|
570
594
|
color: #fff;
|
|
571
595
|
}
|
|
596
|
+
|
|
572
597
|
.agent-download-link {
|
|
573
598
|
font-size: 13px;
|
|
574
|
-
font-weight:
|
|
599
|
+
font-weight: 600;
|
|
575
600
|
color: var(--accent-strong);
|
|
576
601
|
text-decoration: none;
|
|
577
602
|
}
|
|
603
|
+
|
|
578
604
|
.agent-download-link:hover { text-decoration: underline; }
|
|
579
605
|
|
|
580
606
|
.ready-strip {
|