daedalus-cli 3.74.0 → 3.76.0

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +1 -1
  3. package/dist/agents/roles.d.ts.map +1 -1
  4. package/dist/agents/roles.js +4 -1
  5. package/dist/agents/roles.js.map +1 -1
  6. package/dist/commands/webui.js +5 -5
  7. package/dist/commands/webui.js.map +1 -1
  8. package/dist/formatting.d.ts +1 -1
  9. package/dist/formatting.d.ts.map +1 -1
  10. package/dist/formatting.js +6 -2
  11. package/dist/formatting.js.map +1 -1
  12. package/dist/marathon/engine.js +1 -1
  13. package/dist/marathon/engine.js.map +1 -1
  14. package/dist/marathon/evaluator.d.ts +1 -0
  15. package/dist/marathon/evaluator.d.ts.map +1 -1
  16. package/dist/marathon/evaluator.js +33 -5
  17. package/dist/marathon/evaluator.js.map +1 -1
  18. package/dist/marathon/planner.d.ts.map +1 -1
  19. package/dist/marathon/planner.js +2 -1
  20. package/dist/marathon/planner.js.map +1 -1
  21. package/dist/repl.js +1 -1
  22. package/dist/repl.js.map +1 -1
  23. package/dist/tools/builtin/files.d.ts.map +1 -1
  24. package/dist/tools/builtin/files.js +11 -1
  25. package/dist/tools/builtin/files.js.map +1 -1
  26. package/dist/webui/install.test.d.ts +2 -0
  27. package/dist/webui/install.test.d.ts.map +1 -0
  28. package/dist/webui/install.test.js +43 -0
  29. package/dist/webui/install.test.js.map +1 -0
  30. package/dist/webui/public/apple-touch-icon.png +0 -0
  31. package/dist/webui/public/icon-192.png +0 -0
  32. package/dist/webui/public/icon-512.png +0 -0
  33. package/dist/webui/public/index.html +116 -32
  34. package/dist/webui/public/logo.png +0 -0
  35. package/dist/webui/public/manifest.json +25 -0
  36. package/dist/webui/public/script.js +404 -9
  37. package/dist/webui/public/styles.css +773 -3
  38. package/dist/webui/public/sw.js +81 -0
  39. package/dist/webui/public/types.d.ts +9 -0
  40. package/dist/webui/public/types.d.ts.map +1 -0
  41. package/dist/webui/public/types.js +2 -0
  42. package/dist/webui/public/types.js.map +1 -0
  43. package/dist/webui/public/types.ts +8 -0
  44. package/dist/webui/qr.d.ts +23 -0
  45. package/dist/webui/qr.d.ts.map +1 -0
  46. package/dist/webui/qr.js +60 -0
  47. package/dist/webui/qr.js.map +1 -0
  48. package/dist/webui/qr.test.d.ts +2 -0
  49. package/dist/webui/qr.test.d.ts.map +1 -0
  50. package/dist/webui/qr.test.js +34 -0
  51. package/dist/webui/qr.test.js.map +1 -0
  52. package/dist/webui/server.d.ts +3 -2
  53. package/dist/webui/server.d.ts.map +1 -1
  54. package/dist/webui/server.js +99 -35
  55. package/dist/webui/server.js.map +1 -1
  56. package/dist/webui/server.test.js +33 -1
  57. package/dist/webui/server.test.js.map +1 -1
  58. package/dist/webui/styles.test.d.ts +2 -0
  59. package/dist/webui/styles.test.d.ts.map +1 -0
  60. package/dist/webui/styles.test.js +19 -0
  61. package/dist/webui/styles.test.js.map +1 -0
  62. package/dist/webui/ws.d.ts +17 -0
  63. package/dist/webui/ws.d.ts.map +1 -0
  64. package/dist/webui/ws.js +57 -0
  65. package/dist/webui/ws.js.map +1 -0
  66. package/dist/webui/ws.test.d.ts +2 -0
  67. package/dist/webui/ws.test.d.ts.map +1 -0
  68. package/dist/webui/ws.test.js +66 -0
  69. package/dist/webui/ws.test.js.map +1 -0
  70. package/package.json +4 -1
@@ -1,3 +1,38 @@
1
+ // Service worker registration
2
+ if ('serviceWorker' in navigator) {
3
+ navigator.serviceWorker.register('/sw.js')
4
+ .then(registration => {
5
+ console.log('[script.js] Service worker registered successfully:', registration);
6
+
7
+ registration.addEventListener('updatefound', () => {
8
+ console.log('[script.js] Service worker update found');
9
+ const newWorker = registration.installing;
10
+ if (newWorker) {
11
+ newWorker.addEventListener('statechange', event => {
12
+ if (event.target.state === 'installed') {
13
+ if (navigator.serviceWorker.controller) {
14
+ console.log('[script.js] New service worker installed, page will reload on next navigation');
15
+ addLog('Service worker update available. Page will reload on next visit.');
16
+ }
17
+ }
18
+ });
19
+ }
20
+ });
21
+ })
22
+ .catch(error => {
23
+ console.warn('[script.js] Service worker registration failed:', error);
24
+ });
25
+
26
+ let refreshing = false;
27
+ navigator.serviceWorker.addEventListener('controllerchange', () => {
28
+ if (!refreshing) {
29
+ refreshing = true;
30
+ console.log('[script.js] Service worker controller changed, reloading page');
31
+ window.location.reload();
32
+ }
33
+ });
34
+ }
35
+
1
36
  const statusEl = document.getElementById('connection-status');
2
37
  const lastUpdateEl = document.getElementById('last-update');
3
38
  const logContainer = document.getElementById('log-container');
@@ -173,6 +208,7 @@ function attachCliFooter(msgEl, modelName, toolCount, durationMs, tokenCount) {
173
208
  msgEl.appendChild(footer);
174
209
  }
175
210
 
211
+ let userProfileName = 'YOU';
176
212
  let activeAssistantBody = null;
177
213
  let activeAssistantMsgEl = null;
178
214
  let thinkingEl = null;
@@ -191,7 +227,7 @@ function addChatMessage(role, text, roleBadge = null, imageBase64 = null, timest
191
227
 
192
228
  const sender = document.createElement('span');
193
229
  sender.className = 'sender';
194
- sender.textContent = role === 'user' ? 'YOU' : 'DAEDALUS';
230
+ sender.textContent = role === 'user' ? userProfileName : 'DAEDALUS';
195
231
  header.appendChild(sender);
196
232
 
197
233
  if (roleBadge) {
@@ -286,7 +322,7 @@ function renderToolStart(toolName) {
286
322
 
287
323
  const icon = document.createElement('span');
288
324
  icon.className = 'tool-tree-icon';
289
- icon.textContent = '';
325
+ icon.innerHTML = '<svg style="width: 12px; height: 12px; display: inline-block; vertical-align: -1px;" viewBox="0 0 24 24" fill="currentColor"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>';
290
326
 
291
327
  const label = document.createElement('span');
292
328
  label.className = 'tool-tree-label';
@@ -322,16 +358,15 @@ function renderToolStart(toolName) {
322
358
  details.className = 'tool-tree-details hidden';
323
359
 
324
360
  header.addEventListener('click', () => {
325
- tree.classList.toggle('open');
326
- details.classList.toggle('hidden');
361
+ const isHidden = details.classList.toggle('hidden');
362
+ chevron.style.transform = isHidden ? 'rotate(0deg)' : 'rotate(90deg)';
327
363
  });
328
364
 
329
365
  tree.appendChild(header);
330
366
  tree.appendChild(details);
331
367
  chatMessages.appendChild(tree);
332
- chatMessages.scrollTop = chatMessages.scrollHeight;
333
-
334
368
  currentToolTreeEl = tree;
369
+ chatMessages.scrollTop = chatMessages.scrollHeight;
335
370
  }
336
371
 
337
372
  const label = currentToolTreeEl.querySelector('.tool-tree-label');
@@ -364,7 +399,7 @@ function renderToolResult(toolName, resultSummary) {
364
399
 
365
400
  const summary = document.createElement('span');
366
401
  summary.className = 'tool-tree-item-summary';
367
- summary.textContent = resultSummary ? `(${resultSummary})` : 'completed';
402
+ summary.textContent = resultSummary ? `(${resultSummary})` : 'completed';
368
403
 
369
404
  item.appendChild(rail);
370
405
  item.appendChild(name);
@@ -380,7 +415,7 @@ function renderToolResult(toolName, resultSummary) {
380
415
  const badge = currentToolTreeEl.querySelector('.tool-tree-badge');
381
416
  if (badge) {
382
417
  badge.className = 'tool-tree-badge completed';
383
- badge.textContent = 'DONE';
418
+ badge.textContent = 'DONE';
384
419
  }
385
420
 
386
421
  currentToolTreeEl.classList.remove('running');
@@ -1216,12 +1251,372 @@ if (modelModal) {
1216
1251
  });
1217
1252
  }
1218
1253
 
1254
+ // ─────────────────────────────────────────────────────────────
1255
+ // QR Code Pairing Modal — M-5 / M-6
1256
+ // ─────────────────────────────────────────────────────────────
1257
+ const qrPairBtn = document.getElementById('qr-pair-btn');
1258
+ const qrModal = document.getElementById('qr-modal');
1259
+ const qrModalClose = document.getElementById('qr-modal-close');
1260
+ const qrImage = document.getElementById('qr-image');
1261
+ const qrWsUrl = document.getElementById('qr-ws-url');
1262
+
1263
+ async function openQrModal() {
1264
+ if (qrModal) {
1265
+ qrModal.classList.remove('hidden');
1266
+ let webUrl = window.location.origin;
1267
+ if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
1268
+ try {
1269
+ const res = await fetch('/api/pairing-url');
1270
+ if (res.ok) {
1271
+ const data = await res.json();
1272
+ if (data.url) webUrl = data.url;
1273
+ }
1274
+ } catch {}
1275
+ }
1276
+ if (qrWsUrl) {
1277
+ qrWsUrl.textContent = webUrl;
1278
+ }
1279
+ if (qrImage) {
1280
+ qrImage.src = `/api/qr?url=${encodeURIComponent(webUrl)}&t=${Date.now()}`;
1281
+ }
1282
+ addLog(`QR pairing portal opened: <strong>${webUrl}</strong>`);
1283
+ }
1284
+ }
1285
+
1286
+ function closeQrModal() {
1287
+ if (qrModal) {
1288
+ qrModal.classList.add('hidden');
1289
+ }
1290
+ }
1291
+
1292
+ if (qrPairBtn) {
1293
+ qrPairBtn.addEventListener('click', openQrModal);
1294
+ }
1295
+
1296
+ if (qrModalClose) {
1297
+ qrModalClose.addEventListener('click', closeQrModal);
1298
+ }
1299
+
1300
+ if (qrModal) {
1301
+ qrModal.addEventListener('click', (e) => {
1302
+ if (e.target === qrModal) closeQrModal();
1303
+ });
1304
+ }
1305
+
1306
+ async function loadUserProfile() {
1307
+ try {
1308
+ const res = await fetch('/api/profile');
1309
+ if (!res.ok) return;
1310
+ const data = await res.json();
1311
+ if (data.name && data.name.trim()) {
1312
+ userProfileName = data.name.trim().toUpperCase();
1313
+ document.querySelectorAll('.chat-msg.user .sender').forEach(el => {
1314
+ el.textContent = userProfileName;
1315
+ });
1316
+ }
1317
+ } catch {}
1318
+ }
1319
+
1320
+ // ─────────────────────────────────────────────────────────────
1321
+ // Touch-Optimized UI — M-4: TouchTargetConfig interface
1322
+ // ─────────────────────────────────────────────────────────────
1323
+ /**
1324
+ * @typedef {Object} TouchTargetConfig
1325
+ * @property {string} selector - CSS selector for the interactive element
1326
+ * @property {number} [minSizePx=48] - Minimum width and height in pixels
1327
+ * @property {string} [touchAction='manipulation'] - touch-action CSS value
1328
+ */
1329
+
1330
+ /**
1331
+ * Apply touch optimizations by injecting CSS for minimum tap targets
1332
+ * and touch-action on elements matching the given selectors.
1333
+ * @param {TouchTargetConfig[]} configs
1334
+ */
1335
+ function applyTouchOptimizations(configs) {
1336
+ if (!configs || configs.length === 0) return;
1337
+
1338
+ const styleId = 'touch-optimization-styles';
1339
+ let styleEl = document.getElementById(styleId);
1340
+
1341
+ if (!styleEl) {
1342
+ styleEl = document.createElement('style');
1343
+ styleEl.id = styleId;
1344
+ document.head.appendChild(styleEl);
1345
+ }
1346
+
1347
+ const rules = configs.map(function (cfg) {
1348
+ var minSize = cfg.minSizePx !== undefined ? cfg.minSizePx : 48;
1349
+ var touchAction = cfg.touchAction !== undefined ? cfg.touchAction : 'manipulation';
1350
+ var sel = cfg.selector;
1351
+ return [
1352
+ sel + ' {',
1353
+ ' min-width: ' + minSize + 'px;',
1354
+ ' min-height: ' + minSize + 'px;',
1355
+ ' touch-action: ' + touchAction + ';',
1356
+ '}',
1357
+ ].join('\n');
1358
+ });
1359
+
1360
+ styleEl.textContent = rules.join('\n');
1361
+ }
1362
+
1363
+ // ─────────────────────────────────────────────────────────────
1364
+ // Critical element pointerdown listeners — M-4
1365
+ // ─────────────────────────────────────────────────────────────
1366
+ /**
1367
+ * Add both click and pointerdown listeners to ensure tactile responsiveness.
1368
+ * pointerdown fires before click and works for both touch and mouse.
1369
+ * @param {HTMLElement|null} el
1370
+ * @param {() => void} handler
1371
+ */
1372
+ function addDualInteractionListeners(el, handler) {
1373
+ if (!el) return;
1374
+ el.addEventListener('pointerdown', function (e) {
1375
+ e.preventDefault();
1376
+ handler();
1377
+ });
1378
+ // Keep click for keyboard / accessibility
1379
+ if (!el.dataset._hasClickBound) {
1380
+ el.addEventListener('click', handler);
1381
+ el.dataset._hasClickBound = 'true';
1382
+ }
1383
+ }
1384
+
1385
+ // Wire up pointerdown on all critical interactive elements
1386
+ document.addEventListener('DOMContentLoaded', function () {
1387
+ // #active-model-badge — opens model selection modal
1388
+ addDualInteractionListeners(
1389
+ document.getElementById('active-model-badge'),
1390
+ openModelModal
1391
+ );
1392
+
1393
+ // #clear-log — clears the log panel
1394
+ addDualInteractionListeners(
1395
+ document.getElementById('clear-log'),
1396
+ function () {
1397
+ if (logContainer) logContainer.innerHTML = '';
1398
+ }
1399
+ );
1400
+
1401
+ // #new-chat-btn — starts a new chat session
1402
+ addDualInteractionListeners(
1403
+ document.getElementById('new-chat-btn'),
1404
+ function () {
1405
+ if (chatMessages) chatMessages.innerHTML = '';
1406
+ addLog('New rite initiated. The cosmos awaits your query.');
1407
+ }
1408
+ );
1409
+
1410
+ // #attach-btn — triggers file attachment
1411
+ addDualInteractionListeners(
1412
+ document.getElementById('attach-btn'),
1413
+ function () {
1414
+ var fileInput = document.getElementById('file-input');
1415
+ if (fileInput) fileInput.click();
1416
+ }
1417
+ );
1418
+
1419
+ // #send-btn — submits the chat form
1420
+ addDualInteractionListeners(
1421
+ document.getElementById('send-btn'),
1422
+ function () {
1423
+ var form = document.getElementById('chat-form');
1424
+ if (form) form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
1425
+ }
1426
+ );
1427
+
1428
+ // #model-modal-close — closes model selection modal
1429
+ addDualInteractionListeners(
1430
+ document.getElementById('model-modal-close'),
1431
+ closeModelModal
1432
+ );
1433
+
1434
+ // #qr-pair-btn — opens QR pairing modal
1435
+ addDualInteractionListeners(
1436
+ document.getElementById('qr-pair-btn'),
1437
+ openQrModal
1438
+ );
1439
+
1440
+ // #qr-modal-close — closes QR pairing modal
1441
+ addDualInteractionListeners(
1442
+ document.getElementById('qr-modal-close'),
1443
+ closeQrModal
1444
+ );
1445
+
1446
+ // Apply global touch optimizations via CSS
1447
+ applyTouchOptimizations([
1448
+ { selector: 'button', minSizePx: 48, touchAction: 'manipulation' },
1449
+ { selector: 'a[href]', minSizePx: 48, touchAction: 'manipulation' },
1450
+ { selector: '.model-pill', minSizePx: 44, touchAction: 'manipulation' },
1451
+ { selector: '.clear-btn', minSizePx: 48, touchAction: 'manipulation' },
1452
+ { selector: '.chat-header-btn', minSizePx: 48, touchAction: 'manipulation' },
1453
+ { selector: '.attach-btn', minSizePx: 44, touchAction: 'manipulation' },
1454
+ { selector: '#send-btn', minSizePx: 48, touchAction: 'manipulation' },
1455
+ { selector: '.model-option-card', minSizePx: 48, touchAction: 'manipulation' },
1456
+ { selector: '.code-copy-btn', minSizePx: 36, touchAction: 'manipulation' },
1457
+ ]);
1458
+ });
1459
+
1460
+ // ─────────────────────────────────────────────────────────────
1461
+ // WebSocket & Milestone Push Notifications — M-7
1462
+ // ─────────────────────────────────────────────────────────────
1463
+ let wsClient = null;
1464
+ let wsReconnectTimer = null;
1465
+
1466
+ function showMilestoneNotification(payload) {
1467
+ if (!payload || payload.type !== 'milestone') return;
1468
+
1469
+ const title = `[DAEDALUS] Milestone ${payload.id ? payload.id.toUpperCase() : ''}: ${payload.title || 'Update'}`;
1470
+ const options = {
1471
+ body: payload.summary || `Status: ${(payload.status || 'passed').toUpperCase()}${payload.score !== undefined ? ` (${payload.score}/100)` : ''}`,
1472
+ icon: '/favicon.svg',
1473
+ badge: '/favicon.svg',
1474
+ tag: `daedalus-milestone-${payload.id || Date.now()}`,
1475
+ renotify: true,
1476
+ };
1477
+
1478
+ if ('Notification' in window) {
1479
+ if (Notification.permission === 'granted') {
1480
+ try {
1481
+ new Notification(title, options);
1482
+ } catch {}
1483
+ } else if (Notification.permission !== 'denied') {
1484
+ Notification.requestPermission().then(function (permission) {
1485
+ if (permission === 'granted') {
1486
+ try {
1487
+ new Notification(title, options);
1488
+ } catch {}
1489
+ }
1490
+ });
1491
+ }
1492
+ }
1493
+
1494
+ // Also log into Oracle system log
1495
+ addLog(`Milestone <strong>${payload.id || ''}</strong>: ${payload.title} — <em>${(payload.status || 'passed').toUpperCase()}</em>`);
1496
+ }
1497
+
1498
+ function connectWebSocket() {
1499
+ if (wsClient && (wsClient.readyState === WebSocket.OPEN || wsClient.readyState === WebSocket.CONNECTING)) {
1500
+ return;
1501
+ }
1502
+
1503
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
1504
+ const wsUrl = `${protocol}//${window.location.host}`;
1505
+
1506
+ try {
1507
+ wsClient = new WebSocket(wsUrl);
1508
+
1509
+ wsClient.onopen = function () {
1510
+ if (wsReconnectTimer) {
1511
+ clearTimeout(wsReconnectTimer);
1512
+ wsReconnectTimer = null;
1513
+ }
1514
+ };
1515
+
1516
+ wsClient.onmessage = function (event) {
1517
+ try {
1518
+ const data = JSON.parse(event.data);
1519
+ if (data.type === 'milestone') {
1520
+ showMilestoneNotification(data);
1521
+ }
1522
+ } catch {}
1523
+ };
1524
+
1525
+ wsClient.onclose = function () {
1526
+ wsClient = null;
1527
+ if (!wsReconnectTimer) {
1528
+ wsReconnectTimer = setTimeout(connectWebSocket, 3000);
1529
+ }
1530
+ };
1531
+
1532
+ wsClient.onerror = function () {
1533
+ try { wsClient.close(); } catch {}
1534
+ };
1535
+ } catch {}
1536
+ }
1537
+
1538
+ // ─────────────────────────────────────────────────────────────
1539
+ // Bootstrapping
1540
+ // ─────────────────────────────────────────────────────────────
1219
1541
  connectSSE();
1542
+ connectWebSocket();
1220
1543
  loadModels();
1544
+ loadUserProfile();
1221
1545
 
1222
- setInterval(() => {
1546
+ setInterval(function () {
1223
1547
  if (lastUpdateEl) {
1224
1548
  lastUpdateEl.textContent = new Date().toLocaleTimeString();
1225
1549
  }
1226
1550
  }, 1000);
1227
1551
 
1552
+ // ─────────────────────────────────────────────────────────────
1553
+ // PWA Install Prompt Handling — M-8
1554
+ // ─────────────────────────────────────────────────────────────
1555
+ let deferredPrompt = null;
1556
+ const installBanner = document.getElementById('install-banner');
1557
+ const installButton = document.getElementById('install-button');
1558
+ const installBannerCloseButtons = document.querySelectorAll('.install-banner-close, .install-banner-dismiss-btn');
1559
+
1560
+ function showInstallBanner() {
1561
+ if (installBanner) {
1562
+ installBanner.classList.remove('hidden');
1563
+ }
1564
+ }
1565
+
1566
+ function hideInstallBanner() {
1567
+ if (installBanner) {
1568
+ installBanner.classList.add('hidden');
1569
+ }
1570
+ }
1571
+
1572
+ window.addEventListener('beforeinstallprompt', function (event) {
1573
+ event.preventDefault();
1574
+ deferredPrompt = event;
1575
+ console.log('[script.js] beforeinstallprompt captured, install banner ready');
1576
+ showInstallBanner();
1577
+ });
1578
+
1579
+ if (installButton) {
1580
+ installButton.addEventListener('click', async function () {
1581
+ if (!deferredPrompt) {
1582
+ console.warn('[script.js] No deferred install prompt available');
1583
+ return;
1584
+ }
1585
+
1586
+ try {
1587
+ const result = await deferredPrompt.prompt();
1588
+ const choice = result && result.outcome ? result.outcome : 'unknown';
1589
+
1590
+ if (choice === 'accepted') {
1591
+ console.log('[script.js] User accepted PWA install prompt');
1592
+ addLog('PWA install accepted. Daedalus will be added to your home screen.');
1593
+ } else {
1594
+ console.log('[script.js] User dismissed PWA install prompt:', choice);
1595
+ addLog('PWA install dismissed by user.');
1596
+ }
1597
+ } catch (err) {
1598
+ console.error('[script.js] PWA install prompt failed:', err);
1599
+ addLog('PWA install prompt encountered an error.');
1600
+ } finally {
1601
+ deferredPrompt = null;
1602
+ hideInstallBanner();
1603
+ }
1604
+ });
1605
+ }
1606
+
1607
+ installBannerCloseButtons.forEach(btn => {
1608
+ btn.addEventListener('click', function () {
1609
+ console.log('[script.js] Install banner dismissed by user');
1610
+ deferredPrompt = null;
1611
+ hideInstallBanner();
1612
+ });
1613
+ });
1614
+
1615
+ window.addEventListener('appinstalled', function () {
1616
+ console.log('[script.js] PWA installed successfully');
1617
+ addLog('Daedalus has been installed as an app.');
1618
+ deferredPrompt = null;
1619
+ hideInstallBanner();
1620
+ });
1621
+
1622
+