@puddle-code/cli 0.2.21 → 0.3.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 (142) hide show
  1. package/CHANGELOG.md +7 -4
  2. package/README.md +4 -0
  3. package/dist/host-control.mjs +202 -22
  4. package/dist/index.js +223 -29
  5. package/dist/install-cli.sh +13 -2
  6. package/dist/public/assets/App-XpQccO6u.js +2 -0
  7. package/dist/public/assets/{Dashboard-BhAek_r4.js → Dashboard-DMEBtYwv.js} +1 -1
  8. package/dist/public/assets/{EditorTabClose-CSKAGWsA.js → EditorTabClose-PbgNs_rN.js} +1 -1
  9. package/dist/public/assets/{FindWidget-B6dVpkKr.js → FindWidget-ZDHywlX4.js} +1 -1
  10. package/dist/public/assets/LazyTerminal-DRgaFViU.js +2 -0
  11. package/dist/public/assets/{ModelRefcount-CrpDNwTn.js → ModelRefcount-CI3cYeQp.js} +1 -1
  12. package/dist/public/assets/{NewProjectDialog-D-hVQlA4.js → NewProjectDialog-Bhfj_sYl.js} +1 -1
  13. package/dist/public/assets/{PaneEditorBody--h9jLt1e.js → PaneEditorBody-Bz74yrKU.js} +3 -3
  14. package/dist/public/assets/{PdfViewer-CNzO040W.js → PdfViewer-73_UlYnM.js} +2 -2
  15. package/dist/public/assets/{SettingsDialog-DaxYdggs.js → SettingsDialog-DJmvzJHQ.js} +2 -2
  16. package/dist/public/assets/{Terminal-DQL8GETy.js → Terminal-BwuoWKED.js} +5 -5
  17. package/dist/public/assets/{Workspace-BDDSmIKE.js → Workspace-DPL1IKA6.js} +4 -4
  18. package/dist/public/assets/{abnfDiagram-VCTEODGH-Dj6CI3Nv.js → abnfDiagram-VCTEODGH-AawOhIcj.js} +1 -1
  19. package/dist/public/assets/architecture-7GRP2DOG-BbAArV9u.js +1 -0
  20. package/dist/public/assets/{architectureDiagram-5GKGNRK7-7n7HJQxV.js → architectureDiagram-5GKGNRK7-D9vP5DO8.js} +1 -1
  21. package/dist/public/assets/{blockDiagram-I7D4REHJ-Cmsxv8OS.js → blockDiagram-I7D4REHJ-Cm9i3cTT.js} +1 -1
  22. package/dist/public/assets/{buffer-store-NeRSjzSg.js → buffer-store-BIpSx_-b.js} +2 -2
  23. package/dist/public/assets/{button-hdghxls7.js → button-CgZcwDne.js} +1 -1
  24. package/dist/public/assets/{c4Diagram-7LVT6UL2-D23ryIme.js → c4Diagram-7LVT6UL2-BiN1-NKB.js} +1 -1
  25. package/dist/public/assets/channel-CUWW1WRo.js +1 -0
  26. package/dist/public/assets/{chunk-2E4U76K2-usroR3SM.js → chunk-2E4U76K2-BxAMvG2q.js} +1 -1
  27. package/dist/public/assets/{chunk-4HAMMTFA-B4mxD8f1.js → chunk-4HAMMTFA-D4UgoMR5.js} +1 -1
  28. package/dist/public/assets/{chunk-75Z2AOVW-BSDNHsOk.js → chunk-75Z2AOVW-ViSC5KeO.js} +1 -1
  29. package/dist/public/assets/{chunk-CLGD4ZFX-DpUm0L6C.js → chunk-CLGD4ZFX-C1obqh7u.js} +1 -1
  30. package/dist/public/assets/{chunk-DU6HZSFF-Cin9bFEJ.js → chunk-DU6HZSFF-Dd9xUi9L.js} +1 -1
  31. package/dist/public/assets/{chunk-F27PBJKO-BWPIZmRh.js → chunk-F27PBJKO-S5xJlLKg.js} +1 -1
  32. package/dist/public/assets/{chunk-GMAD6QVW-eS27qQYi.js → chunk-GMAD6QVW-DXbO7ojE.js} +1 -1
  33. package/dist/public/assets/{chunk-GVQU2GXP-DvZzuy44.js → chunk-GVQU2GXP-vZ5Z6XwC.js} +1 -1
  34. package/dist/public/assets/{chunk-IMKFNOWR-CKfEqBn8.js → chunk-IMKFNOWR-BGcGac3f.js} +1 -1
  35. package/dist/public/assets/{chunk-L3NEJ4N5-Gq2sL0Oq.js → chunk-L3NEJ4N5-BfxBma0T.js} +1 -1
  36. package/dist/public/assets/{chunk-OSK3NFVY-DSHFDm2K.js → chunk-OSK3NFVY-BaWjSKfn.js} +1 -1
  37. package/dist/public/assets/{chunk-P2QGCYS3-B0b6PsO0.js → chunk-P2QGCYS3-Ce8Ukv1J.js} +1 -1
  38. package/dist/public/assets/{chunk-POPQ4Y6H-B_whPWNN.js → chunk-POPQ4Y6H-lrRlaehc.js} +1 -1
  39. package/dist/public/assets/{chunk-PWAF6VOD-DfdxoQ_F.js → chunk-PWAF6VOD-DcoK5-E2.js} +1 -1
  40. package/dist/public/assets/{chunk-SHT3W25Y-CHrnqlvt.js → chunk-SHT3W25Y-BWdHabuP.js} +1 -1
  41. package/dist/public/assets/{chunk-SVP7TREG-keTkvLck.js → chunk-SVP7TREG-CfpAvBiO.js} +1 -1
  42. package/dist/public/assets/{chunk-TICWLB2K-ZF0XdA5t.js → chunk-TICWLB2K-BnqQqZIp.js} +1 -1
  43. package/dist/public/assets/{chunk-TLUHSLCS-B3xiw07r.js → chunk-TLUHSLCS-Cd6EShzW.js} +2 -2
  44. package/dist/public/assets/classDiagram-ZZMXUADV-Cve4a5Gc.js +1 -0
  45. package/dist/public/assets/classDiagram-v2-VYDZK3BY-Cve4a5Gc.js +1 -0
  46. package/dist/public/assets/{context-menu-fGutCa7q.js → context-menu-CtKnUzHa.js} +1 -1
  47. package/dist/public/assets/{copy-B38GDFbv.js → copy-Xz4fK2YI.js} +1 -1
  48. package/dist/public/assets/{cssMode-BBPxRrZH.js → cssMode-CNBJ-DrN.js} +1 -1
  49. package/dist/public/assets/{cynefin-OW5HDTMX-BKwXHhp6.js → cynefin-OW5HDTMX-C1-MjjCZ.js} +1 -1
  50. package/dist/public/assets/{cynefinDiagram-5FMLGOSQ-B59dmX8D.js → cynefinDiagram-5FMLGOSQ-xt5hQJ88.js} +1 -1
  51. package/dist/public/assets/{dagre-GXQ25YYZ-BysK_yGg.js → dagre-GXQ25YYZ-Bgahh86t.js} +1 -1
  52. package/dist/public/assets/{diagram-S7CK7UJ4-BTvgOprS.js → diagram-S7CK7UJ4-Djip7YHP.js} +1 -1
  53. package/dist/public/assets/{diagram-UQ7AKVKN-C1FXQfFT.js → diagram-UQ7AKVKN-StD7DYFa.js} +1 -1
  54. package/dist/public/assets/{diagram-VSXAHHWV-CTub3LLw.js → diagram-VSXAHHWV-C6W2XMTF.js} +1 -1
  55. package/dist/public/assets/{diagram-VX7I27RA-C8YSQsst.js → diagram-VX7I27RA-C5rBIjxM.js} +1 -1
  56. package/dist/public/assets/{diagram-Z3DM3KII-zBY2ZT-7.js → diagram-Z3DM3KII-DDzmZnRU.js} +1 -1
  57. package/dist/public/assets/dialog-_JRACgt4.js +41 -0
  58. package/dist/public/assets/disclosure-DBXM9uBm.js +1 -0
  59. package/dist/public/assets/{dist-DWt3BCrg.js → dist-Bl7vUoam.js} +1 -1
  60. package/dist/public/assets/{dist-1iWQMdtt.js → dist-CzEdee3K.js} +1 -1
  61. package/dist/public/assets/{ebnfDiagram-PWID7BFC-DZbYMMq9.js → ebnfDiagram-PWID7BFC-TJlh5Fea.js} +1 -1
  62. package/dist/public/assets/{erDiagram-RLTQ6QDP-BufFqh6K.js → erDiagram-RLTQ6QDP-BSNVCw_x.js} +1 -1
  63. package/dist/public/assets/{errors-0CwhraNS.js → errors-BzfxU_Kw.js} +1 -1
  64. package/dist/public/assets/eventmodeling-NTZA5JFV-DZ8G1RJ1.js +1 -0
  65. package/dist/public/assets/flowDiagram-HODETNUW-Dd9WpTU6.js +1 -0
  66. package/dist/public/assets/{ganttDiagram-EL5Y4UJY-CzBnAVfV.js → ganttDiagram-EL5Y4UJY-BGFNgCoM.js} +1 -1
  67. package/dist/public/assets/{gitGraph-4MIJSDKK-DzkiKDz3.js → gitGraph-4MIJSDKK-Du3WBCxU.js} +1 -1
  68. package/dist/public/assets/{gitGraphDiagram-WWUBYQGX-Cg5zqV71.js → gitGraphDiagram-WWUBYQGX-Dq_yAVmM.js} +1 -1
  69. package/dist/public/assets/{htmlMode-HP2zLptL.js → htmlMode-BWm5ayBQ.js} +1 -1
  70. package/dist/public/assets/index-BP-EF5G2.js +82 -0
  71. package/dist/public/assets/index-CAvJOpdz.css +2 -0
  72. package/dist/public/assets/{info-A6RAGUB7-DrzRcVCp.js → info-A6RAGUB7-V0hlAV3d.js} +1 -1
  73. package/dist/public/assets/{infoDiagram-27XIBGKW-DKhhQ6Mi.js → infoDiagram-27XIBGKW-DZM1jHkq.js} +1 -1
  74. package/dist/public/assets/{ishikawaDiagram-5VMMS53U-Sxvy6BP4.js → ishikawaDiagram-5VMMS53U-CZlO72MU.js} +1 -1
  75. package/dist/public/assets/{journeyDiagram-3NMN7TZE-DLCDGlAt.js → journeyDiagram-3NMN7TZE-DZZieltZ.js} +1 -1
  76. package/dist/public/assets/{jsonMode-3cqCCYan.js → jsonMode-DA3Di61-.js} +1 -1
  77. package/dist/public/assets/{kanban-definition-UXKFOSKX-BBHezsUK.js → kanban-definition-UXKFOSKX-BAFfgDCD.js} +1 -1
  78. package/dist/public/assets/{line-CXD0KIW6.js → line-DNRJuk5l.js} +1 -1
  79. package/dist/public/assets/{markdown-mermaid-gr78oRak.js → markdown-mermaid-zldCuMow.js} +2 -2
  80. package/dist/public/assets/{math-DD0qgOf_.js → math-Bl0zHVK2.js} +1 -1
  81. package/dist/public/assets/{media-refresh-store-DTLvGgrw.js → media-refresh-store-BemN_6sc.js} +1 -1
  82. package/dist/public/assets/{mermaid-parser.core-CQpv25sY.js → mermaid-parser.core-DpOHgT8D.js} +3 -3
  83. package/dist/public/assets/{mermaid.core-CO3okgIG.js → mermaid.core-DuO1Atct.js} +3 -3
  84. package/dist/public/assets/{mindmap-definition-YA3MSWOX-DnuqRR1y.js → mindmap-definition-YA3MSWOX-Bbgshnhk.js} +1 -1
  85. package/dist/public/assets/notification-delivery-DehaYHWS.js +1 -0
  86. package/dist/public/assets/{packet-AYTQ26CC--9UnHksn.js → packet-AYTQ26CC-H2b8zBMM.js} +1 -1
  87. package/dist/public/assets/{pdf-DFR5TvGJ.js → pdf-DTc2umrm.js} +1 -1
  88. package/dist/public/assets/{pegDiagram-XKGWAZYB-m8JdNNtC.js → pegDiagram-XKGWAZYB-BKdl5Qrn.js} +1 -1
  89. package/dist/public/assets/{pie-WAS4IAKB-GnDinOP5.js → pie-WAS4IAKB-SB8x1Dpp.js} +1 -1
  90. package/dist/public/assets/{pieDiagram-E7YTZNPT-u-NA8MFl.js → pieDiagram-E7YTZNPT-YXMNp-5O.js} +1 -1
  91. package/dist/public/assets/{project-abbrev-Ce2YQx3q.js → project-abbrev-D7aa-LGS.js} +1 -1
  92. package/dist/public/assets/{protocol-support-DM3D3Owq.js → protocol-support-Dm8bEo7Y.js} +1 -1
  93. package/dist/public/assets/{quadrantDiagram-AXDQQJYC-si_KhMBB.js → quadrantDiagram-AXDQQJYC-B2TAChhA.js} +1 -1
  94. package/dist/public/assets/{radar-RG4KPBEZ-CnJwODJt.js → radar-RG4KPBEZ-D2xzXNL8.js} +1 -1
  95. package/dist/public/assets/{railroad-74A4TZTK-Cgg0LWTI.js → railroad-74A4TZTK-CKug6hj8.js} +1 -1
  96. package/dist/public/assets/railroad-abnf-HS5TGJTU-cj9XZHow.js +1 -0
  97. package/dist/public/assets/railroad-ebnf-LZEXJU2U-CCIfyLjg.js +1 -0
  98. package/dist/public/assets/railroad-peg-WCYAUIDC-DkxYDi7O.js +1 -0
  99. package/dist/public/assets/{railroadDiagram-O6MQD6OU-DuQX8qaE.js → railroadDiagram-O6MQD6OU-Bft2oh-d.js} +1 -1
  100. package/dist/public/assets/{requirementDiagram-BXWQKSXE-Chwrn5Tf.js → requirementDiagram-BXWQKSXE-e0GCEfhM.js} +1 -1
  101. package/dist/public/assets/{sankeyDiagram-P5KCCOFB-FpJ30BIQ.js → sankeyDiagram-P5KCCOFB-Csb70YNA.js} +1 -1
  102. package/dist/public/assets/{scroll-restoration-B3J09_am.js → scroll-restoration-D7f4DDsJ.js} +1 -1
  103. package/dist/public/assets/{selection-actions-awCJJCSe.js → selection-actions-D5wKfiBu.js} +1 -1
  104. package/dist/public/assets/{sequenceDiagram-WJ2MYXX4-DVw87Ucm.js → sequenceDiagram-WJ2MYXX4-DcnD8T30.js} +1 -1
  105. package/dist/public/assets/{stateDiagram-D77RDMKH-qmBcs9VH.js → stateDiagram-D77RDMKH-GnwFUlNg.js} +1 -1
  106. package/dist/public/assets/stateDiagram-v2-MP3YSRHH-Bktkoy9c.js +1 -0
  107. package/dist/public/assets/{swimlanes-42K2YHIH-ts5KQGMt.js → swimlanes-42K2YHIH-CPq_eKyi.js} +1 -1
  108. package/dist/public/assets/swimlanesDiagram-VR7AAH4N-BmjIwfwd.js +8 -0
  109. package/dist/public/assets/{switch-DMkSxGf3.js → switch-DZbOKnZ0.js} +1 -1
  110. package/dist/public/assets/{timeline-definition-24CTP7MA-BDL_pzrt.js → timeline-definition-24CTP7MA-DjUPAJJx.js} +1 -1
  111. package/dist/public/assets/{treeView-Q6P3EWNA-B1vMOTkl.js → treeView-Q6P3EWNA-DBuhlxaq.js} +1 -1
  112. package/dist/public/assets/{treemap-WGGIJYW6-wi5T0FIb.js → treemap-WGGIJYW6-3TKwSIeh.js} +1 -1
  113. package/dist/public/assets/{tsMode-CsaS5imR.js → tsMode-BYwFH3pC.js} +1 -1
  114. package/dist/public/assets/{use-ui-state-2VprViGF.js → use-ui-state-BXROVbIw.js} +1 -1
  115. package/dist/public/assets/{vennDiagram-4TSXK5OY-Djvp5-2_.js → vennDiagram-4TSXK5OY-oDrGN_Dh.js} +1 -1
  116. package/dist/public/assets/{view-state-store-rOwEBYKn.js → view-state-store-vUrDt7wu.js} +1 -1
  117. package/dist/public/assets/{wardley-WFR3VGLG-Di-PFH_i.js → wardley-WFR3VGLG-Br5CacuJ.js} +1 -1
  118. package/dist/public/assets/{wardleyDiagram-VM6X3IG4-B5Y8UZiL.js → wardleyDiagram-VM6X3IG4-Cw_uOLXA.js} +1 -1
  119. package/dist/public/assets/{ws-DsytGlkr.js → ws-Ug1Tr2sh.js} +1 -1
  120. package/dist/public/assets/{xychartDiagram-S5SC5T6Z-d-pvxFe2.js → xychartDiagram-S5SC5T6Z-Cl8gNx_J.js} +1 -1
  121. package/dist/public/index.html +2 -2
  122. package/dist/public/notification-worker.js +104 -6
  123. package/dist/puddlang.js +17578 -0
  124. package/package.json +3 -2
  125. package/dist/public/assets/App-NrJUEV9p.js +0 -2
  126. package/dist/public/assets/LazyTerminal-fxF4IO4o.js +0 -2
  127. package/dist/public/assets/architecture-7GRP2DOG-CDcS1iVd.js +0 -1
  128. package/dist/public/assets/channel-B98_B4F2.js +0 -1
  129. package/dist/public/assets/classDiagram-ZZMXUADV-CalLaxUD.js +0 -1
  130. package/dist/public/assets/classDiagram-v2-VYDZK3BY-CalLaxUD.js +0 -1
  131. package/dist/public/assets/dialog-CVAp0B_q.js +0 -41
  132. package/dist/public/assets/disclosure-CWTUvRKU.js +0 -1
  133. package/dist/public/assets/eventmodeling-NTZA5JFV-CC5F0fZk.js +0 -1
  134. package/dist/public/assets/flowDiagram-HODETNUW-Dmt2DAB8.js +0 -1
  135. package/dist/public/assets/index-Bwl8S5Du.css +0 -2
  136. package/dist/public/assets/index-W9vnFL1i.js +0 -82
  137. package/dist/public/assets/notification-delivery-rQp5tkM8.js +0 -1
  138. package/dist/public/assets/railroad-abnf-HS5TGJTU-DPqD3hz7.js +0 -1
  139. package/dist/public/assets/railroad-ebnf-LZEXJU2U-9Jxyrh7e.js +0 -1
  140. package/dist/public/assets/railroad-peg-WCYAUIDC-gr2ZokSI.js +0 -1
  141. package/dist/public/assets/stateDiagram-v2-MP3YSRHH-TK5UygAI.js +0 -1
  142. package/dist/public/assets/swimlanesDiagram-VR7AAH4N-BUMN0oZf.js +0 -8
@@ -1,8 +1,81 @@
1
- /* global self, indexedDB */
1
+ /* global self, indexedDB, crypto, TextEncoder, TextDecoder */
2
2
  /* Notifications only: no fetch interception or caching of host content. */
3
3
  let queue = Promise.resolve();
4
4
  let database;
5
5
  const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
6
+ function targetOf(value) {
7
+ if (
8
+ !value ||
9
+ !/^[0-9a-f]{10}$/.test(value.project) ||
10
+ !uuid.test(value.session) ||
11
+ (value.host !== undefined && !uuid.test(value.host))
12
+ )
13
+ return undefined;
14
+ return {
15
+ project: value.project,
16
+ session: value.session,
17
+ ...(value.host ? { host: value.host } : {}),
18
+ };
19
+ }
20
+ function validContent(value) {
21
+ return (
22
+ value &&
23
+ typeof value.title === 'string' &&
24
+ value.title.length > 0 &&
25
+ value.title.length <= 160 &&
26
+ typeof value.body === 'string' &&
27
+ value.body.length > 0 &&
28
+ value.body.length <= 256 &&
29
+ targetOf(value)
30
+ );
31
+ }
32
+ function bytes(hex) {
33
+ return Uint8Array.from(hex.match(/../g), (byte) => parseInt(byte, 16));
34
+ }
35
+ async function decryptPreview(payload) {
36
+ const preview = payload.preview;
37
+ if (
38
+ !preview ||
39
+ !uuid.test(preview.key) ||
40
+ !/^[a-f0-9]{24}$/.test(preview.iv) ||
41
+ typeof preview.ciphertext !== 'string' ||
42
+ preview.ciphertext.length > 3072 ||
43
+ !/^(?:[a-f0-9]{2}){16,}$/.test(preview.ciphertext)
44
+ )
45
+ return null;
46
+ const db = await new Promise((resolve, reject) => {
47
+ const request = indexedDB.open('puddle-notification-keys', 1);
48
+ request.onupgradeneeded = () => request.result.createObjectStore('keys', { keyPath: 'host' });
49
+ request.onsuccess = () => resolve(request.result);
50
+ request.onerror = () => reject(request.error);
51
+ });
52
+ try {
53
+ const saved = await new Promise((resolve, reject) => {
54
+ const request = db.transaction('keys').objectStore('keys').get(payload.host);
55
+ request.onsuccess = () => resolve(request.result);
56
+ request.onerror = () => reject(request.error);
57
+ });
58
+ if (!saved || saved.id !== preview.key || !/^[a-f0-9]{64}$/.test(saved.secret)) return null;
59
+ const key = await crypto.subtle.importKey('raw', bytes(saved.secret), 'AES-GCM', false, [
60
+ 'decrypt',
61
+ ]);
62
+ const clear = await crypto.subtle.decrypt(
63
+ {
64
+ name: 'AES-GCM',
65
+ iv: bytes(preview.iv),
66
+ additionalData: new TextEncoder().encode(
67
+ JSON.stringify(['puddle-notification-v1', payload.host, payload.id, preview.key]),
68
+ ),
69
+ },
70
+ key,
71
+ bytes(preview.ciphertext),
72
+ );
73
+ const content = JSON.parse(new TextDecoder().decode(clear));
74
+ return validContent(content) ? content : null;
75
+ } finally {
76
+ db.close();
77
+ }
78
+ }
6
79
  function receipts() {
7
80
  if (!database)
8
81
  database = new Promise((resolve, reject) => {
@@ -46,6 +119,7 @@ async function seen(id, save = false) {
46
119
  }
47
120
  function show(request, host, fromPush = false, client) {
48
121
  const task = queue.then(async () => {
122
+ if (typeof request === 'function') request = await request();
49
123
  const deduplicate = request.id.startsWith('puddle-event-');
50
124
  // Every push must call showNotification: Safari revokes permission for
51
125
  // silent pushes. Matching tags replace a racing live alert without renotify.
@@ -55,7 +129,10 @@ function show(request, host, fromPush = false, client) {
55
129
  tag: request.id,
56
130
  silent: true,
57
131
  renotify: false,
58
- data: host ? { host } : { client },
132
+ data: {
133
+ ...(host ? { host } : { client }),
134
+ ...(targetOf(request.target) ? { target: targetOf(request.target) } : {}),
135
+ },
59
136
  icon: '/puddle-app.png',
60
137
  });
61
138
  if (deduplicate) await seen(request.id, true).catch(() => {});
@@ -96,7 +173,18 @@ self.addEventListener('push', (event) => {
96
173
  if (!uuid.test(payload?.id) || !uuid.test(payload?.host)) return;
97
174
  event.waitUntil(
98
175
  show(
99
- { id: `puddle-event-${payload.id}`, title: 'Puddle', body: 'An agent needs your attention.' },
176
+ async () => {
177
+ // A missing/rotated key or damaged ciphertext must still produce a visible push.
178
+ const content = await decryptPreview(payload).catch(() => null);
179
+ return {
180
+ id: `puddle-event-${payload.id}`,
181
+ title: content?.title ?? 'Puddle',
182
+ body: content?.body ?? 'An agent needs your attention.',
183
+ ...(content
184
+ ? { target: { project: content.project, session: content.session, host: payload.host } }
185
+ : {}),
186
+ };
187
+ },
100
188
  payload.host,
101
189
  true,
102
190
  ),
@@ -104,8 +192,13 @@ self.addEventListener('push', (event) => {
104
192
  });
105
193
  self.addEventListener('notificationclick', (event) => {
106
194
  event.notification.close();
107
- const host = event.notification.data?.host;
108
- const target = uuid.test(host) ? `/?notificationHost=${host}` : '/';
195
+ const destination = targetOf(event.notification.data?.target);
196
+ const host = destination?.host ?? event.notification.data?.host;
197
+ const target = uuid.test(host)
198
+ ? `/?notificationHost=${host}${destination ? `&notificationProject=${destination.project}&notificationSession=${destination.session}` : ''}`
199
+ : destination
200
+ ? `/project/${destination.project}/session/${destination.session}`
201
+ : '/';
109
202
  event.waitUntil(
110
203
  (async () => {
111
204
  const windows = await self.clients.matchAll({ type: 'window', includeUncontrolled: true });
@@ -121,11 +214,16 @@ self.addEventListener('notificationclick', (event) => {
121
214
  new URL(client.url).pathname === '/preview.html'
122
215
  )
123
216
  continue;
124
- await client.focus();
217
+ try {
218
+ await client.focus();
219
+ } catch {
220
+ continue;
221
+ }
125
222
  client.postMessage({
126
223
  t: 'notification-click',
127
224
  id: event.notification.tag,
128
225
  ...(uuid.test(host) ? { host } : {}),
226
+ ...(destination ? { target: destination } : {}),
129
227
  });
130
228
  return;
131
229
  }