daedalus-cli 3.75.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 (64) hide show
  1. package/CHANGELOG.md +7 -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 +387 -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 +57 -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,58 @@ 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 {}
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
+
1220
1306
  async function loadUserProfile() {
1221
1307
  try {
1222
1308
  const res = await fetch('/api/profile');
@@ -1231,13 +1317,306 @@ async function loadUserProfile() {
1231
1317
  } catch {}
1232
1318
  }
1233
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
+ // ─────────────────────────────────────────────────────────────
1234
1541
  connectSSE();
1542
+ connectWebSocket();
1235
1543
  loadModels();
1236
1544
  loadUserProfile();
1237
1545
 
1238
- setInterval(() => {
1546
+ setInterval(function () {
1239
1547
  if (lastUpdateEl) {
1240
1548
  lastUpdateEl.textContent = new Date().toLocaleTimeString();
1241
1549
  }
1242
1550
  }, 1000);
1243
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
+