daedalus-cli 3.75.0 → 3.76.1

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