@agent-link/server 0.1.53 → 0.1.55

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-link/server",
3
- "version": "0.1.53",
3
+ "version": "0.1.55",
4
4
  "description": "AgentLink relay server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/web/app.js CHANGED
@@ -64,6 +64,14 @@ const App = {
64
64
  const folderPickerLoading = ref(false);
65
65
  const folderPickerSelected = ref('');
66
66
 
67
+ // Delete confirmation dialog state
68
+ const deleteConfirmOpen = ref(false);
69
+ const deleteConfirmTitle = ref('');
70
+
71
+ // Working directory history state
72
+ const workDirHistory = ref([]);
73
+ const workDirHistoryOpen = ref(false);
74
+
67
75
  // File attachment state
68
76
  const attachments = ref([]);
69
77
  const fileInputRef = ref(null);
@@ -136,6 +144,8 @@ const App = {
136
144
  loadingSessions, loadingHistory, workDir, visibleLimit,
137
145
  folderPickerOpen, folderPickerPath, folderPickerEntries,
138
146
  folderPickerLoading, folderPickerSelected, streaming,
147
+ deleteConfirmOpen, deleteConfirmTitle,
148
+ workDirHistory, workDirHistoryOpen,
139
149
  });
140
150
 
141
151
  const { connect, wsSend, closeWs } = createConnection({
@@ -268,6 +278,16 @@ const App = {
268
278
  folderPickerEnter: sidebar.folderPickerEnter,
269
279
  folderPickerGoToPath: sidebar.folderPickerGoToPath,
270
280
  confirmFolderPicker: sidebar.confirmFolderPicker,
281
+ // Delete session
282
+ deleteConfirmOpen, deleteConfirmTitle,
283
+ deleteSession: sidebar.deleteSession,
284
+ confirmDeleteSession: sidebar.confirmDeleteSession,
285
+ cancelDeleteSession: sidebar.cancelDeleteSession,
286
+ // Working directory history
287
+ workDirHistory, workDirHistoryOpen,
288
+ selectWorkDirHistory: sidebar.selectWorkDirHistory,
289
+ removeWorkDirHistory: sidebar.removeWorkDirHistory,
290
+ toggleWorkDirHistory: sidebar.toggleWorkDirHistory,
271
291
  // File attachments
272
292
  attachments, fileInputRef, dragOver,
273
293
  triggerFileInput: fileAttach.triggerFileInput,
@@ -325,11 +345,33 @@ const App = {
325
345
  </div>
326
346
  <div class="sidebar-workdir-header">
327
347
  <div class="sidebar-workdir-label">Working Directory</div>
328
- <button class="sidebar-change-dir-btn" @click="openFolderPicker" title="Change working directory" :disabled="isProcessing">
329
- <svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>
330
- </button>
348
+ <div class="sidebar-workdir-actions">
349
+ <button class="sidebar-change-dir-btn" @click="toggleWorkDirHistory" title="Recent directories" :disabled="isProcessing" v-if="workDirHistory.length > 1">
350
+ <svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M13 3a9 9 0 0 0-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.954 8.954 0 0 0 13 21a9 9 0 0 0 0-18zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/></svg>
351
+ </button>
352
+ <button class="sidebar-change-dir-btn" @click="openFolderPicker" title="Change working directory" :disabled="isProcessing">
353
+ <svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>
354
+ </button>
355
+ </div>
331
356
  </div>
332
357
  <div class="sidebar-workdir-path" :title="workDir">{{ workDir }}</div>
358
+ <!-- Workdir history dropdown -->
359
+ <div v-if="workDirHistoryOpen && workDirHistory.length > 1" class="workdir-history-dropdown">
360
+ <div
361
+ v-for="dir in workDirHistory" :key="dir"
362
+ :class="['workdir-history-item', { active: dir === workDir }]"
363
+ @click="selectWorkDirHistory(dir)"
364
+ :title="dir"
365
+ >
366
+ <span class="workdir-history-path">{{ dir }}</span>
367
+ <button
368
+ v-if="dir !== workDir"
369
+ class="workdir-history-remove"
370
+ @click.stop="removeWorkDirHistory(dir)"
371
+ title="Remove from history"
372
+ >&times;</button>
373
+ </div>
374
+ </div>
333
375
  </div>
334
376
  </div>
335
377
 
@@ -362,7 +404,17 @@ const App = {
362
404
  :title="s.preview"
363
405
  >
364
406
  <div class="session-title">{{ s.title }}</div>
365
- <div class="session-meta">{{ formatRelativeTime(s.lastModified) }}</div>
407
+ <div class="session-meta">
408
+ <span>{{ formatRelativeTime(s.lastModified) }}</span>
409
+ <button
410
+ v-if="currentClaudeSessionId !== s.sessionId"
411
+ class="session-delete-btn"
412
+ @click.stop="deleteSession(s)"
413
+ title="Delete session"
414
+ >
415
+ <svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
416
+ </button>
417
+ </div>
366
418
  </div>
367
419
  </div>
368
420
  </div>
@@ -614,6 +666,22 @@ const App = {
614
666
  </div>
615
667
  </div>
616
668
  </div>
669
+
670
+ <!-- Delete Session Confirmation Dialog -->
671
+ <div class="folder-picker-overlay" v-if="deleteConfirmOpen" @click.self="cancelDeleteSession">
672
+ <div class="delete-confirm-dialog">
673
+ <div class="delete-confirm-header">Delete Session</div>
674
+ <div class="delete-confirm-body">
675
+ <p>Are you sure you want to delete this session?</p>
676
+ <p class="delete-confirm-title">{{ deleteConfirmTitle }}</p>
677
+ <p class="delete-confirm-warning">This action cannot be undone.</p>
678
+ </div>
679
+ <div class="delete-confirm-footer">
680
+ <button class="folder-picker-cancel" @click="cancelDeleteSession">Cancel</button>
681
+ <button class="delete-confirm-btn" @click="confirmDeleteSession">Delete</button>
682
+ </div>
683
+ </div>
684
+ </div>
617
685
  </div>
618
686
  `
619
687
  };
@@ -145,6 +145,7 @@ export function createConnection(deps) {
145
145
  hostname.value = msg.agent.hostname || '';
146
146
  workDir.value = msg.agent.workDir;
147
147
  agentVersion.value = msg.agent.version || '';
148
+ sidebar.addToWorkDirHistory(msg.agent.workDir);
148
149
  const savedDir = localStorage.getItem('agentlink-workdir');
149
150
  if (savedDir && savedDir !== msg.agent.workDir) {
150
151
  wsSend({ type: 'change_workdir', workDir: savedDir });
@@ -257,6 +258,8 @@ export function createConnection(deps) {
257
258
  } else if (msg.type === 'sessions_list') {
258
259
  historySessions.value = msg.sessions || [];
259
260
  loadingSessions.value = false;
261
+ } else if (msg.type === 'session_deleted') {
262
+ historySessions.value = historySessions.value.filter(s => s.sessionId !== msg.sessionId);
260
263
  } else if (msg.type === 'conversation_resumed') {
261
264
  currentClaudeSessionId.value = msg.claudeSessionId;
262
265
  if (msg.history && Array.isArray(msg.history)) {
@@ -339,6 +342,7 @@ export function createConnection(deps) {
339
342
  } else if (msg.type === 'workdir_changed') {
340
343
  workDir.value = msg.workDir;
341
344
  localStorage.setItem('agentlink-workdir', msg.workDir);
345
+ sidebar.addToWorkDirHistory(msg.workDir);
342
346
  messages.value = [];
343
347
  toolMsgMap.clear();
344
348
  visibleLimit.value = 50;
@@ -31,6 +31,44 @@ export function createSidebar(deps) {
31
31
  folderPickerLoading, folderPickerSelected, streaming,
32
32
  } = deps;
33
33
 
34
+ // ── Working directory history ──
35
+
36
+ const WORKDIR_HISTORY_KEY = 'agentlink-workdir-history';
37
+ const MAX_HISTORY = 10;
38
+ const workDirHistory = deps.workDirHistory;
39
+ const workDirHistoryOpen = deps.workDirHistoryOpen;
40
+
41
+ // Load from localStorage on init
42
+ try {
43
+ const saved = localStorage.getItem(WORKDIR_HISTORY_KEY);
44
+ if (saved) workDirHistory.value = JSON.parse(saved);
45
+ } catch { /* ignore */ }
46
+
47
+ function addToWorkDirHistory(dir) {
48
+ if (!dir) return;
49
+ const list = workDirHistory.value.filter(d => d !== dir);
50
+ list.unshift(dir);
51
+ if (list.length > MAX_HISTORY) list.length = MAX_HISTORY;
52
+ workDirHistory.value = list;
53
+ localStorage.setItem(WORKDIR_HISTORY_KEY, JSON.stringify(list));
54
+ }
55
+
56
+ function selectWorkDirHistory(dir) {
57
+ workDirHistoryOpen.value = false;
58
+ if (dir === workDir.value) return;
59
+ wsSend({ type: 'change_workdir', workDir: dir });
60
+ }
61
+
62
+ function removeWorkDirHistory(dir) {
63
+ const list = workDirHistory.value.filter(d => d !== dir);
64
+ workDirHistory.value = list;
65
+ localStorage.setItem(WORKDIR_HISTORY_KEY, JSON.stringify(list));
66
+ }
67
+
68
+ function toggleWorkDirHistory() {
69
+ workDirHistoryOpen.value = !workDirHistoryOpen.value;
70
+ }
71
+
34
72
  // ── Session management ──
35
73
 
36
74
  function requestSessionList() {
@@ -79,6 +117,33 @@ export function createSidebar(deps) {
79
117
  sidebarOpen.value = !sidebarOpen.value;
80
118
  }
81
119
 
120
+ // ── Delete session ──
121
+
122
+ /** Session pending delete confirmation (null = dialog closed) */
123
+ let pendingDeleteSession = null;
124
+ const deleteConfirmOpen = deps.deleteConfirmOpen;
125
+ const deleteConfirmTitle = deps.deleteConfirmTitle;
126
+
127
+ function deleteSession(session) {
128
+ if (isProcessing.value) return;
129
+ if (currentClaudeSessionId.value === session.sessionId) return; // guard
130
+ pendingDeleteSession = session;
131
+ deleteConfirmTitle.value = session.title || session.sessionId.slice(0, 8);
132
+ deleteConfirmOpen.value = true;
133
+ }
134
+
135
+ function confirmDeleteSession() {
136
+ if (!pendingDeleteSession) return;
137
+ wsSend({ type: 'delete_session', sessionId: pendingDeleteSession.sessionId });
138
+ deleteConfirmOpen.value = false;
139
+ pendingDeleteSession = null;
140
+ }
141
+
142
+ function cancelDeleteSession() {
143
+ deleteConfirmOpen.value = false;
144
+ pendingDeleteSession = null;
145
+ }
146
+
82
147
  // ── Folder picker ──
83
148
 
84
149
  function openFolderPicker() {
@@ -179,6 +244,8 @@ export function createSidebar(deps) {
179
244
 
180
245
  return {
181
246
  requestSessionList, resumeSession, newConversation, toggleSidebar,
247
+ deleteSession, confirmDeleteSession, cancelDeleteSession,
248
+ addToWorkDirHistory, selectWorkDirHistory, removeWorkDirHistory, toggleWorkDirHistory,
182
249
  openFolderPicker, folderPickerNavigateUp, folderPickerSelectItem,
183
250
  folderPickerEnter, folderPickerGoToPath, confirmFolderPicker,
184
251
  groupedSessions,
package/web/style.css CHANGED
@@ -438,6 +438,100 @@ body {
438
438
  font-size: 0.7rem;
439
439
  color: var(--text-secondary);
440
440
  margin-top: 2px;
441
+ display: flex;
442
+ align-items: center;
443
+ justify-content: space-between;
444
+ }
445
+
446
+ .session-delete-btn {
447
+ display: none;
448
+ align-items: center;
449
+ justify-content: center;
450
+ width: 20px;
451
+ height: 20px;
452
+ background: none;
453
+ border: none;
454
+ border-radius: 4px;
455
+ color: var(--text-secondary);
456
+ cursor: pointer;
457
+ padding: 0;
458
+ transition: color 0.15s, background 0.15s;
459
+ }
460
+
461
+ .session-item:hover .session-delete-btn {
462
+ display: flex;
463
+ }
464
+
465
+ .session-delete-btn:hover {
466
+ color: var(--error);
467
+ background: rgba(239, 68, 68, 0.1);
468
+ }
469
+
470
+ /* ── Delete confirmation dialog ── */
471
+ .delete-confirm-dialog {
472
+ width: 360px;
473
+ background: var(--bg-secondary);
474
+ border: 1px solid var(--border);
475
+ border-radius: 12px;
476
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
477
+ }
478
+
479
+ .delete-confirm-header {
480
+ padding: 12px 16px;
481
+ font-size: 0.95rem;
482
+ font-weight: 600;
483
+ border-bottom: 1px solid var(--border);
484
+ }
485
+
486
+ .delete-confirm-body {
487
+ padding: 16px;
488
+ font-size: 0.88rem;
489
+ line-height: 1.5;
490
+ }
491
+
492
+ .delete-confirm-body p {
493
+ margin-bottom: 6px;
494
+ }
495
+
496
+ .delete-confirm-title {
497
+ font-weight: 500;
498
+ color: var(--text-primary);
499
+ overflow: hidden;
500
+ text-overflow: ellipsis;
501
+ white-space: nowrap;
502
+ padding: 6px 10px;
503
+ background: var(--bg-tertiary);
504
+ border-radius: 6px;
505
+ }
506
+
507
+ .delete-confirm-warning {
508
+ font-size: 0.78rem;
509
+ color: var(--text-secondary);
510
+ margin-top: 4px;
511
+ }
512
+
513
+ .delete-confirm-footer {
514
+ padding: 10px 16px;
515
+ display: flex;
516
+ justify-content: flex-end;
517
+ gap: 8px;
518
+ border-top: 1px solid var(--border);
519
+ }
520
+
521
+ .delete-confirm-btn {
522
+ padding: 6px 20px;
523
+ background: var(--error);
524
+ color: #fff;
525
+ border: none;
526
+ border-radius: 8px;
527
+ font-size: 0.85rem;
528
+ font-weight: 600;
529
+ cursor: pointer;
530
+ transition: background 0.15s;
531
+ }
532
+
533
+ .delete-confirm-btn:hover {
534
+ background: #dc2626;
441
535
  }
442
536
 
443
537
  /* ── Chat area (message list + input) ── */
@@ -1449,6 +1543,79 @@ body {
1449
1543
  cursor: not-allowed;
1450
1544
  }
1451
1545
 
1546
+ .sidebar-workdir-actions {
1547
+ display: flex;
1548
+ gap: 4px;
1549
+ }
1550
+
1551
+ /* ── Workdir history dropdown ── */
1552
+ .workdir-history-dropdown {
1553
+ margin-top: 6px;
1554
+ border: 1px solid var(--border);
1555
+ border-radius: 6px;
1556
+ background: var(--bg-secondary);
1557
+ max-height: 200px;
1558
+ overflow-y: auto;
1559
+ }
1560
+
1561
+ .workdir-history-item {
1562
+ display: flex;
1563
+ align-items: center;
1564
+ padding: 6px 8px;
1565
+ font-size: 0.75rem;
1566
+ font-family: 'SF Mono', 'Fira Code', Consolas, monospace;
1567
+ color: var(--text-primary);
1568
+ cursor: pointer;
1569
+ transition: background 0.1s;
1570
+ gap: 4px;
1571
+ }
1572
+
1573
+ .workdir-history-item:hover {
1574
+ background: var(--bg-tertiary);
1575
+ }
1576
+
1577
+ .workdir-history-item.active {
1578
+ color: var(--accent);
1579
+ font-weight: 600;
1580
+ }
1581
+
1582
+ .workdir-history-item + .workdir-history-item {
1583
+ border-top: 1px solid var(--border);
1584
+ }
1585
+
1586
+ .workdir-history-path {
1587
+ flex: 1;
1588
+ overflow: hidden;
1589
+ text-overflow: ellipsis;
1590
+ white-space: nowrap;
1591
+ }
1592
+
1593
+ .workdir-history-remove {
1594
+ flex-shrink: 0;
1595
+ display: none;
1596
+ align-items: center;
1597
+ justify-content: center;
1598
+ width: 18px;
1599
+ height: 18px;
1600
+ background: none;
1601
+ border: none;
1602
+ border-radius: 3px;
1603
+ color: var(--text-secondary);
1604
+ font-size: 0.85rem;
1605
+ cursor: pointer;
1606
+ padding: 0;
1607
+ line-height: 1;
1608
+ }
1609
+
1610
+ .workdir-history-item:hover .workdir-history-remove {
1611
+ display: flex;
1612
+ }
1613
+
1614
+ .workdir-history-remove:hover {
1615
+ color: var(--error);
1616
+ background: rgba(239, 68, 68, 0.1);
1617
+ }
1618
+
1452
1619
  /* ── Folder Picker Modal ── */
1453
1620
  .folder-picker-overlay {
1454
1621
  position: fixed;