@geminilight/mindos 0.6.2 → 0.6.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.
|
@@ -214,45 +214,70 @@ export default function AgentDetailContent({ agentKey }: { agentKey: string }) {
|
|
|
214
214
|
{a.backToOverview}
|
|
215
215
|
</Link>
|
|
216
216
|
|
|
217
|
-
{/* ═══════════ AGENT PROFILE
|
|
218
|
-
<section className="rounded-xl border border-border bg-
|
|
219
|
-
<div className="flex items-center gap-
|
|
217
|
+
{/* ═══════════ AGENT PROFILE ═══════════ */}
|
|
218
|
+
<section className="rounded-xl border border-border bg-card overflow-hidden">
|
|
219
|
+
<div className="flex items-center gap-3.5 px-5 py-4">
|
|
220
220
|
<AgentAvatar name={agent.name} status={status} size="md" />
|
|
221
221
|
<div className="min-w-0 flex-1">
|
|
222
|
-
<h1 className="text-
|
|
223
|
-
<div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 mt-
|
|
224
|
-
<span className={`
|
|
222
|
+
<h1 className="text-lg font-semibold tracking-tight font-display text-foreground">{agent.name}</h1>
|
|
223
|
+
<div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 mt-0.5 text-2xs text-muted-foreground/60">
|
|
224
|
+
<span className={`font-medium px-1.5 py-px rounded-full ${
|
|
225
225
|
status === 'connected' ? 'bg-success/10 text-success'
|
|
226
226
|
: status === 'detected' ? 'bg-[var(--amber-subtle)] text-[var(--amber)]'
|
|
227
227
|
: 'bg-muted text-muted-foreground'
|
|
228
228
|
}`}>{status}</span>
|
|
229
|
-
<span className="
|
|
230
|
-
<span className="text-
|
|
231
|
-
<span
|
|
229
|
+
<span className="font-mono">{agent.transport ?? agent.preferredTransport}</span>
|
|
230
|
+
<span className="text-muted-foreground/25" aria-hidden="true">·</span>
|
|
231
|
+
<span>{agent.skillMode ?? a.na}</span>
|
|
232
232
|
</div>
|
|
233
233
|
</div>
|
|
234
234
|
</div>
|
|
235
|
-
<div className="flex flex-wrap items-center gap-x-
|
|
236
|
-
<span>{
|
|
237
|
-
<span>{
|
|
238
|
-
<span className="font-medium text-foreground/80 tabular-nums">{configuredMcpServers.length} MCP · {nativeInstalledSkills.length} skills</span>
|
|
235
|
+
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-2xs text-muted-foreground/50 px-5 py-2 border-t border-border/40">
|
|
236
|
+
<span>{agent.format} <span className="text-muted-foreground/30">·</span> {formatRelativeTime(agent.runtimeLastActivityAt)}</span>
|
|
237
|
+
<span className="tabular-nums">{configuredMcpServers.length} MCP <span className="text-muted-foreground/30">·</span> {nativeInstalledSkills.length} skills</span>
|
|
239
238
|
</div>
|
|
240
239
|
</section>
|
|
241
240
|
|
|
242
241
|
{/* ═══════════ MCP MANAGEMENT ═══════════ */}
|
|
243
|
-
<section className="rounded-xl border border-border bg-card p-
|
|
244
|
-
<
|
|
245
|
-
<
|
|
246
|
-
<Server size={
|
|
242
|
+
<section className="rounded-xl border border-border bg-card p-4 space-y-3">
|
|
243
|
+
<div className="flex items-center justify-between">
|
|
244
|
+
<h2 className="text-xs font-semibold text-foreground flex items-center gap-2">
|
|
245
|
+
<Server size={12} className="text-muted-foreground/50" />
|
|
246
|
+
{a.detail.mcpManagement}
|
|
247
|
+
</h2>
|
|
248
|
+
<div className="flex items-center gap-1.5">
|
|
249
|
+
{!isMindOS && (
|
|
250
|
+
<ActionButton
|
|
251
|
+
onClick={() => void handleCopySnippet()}
|
|
252
|
+
disabled={false}
|
|
253
|
+
busy={false}
|
|
254
|
+
label={snippetCopied ? a.detail.mcpCopied : a.detail.mcpCopySnippet}
|
|
255
|
+
/>
|
|
256
|
+
)}
|
|
257
|
+
<ActionButton
|
|
258
|
+
onClick={() => void mcp.refresh()}
|
|
259
|
+
disabled={false}
|
|
260
|
+
busy={false}
|
|
261
|
+
label={a.detail.mcpRefresh}
|
|
262
|
+
/>
|
|
263
|
+
{!isMindOS && (
|
|
264
|
+
<ActionButton
|
|
265
|
+
onClick={() => void handleApplyMcpConfig(currentScope, currentTransport)}
|
|
266
|
+
disabled={mcpBusy}
|
|
267
|
+
busy={mcpBusy}
|
|
268
|
+
label={a.detail.mcpReconnect}
|
|
269
|
+
/>
|
|
270
|
+
)}
|
|
247
271
|
</div>
|
|
248
|
-
|
|
249
|
-
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
{mcpMessage && <p className="text-2xs text-muted-foreground animate-in fade-in duration-200">{mcpMessage}</p>}
|
|
250
275
|
|
|
251
|
-
{/* MCP status
|
|
252
|
-
<div className="
|
|
276
|
+
{/* MCP status metadata */}
|
|
277
|
+
<div className="flex flex-wrap gap-x-6 gap-y-1 py-2 border-y border-border/30">
|
|
253
278
|
{isMindOS ? (
|
|
254
279
|
<>
|
|
255
|
-
<DetailLine label="Status" value={mcp.status?.running ? '
|
|
280
|
+
<DetailLine label="Status" value={mcp.status?.running ? 'Running' : 'Stopped'} />
|
|
256
281
|
<DetailLine label="Endpoint" value={mcp.status?.endpoint ?? a.na} />
|
|
257
282
|
<DetailLine label="Tools" value={mcp.status?.toolCount != null ? String(mcp.status.toolCount) : a.na} />
|
|
258
283
|
</>
|
|
@@ -265,11 +290,11 @@ export default function AgentDetailContent({ agentKey }: { agentKey: string }) {
|
|
|
265
290
|
)}
|
|
266
291
|
</div>
|
|
267
292
|
|
|
268
|
-
{/* Configured MCP servers
|
|
269
|
-
<div className="
|
|
293
|
+
{/* Configured MCP servers */}
|
|
294
|
+
<div className="space-y-2">
|
|
270
295
|
<div className="flex items-center justify-between">
|
|
271
|
-
<p className="text-
|
|
272
|
-
<span className="text-2xs text-muted-foreground/
|
|
296
|
+
<p className="text-2xs font-medium text-muted-foreground/60 uppercase tracking-wider">{a.detail.configuredMcpServers}</p>
|
|
297
|
+
<span className="text-2xs text-muted-foreground/40 tabular-nums">{configuredMcpServers.length}</span>
|
|
273
298
|
</div>
|
|
274
299
|
|
|
275
300
|
{mcpHint && (
|
|
@@ -279,32 +304,30 @@ export default function AgentDetailContent({ agentKey }: { agentKey: string }) {
|
|
|
279
304
|
)}
|
|
280
305
|
|
|
281
306
|
{configuredMcpServers.length === 0 ? (
|
|
282
|
-
<p className="text-
|
|
307
|
+
<p className="text-2xs text-muted-foreground/50">{a.detail.configuredMcpServersEmpty}</p>
|
|
283
308
|
) : (
|
|
284
|
-
<div className="space-y-1
|
|
309
|
+
<div className="space-y-1 max-h-[240px] overflow-y-auto">
|
|
285
310
|
{configuredMcpServers.map((name) => {
|
|
286
311
|
const sharedWith = (crossAgentMcpMap.get(name) ?? []).filter((n) => n !== agent.name);
|
|
287
312
|
return (
|
|
288
|
-
<div key={name} className="flex items-center gap-2
|
|
289
|
-
<
|
|
290
|
-
|
|
291
|
-
</div>
|
|
292
|
-
<span className="text-xs font-medium text-foreground flex-1 min-w-0 truncate">{name}</span>
|
|
313
|
+
<div key={name} className="flex items-center gap-2 rounded-md px-2 py-1.5 group/mcp hover:bg-muted/30 transition-colors duration-100">
|
|
314
|
+
<Server size={11} className="text-muted-foreground/40 shrink-0" />
|
|
315
|
+
<span className="text-xs text-foreground flex-1 min-w-0 truncate">{name}</span>
|
|
293
316
|
{sharedWith.length > 0 && (
|
|
294
|
-
<div className="flex items-center gap-
|
|
317
|
+
<div className="flex items-center gap-0.5">
|
|
295
318
|
{sharedWith.slice(0, 3).map((n) => (
|
|
296
319
|
<AgentAvatar key={n} name={n} size="sm" />
|
|
297
320
|
))}
|
|
298
|
-
{sharedWith.length > 3 && <span className="text-2xs text-muted-foreground">+{sharedWith.length - 3}</span>}
|
|
321
|
+
{sharedWith.length > 3 && <span className="text-2xs text-muted-foreground/50">+{sharedWith.length - 3}</span>}
|
|
299
322
|
</div>
|
|
300
323
|
)}
|
|
301
324
|
<button
|
|
302
325
|
type="button"
|
|
303
326
|
onClick={() => setConfirmMcpRemove(name)}
|
|
304
|
-
className="text-
|
|
327
|
+
className="text-muted-foreground/40 hover:text-destructive cursor-pointer opacity-0 group-hover/mcp:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded p-0.5 transition-all duration-150"
|
|
305
328
|
aria-label={`${a.detail.mcpServerRemove} ${name}`}
|
|
306
329
|
>
|
|
307
|
-
<Trash2 size={
|
|
330
|
+
<Trash2 size={11} />
|
|
308
331
|
</button>
|
|
309
332
|
</div>
|
|
310
333
|
);
|
|
@@ -312,50 +335,19 @@ export default function AgentDetailContent({ agentKey }: { agentKey: string }) {
|
|
|
312
335
|
</div>
|
|
313
336
|
)}
|
|
314
337
|
</div>
|
|
315
|
-
|
|
316
|
-
{/* MCP actions */}
|
|
317
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
318
|
-
{!isMindOS && (
|
|
319
|
-
<ActionButton
|
|
320
|
-
onClick={() => void handleCopySnippet()}
|
|
321
|
-
disabled={false}
|
|
322
|
-
busy={false}
|
|
323
|
-
label={snippetCopied ? a.detail.mcpCopied : a.detail.mcpCopySnippet}
|
|
324
|
-
/>
|
|
325
|
-
)}
|
|
326
|
-
<ActionButton
|
|
327
|
-
onClick={() => void mcp.refresh()}
|
|
328
|
-
disabled={false}
|
|
329
|
-
busy={false}
|
|
330
|
-
label={a.detail.mcpRefresh}
|
|
331
|
-
/>
|
|
332
|
-
{!isMindOS && (
|
|
333
|
-
<ActionButton
|
|
334
|
-
onClick={() => void handleApplyMcpConfig(currentScope, currentTransport)}
|
|
335
|
-
disabled={mcpBusy}
|
|
336
|
-
busy={mcpBusy}
|
|
337
|
-
label={a.detail.mcpReconnect}
|
|
338
|
-
variant="primary"
|
|
339
|
-
/>
|
|
340
|
-
)}
|
|
341
|
-
</div>
|
|
342
|
-
{!isMindOS && <p className="text-2xs text-muted-foreground truncate">{snippet.path}</p>}
|
|
343
|
-
{mcpMessage && <p className="text-xs text-muted-foreground animate-in fade-in duration-200">{mcpMessage}</p>}
|
|
344
338
|
</section>
|
|
345
339
|
|
|
346
340
|
{/* ═══════════ SKILL ASSIGNMENTS ═══════════ */}
|
|
347
|
-
<section className="rounded-xl border border-border bg-card p-
|
|
341
|
+
<section className="rounded-xl border border-border bg-card p-4 space-y-3">
|
|
348
342
|
<div className="flex items-center justify-between">
|
|
349
|
-
<h2 className="text-
|
|
350
|
-
<
|
|
351
|
-
<Zap size={13} className="text-muted-foreground/70" />
|
|
352
|
-
</div>
|
|
343
|
+
<h2 className="text-xs font-semibold text-foreground flex items-center gap-2">
|
|
344
|
+
<Zap size={12} className="text-muted-foreground/50" />
|
|
353
345
|
{a.detail.skillAssignments}
|
|
354
346
|
</h2>
|
|
355
|
-
<div className="flex items-center gap-
|
|
356
|
-
<span
|
|
357
|
-
<span className="
|
|
358
|
-
<span
|
|
347
|
+
<div className="flex items-center gap-1.5 text-2xs text-muted-foreground/50 tabular-nums">
|
|
348
|
+
<span>{skillSummary.enabled}/{skillSummary.total} enabled</span>
|
|
349
|
+
<span className="text-muted-foreground/25">·</span>
|
|
350
|
+
<span>{nativeInstalledSkills.length} native</span>
|
|
359
351
|
</div>
|
|
360
352
|
</div>
|
|
361
353
|
|
|
@@ -528,9 +520,26 @@ export default function AgentDetailContent({ agentKey }: { agentKey: string }) {
|
|
|
528
520
|
|
|
529
521
|
function DetailLine({ label, value }: { label: string; value: string }) {
|
|
530
522
|
return (
|
|
531
|
-
<div className="
|
|
532
|
-
<
|
|
533
|
-
<
|
|
523
|
+
<div className="flex items-baseline gap-2 px-0.5">
|
|
524
|
+
<span className="text-2xs text-muted-foreground/50 uppercase tracking-wider shrink-0 min-w-[60px]">{label}</span>
|
|
525
|
+
<span className="text-xs text-foreground/80 font-mono truncate">{value}</span>
|
|
534
526
|
</div>
|
|
535
527
|
);
|
|
536
528
|
}
|
|
529
|
+
|
|
530
|
+
function formatRelativeTime(iso: string | undefined | null): string {
|
|
531
|
+
if (!iso) return '—';
|
|
532
|
+
try {
|
|
533
|
+
const diff = Date.now() - new Date(iso).getTime();
|
|
534
|
+
if (diff < 0) return 'just now';
|
|
535
|
+
const mins = Math.floor(diff / 60000);
|
|
536
|
+
if (mins < 1) return 'just now';
|
|
537
|
+
if (mins < 60) return `${mins}m ago`;
|
|
538
|
+
const hrs = Math.floor(mins / 60);
|
|
539
|
+
if (hrs < 24) return `${hrs}h ago`;
|
|
540
|
+
const days = Math.floor(hrs / 24);
|
|
541
|
+
return `${days}d ago`;
|
|
542
|
+
} catch {
|
|
543
|
+
return iso;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
@@ -2,12 +2,11 @@ import os from 'os';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { Type } from '@sinclair/typebox';
|
|
4
4
|
import type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from 'mcporter';
|
|
5
|
+
|
|
6
|
+
// mcporter is ESM-only — must use dynamic import() to avoid CJS resolution failure
|
|
7
|
+
type McporterModule = typeof import('mcporter');
|
|
8
|
+
type Runtime = Awaited<ReturnType<McporterModule['createRuntime']>>;
|
|
9
|
+
type ServerToolInfo = { name: string; description?: string; inputSchema?: unknown };
|
|
11
10
|
|
|
12
11
|
export interface McporterServerSummary {
|
|
13
12
|
name: string;
|
|
@@ -65,6 +64,13 @@ const TOOL_TIMEOUT_MS = 30_000;
|
|
|
65
64
|
|
|
66
65
|
let _runtime: Runtime | null = null;
|
|
67
66
|
let _runtimePromise: Promise<Runtime | null> | null = null;
|
|
67
|
+
let _mcporter: McporterModule | null = null;
|
|
68
|
+
|
|
69
|
+
async function loadMcporter(): Promise<McporterModule> {
|
|
70
|
+
if (_mcporter) return _mcporter;
|
|
71
|
+
_mcporter = await import('mcporter');
|
|
72
|
+
return _mcporter;
|
|
73
|
+
}
|
|
68
74
|
|
|
69
75
|
async function getRuntime(): Promise<Runtime | null> {
|
|
70
76
|
if (_runtime) return _runtime;
|
|
@@ -72,7 +78,8 @@ async function getRuntime(): Promise<Runtime | null> {
|
|
|
72
78
|
|
|
73
79
|
_runtimePromise = (async () => {
|
|
74
80
|
try {
|
|
75
|
-
const
|
|
81
|
+
const mod = await loadMcporter();
|
|
82
|
+
const rt = await mod.createRuntime({
|
|
76
83
|
configPath: MCP_CONFIG_PATH,
|
|
77
84
|
clientInfo: { name: 'mindos', version: '1.0.0' },
|
|
78
85
|
});
|
|
@@ -178,7 +185,8 @@ export async function callMcporterTool(
|
|
|
178
185
|
args,
|
|
179
186
|
timeoutMs: TOOL_TIMEOUT_MS,
|
|
180
187
|
});
|
|
181
|
-
const
|
|
188
|
+
const mod = await loadMcporter();
|
|
189
|
+
const result = mod.createCallResult(raw);
|
|
182
190
|
return result.text('\n') ?? JSON.stringify(raw);
|
|
183
191
|
}
|
|
184
192
|
|
package/package.json
CHANGED