@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 (consolidated header) ═══════════ */}
218
- <section className="rounded-xl border border-border bg-gradient-to-b from-card to-card/80 overflow-hidden">
219
- <div className="flex items-center gap-4 p-5">
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-xl 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-1">
224
- <span className={`text-2xs font-medium px-2 py-0.5 rounded-full ${
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="text-2xs text-muted-foreground/60 font-mono">{agent.transport ?? agent.preferredTransport}</span>
230
- <span className="text-2xs text-muted-foreground/30" aria-hidden="true">·</span>
231
- <span className="text-2xs text-muted-foreground/60">{agent.skillMode ?? a.na}</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-5 gap-y-1 text-xs text-muted-foreground/70 px-5 py-3 border-t border-border/50 bg-muted/[0.03]">
236
- <span>{a.detail.format}: <span className="text-foreground/80 font-medium">{agent.format}</span></span>
237
- <span>{a.detail.lastActivityAt}: <span className="text-foreground/80 tabular-nums font-medium">{agent.runtimeLastActivityAt ?? a.na}</span></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-5 space-y-4">
244
- <h2 className="text-sm font-semibold text-foreground flex items-center gap-2">
245
- <div className="w-6 h-6 rounded-md bg-muted/50 flex items-center justify-center">
246
- <Server size={13} className="text-muted-foreground/70" />
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
- {a.detail.mcpManagement}
249
- </h2>
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 row */}
252
- <div className="grid grid-cols-1 md:grid-cols-3 gap-3 text-sm">
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 ? 'Running' : 'Stopped'} />
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 with management */}
269
- <div className="rounded-xl border border-border/60 bg-background/50 p-4 space-y-2.5">
293
+ {/* Configured MCP servers */}
294
+ <div className="space-y-2">
270
295
  <div className="flex items-center justify-between">
271
- <p className="text-xs font-semibold text-foreground">{a.detail.configuredMcpServers}</p>
272
- <span className="text-2xs text-muted-foreground/60 tabular-nums">{a.detail.configuredMcpServersCount(configuredMcpServers.length)}</span>
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-xs text-muted-foreground">{a.detail.configuredMcpServersEmpty}</p>
307
+ <p className="text-2xs text-muted-foreground/50">{a.detail.configuredMcpServersEmpty}</p>
283
308
  ) : (
284
- <div className="space-y-1.5 max-h-[280px] overflow-y-auto">
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.5 rounded-lg border border-border/40 bg-muted/[0.02] px-3 py-2.5 group/mcp hover:border-border/60 hover:bg-muted/[0.06] hover:shadow-[0_1px_3px_rgba(0,0,0,0.02)] transition-all duration-150">
289
- <div className="w-5 h-5 rounded-md bg-[var(--amber)]/[0.08] flex items-center justify-center shrink-0">
290
- <Server size={10} className="text-[var(--amber)]" />
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-1">
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-2xs text-muted-foreground 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 px-1 py-0.5 transition-all duration-150"
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={12} />
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-5 space-y-4">
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-sm font-semibold text-foreground flex items-center gap-2">
350
- <div className="w-6 h-6 rounded-md bg-muted/50 flex items-center justify-center">
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-2 text-2xs text-muted-foreground/60 tabular-nums">
356
- <span className="px-1.5 py-0.5 rounded bg-muted/40">MindOS {skillSummary.total}</span>
357
- <span className="px-1.5 py-0.5 rounded bg-emerald-500/[0.06] text-emerald-600 dark:text-emerald-400">{a.detail.skillsEnabled.split(' ')[0]} {skillSummary.enabled}</span>
358
- <span className="px-1.5 py-0.5 rounded bg-muted/40">{a.detail.nativeInstalledSkills} {nativeInstalledSkills.length}</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="rounded-lg border border-border/60 bg-muted/[0.02] px-3.5 py-2.5 hover:bg-muted/[0.06] transition-colors duration-100">
532
- <p className="text-2xs text-muted-foreground/60 mb-1 uppercase tracking-wider">{label}</p>
533
- <p className="text-sm text-foreground font-medium truncate">{value}</p>
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
- import {
6
- createRuntime,
7
- createCallResult,
8
- type Runtime,
9
- type ServerToolInfo,
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 rt = await createRuntime({
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 result = createCallResult(raw);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geminilight/mindos",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "MindOS — Human-Agent Collaborative Mind System. Local-first knowledge base that syncs your mind to all AI Agents via MCP.",
5
5
  "keywords": [
6
6
  "mindos",