@spencerboucher/switchyard 0.8.0 → 0.10.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 (161) hide show
  1. package/README.md +4 -4
  2. package/dist/bridge/lines.js +33 -0
  3. package/dist/bridge/switchyard-terminal.js +59 -22
  4. package/dist/switchyard.mjs +9769 -7208
  5. package/dist/terminal/holder.js +172 -55
  6. package/package.json +1 -1
  7. package/web-dist/apple-touch-icon.png +0 -0
  8. package/web-dist/assets/AutomationView-BsTwLnMp.js +2 -0
  9. package/web-dist/assets/AutomationView-BsTwLnMp.js.map +1 -0
  10. package/web-dist/assets/Card-CAFxHoHd.js +2 -0
  11. package/web-dist/assets/Card-CAFxHoHd.js.map +1 -0
  12. package/web-dist/assets/Checkbox-rDvnQnxc.js +2 -0
  13. package/web-dist/assets/Checkbox-rDvnQnxc.js.map +1 -0
  14. package/web-dist/assets/Copy-D8xhl7eJ.js +2 -0
  15. package/web-dist/assets/Copy-D8xhl7eJ.js.map +1 -0
  16. package/web-dist/assets/DangerZone-og55TRug.js +2 -0
  17. package/web-dist/assets/DangerZone-og55TRug.js.map +1 -0
  18. package/web-dist/assets/Field-Dbg_8KTt.js +2 -0
  19. package/web-dist/assets/Field-Dbg_8KTt.js.map +1 -0
  20. package/web-dist/assets/InteractionCard-Yu5wgLC3.js +6 -0
  21. package/web-dist/assets/InteractionCard-Yu5wgLC3.js.map +1 -0
  22. package/web-dist/assets/Menu-euseM8yR.js +2 -0
  23. package/web-dist/assets/Menu-euseM8yR.js.map +1 -0
  24. package/web-dist/assets/Overlay-BNPvMjLX.js +2 -0
  25. package/web-dist/assets/Overlay-BNPvMjLX.js.map +1 -0
  26. package/web-dist/assets/Priority-Ca1B2yFr.js +2 -0
  27. package/web-dist/assets/Priority-Ca1B2yFr.js.map +1 -0
  28. package/web-dist/assets/{SegmentedControl-BZPzNU08.js → SegmentedControl-D2d-NEr-.js} +2 -2
  29. package/web-dist/assets/{SegmentedControl-BZPzNU08.js.map → SegmentedControl-D2d-NEr-.js.map} +1 -1
  30. package/web-dist/assets/Select-C9L1HKMm.js +2 -0
  31. package/web-dist/assets/Select-C9L1HKMm.js.map +1 -0
  32. package/web-dist/assets/StatusChip-BYuVxYS7.js +2 -0
  33. package/web-dist/assets/StatusChip-BYuVxYS7.js.map +1 -0
  34. package/web-dist/assets/Switch-DAcUcSbH.js +2 -0
  35. package/web-dist/assets/Switch-DAcUcSbH.js.map +1 -0
  36. package/web-dist/assets/Tabs-xtagjtA5.js +184 -0
  37. package/web-dist/assets/Tabs-xtagjtA5.js.map +1 -0
  38. package/web-dist/assets/TextArea-CKGHwURx.js +2 -0
  39. package/web-dist/assets/TextArea-CKGHwURx.js.map +1 -0
  40. package/web-dist/assets/TextLink-Bub7sMxr.js +2 -0
  41. package/web-dist/assets/TextLink-Bub7sMxr.js.map +1 -0
  42. package/web-dist/assets/arrow-down-C-iLrN8I.js +2 -0
  43. package/web-dist/assets/arrow-down-C-iLrN8I.js.map +1 -0
  44. package/web-dist/assets/arrow-up-Dj-VTKx4.js +2 -0
  45. package/web-dist/assets/arrow-up-Dj-VTKx4.js.map +1 -0
  46. package/web-dist/assets/attention-CI4dHY81.js +2 -0
  47. package/web-dist/assets/attention-CI4dHY81.js.map +1 -0
  48. package/web-dist/assets/auth-308hUVgi.js +2 -0
  49. package/web-dist/assets/auth-308hUVgi.js.map +1 -0
  50. package/web-dist/assets/automations-BzpsJoa3.js +2 -0
  51. package/web-dist/assets/automations-BzpsJoa3.js.map +1 -0
  52. package/web-dist/assets/bell-DSwa2PAb.js +2 -0
  53. package/web-dist/assets/bell-DSwa2PAb.js.map +1 -0
  54. package/web-dist/assets/board-CeJxBVHC.js +10 -0
  55. package/web-dist/assets/board-CeJxBVHC.js.map +1 -0
  56. package/web-dist/assets/code-DkNQvMI9.js +12 -0
  57. package/web-dist/assets/code-DkNQvMI9.js.map +1 -0
  58. package/web-dist/assets/describe-CVXeUClF.js +5 -0
  59. package/web-dist/assets/describe-CVXeUClF.js.map +1 -0
  60. package/web-dist/assets/enroll-jnKygG4L.js +2 -0
  61. package/web-dist/assets/enroll-jnKygG4L.js.map +1 -0
  62. package/web-dist/assets/index-BHGZTS2e.js +16 -0
  63. package/web-dist/assets/index-BHGZTS2e.js.map +1 -0
  64. package/web-dist/assets/index-D2ml031z.css +2 -0
  65. package/web-dist/assets/logic-B46fwD-8.js +2 -0
  66. package/web-dist/assets/logic-B46fwD-8.js.map +1 -0
  67. package/web-dist/assets/more-DvKHMdBk.js +2 -0
  68. package/web-dist/assets/more-DvKHMdBk.js.map +1 -0
  69. package/web-dist/assets/queue-B2Vl0uSh.js +2 -0
  70. package/web-dist/assets/queue-B2Vl0uSh.js.map +1 -0
  71. package/web-dist/assets/repo-BDS8RBxq.js +2 -0
  72. package/web-dist/assets/repo-BDS8RBxq.js.map +1 -0
  73. package/web-dist/assets/server-BqZ-w6M8.js +2 -0
  74. package/web-dist/assets/server-BqZ-w6M8.js.map +1 -0
  75. package/web-dist/assets/settings-CLt7x9u8.js +8 -0
  76. package/web-dist/assets/settings-CLt7x9u8.js.map +1 -0
  77. package/web-dist/assets/states-C773NlHo.js +56 -0
  78. package/web-dist/assets/states-C773NlHo.js.map +1 -0
  79. package/web-dist/assets/terminal-BOV_MXPZ.js +2 -0
  80. package/web-dist/assets/terminal-BOV_MXPZ.js.map +1 -0
  81. package/web-dist/assets/terminal-login-BK5UjUir.js +2 -0
  82. package/web-dist/assets/terminal-login-BK5UjUir.js.map +1 -0
  83. package/web-dist/assets/useBackTo-BIrCq3W2.js +2 -0
  84. package/web-dist/assets/useBackTo-BIrCq3W2.js.map +1 -0
  85. package/web-dist/assets/useBreakpoint-BphHBrzF.js +2 -0
  86. package/web-dist/assets/useBreakpoint-BphHBrzF.js.map +1 -0
  87. package/web-dist/assets/useNow-Cm-tdyod.js +2 -0
  88. package/web-dist/assets/useNow-Cm-tdyod.js.map +1 -0
  89. package/web-dist/assets/useResource-BBqCoY2q.js +2 -0
  90. package/web-dist/assets/useResource-BBqCoY2q.js.map +1 -0
  91. package/web-dist/icon-192.png +0 -0
  92. package/web-dist/icon-512.png +0 -0
  93. package/web-dist/icon.svg +20 -8
  94. package/web-dist/index.html +7 -4
  95. package/web-dist/manifest.webmanifest +1 -1
  96. package/web-dist/maskable-512.png +0 -0
  97. package/web-dist/sw.js +1 -1
  98. package/web-dist/sw.js.map +1 -1
  99. package/web-dist/assets/AutomationView-GCthHkym.js +0 -2
  100. package/web-dist/assets/AutomationView-GCthHkym.js.map +0 -1
  101. package/web-dist/assets/Badge-CEMI3YyI.js +0 -2
  102. package/web-dist/assets/Badge-CEMI3YyI.js.map +0 -1
  103. package/web-dist/assets/InteractionCard-DaLqafmA.js +0 -6
  104. package/web-dist/assets/InteractionCard-DaLqafmA.js.map +0 -1
  105. package/web-dist/assets/Menu-BcKiEc6X.js +0 -2
  106. package/web-dist/assets/Menu-BcKiEc6X.js.map +0 -1
  107. package/web-dist/assets/Tabs-COH5EMql.js +0 -184
  108. package/web-dist/assets/Tabs-COH5EMql.js.map +0 -1
  109. package/web-dist/assets/TextArea-CeMEPZn4.js +0 -2
  110. package/web-dist/assets/TextArea-CeMEPZn4.js.map +0 -1
  111. package/web-dist/assets/arrow-left-C6YWM6y6.js +0 -2
  112. package/web-dist/assets/arrow-left-C6YWM6y6.js.map +0 -1
  113. package/web-dist/assets/attention-BAdea2kA.js +0 -2
  114. package/web-dist/assets/attention-BAdea2kA.js.map +0 -1
  115. package/web-dist/assets/automations-DSFUzB0w.js +0 -2
  116. package/web-dist/assets/automations-DSFUzB0w.js.map +0 -1
  117. package/web-dist/assets/board-eQ3H8yCp.js +0 -10
  118. package/web-dist/assets/board-eQ3H8yCp.js.map +0 -1
  119. package/web-dist/assets/code-D7JmYMKe.js +0 -13
  120. package/web-dist/assets/code-D7JmYMKe.js.map +0 -1
  121. package/web-dist/assets/copy-oWgGHmkS.js +0 -2
  122. package/web-dist/assets/copy-oWgGHmkS.js.map +0 -1
  123. package/web-dist/assets/describe-C1YimBXJ.js +0 -5
  124. package/web-dist/assets/describe-C1YimBXJ.js.map +0 -1
  125. package/web-dist/assets/dist-B_RpNOcK.js +0 -56
  126. package/web-dist/assets/dist-B_RpNOcK.js.map +0 -1
  127. package/web-dist/assets/enroll-1OCy5r50.js +0 -2
  128. package/web-dist/assets/enroll-1OCy5r50.js.map +0 -1
  129. package/web-dist/assets/index-BhpEXLav.js +0 -11
  130. package/web-dist/assets/index-BhpEXLav.js.map +0 -1
  131. package/web-dist/assets/index-DUeOwDZ6.css +0 -2
  132. package/web-dist/assets/logic-CBvFOyIo.js +0 -2
  133. package/web-dist/assets/logic-CBvFOyIo.js.map +0 -1
  134. package/web-dist/assets/more-RjVyxPaz.js +0 -2
  135. package/web-dist/assets/more-RjVyxPaz.js.map +0 -1
  136. package/web-dist/assets/now-CDjeFSLD.js +0 -2
  137. package/web-dist/assets/now-CDjeFSLD.js.map +0 -1
  138. package/web-dist/assets/plus-BLP-wIYD.js +0 -2
  139. package/web-dist/assets/plus-BLP-wIYD.js.map +0 -1
  140. package/web-dist/assets/save-DEvZPWlS.js +0 -2
  141. package/web-dist/assets/save-DEvZPWlS.js.map +0 -1
  142. package/web-dist/assets/search-C6cn5WOo.js +0 -2
  143. package/web-dist/assets/search-C6cn5WOo.js.map +0 -1
  144. package/web-dist/assets/send-BKjswHxB.js +0 -2
  145. package/web-dist/assets/send-BKjswHxB.js.map +0 -1
  146. package/web-dist/assets/server-SxFxIScj.js +0 -2
  147. package/web-dist/assets/server-SxFxIScj.js.map +0 -1
  148. package/web-dist/assets/settings-tH4UuIfJ.js +0 -7
  149. package/web-dist/assets/settings-tH4UuIfJ.js.map +0 -1
  150. package/web-dist/assets/shared-CRX6KSBm.js +0 -3
  151. package/web-dist/assets/shared-CRX6KSBm.js.map +0 -1
  152. package/web-dist/assets/sliders-horizontal-DAoirila.js +0 -2
  153. package/web-dist/assets/sliders-horizontal-DAoirila.js.map +0 -1
  154. package/web-dist/assets/states-DXozulGZ.js +0 -2
  155. package/web-dist/assets/states-DXozulGZ.js.map +0 -1
  156. package/web-dist/assets/terminal-login-CW2rE50W.js +0 -2
  157. package/web-dist/assets/terminal-login-CW2rE50W.js.map +0 -1
  158. package/web-dist/assets/trash-C6X2OLrI.js +0 -2
  159. package/web-dist/assets/trash-C6X2OLrI.js.map +0 -1
  160. package/web-dist/assets/useNow-C_B7JgMM.js +0 -2
  161. package/web-dist/assets/useNow-C_B7JgMM.js.map +0 -1
@@ -0,0 +1,5 @@
1
+ import{_ as e,m as t,t as n}from"./jsx-runtime-D13aM9HY.js";import{St as r,_t as i,et as a,v as o,vt as s,yt as c,z as l}from"./states-C773NlHo.js";import{i as u,u as ee}from"./useBreakpoint-BphHBrzF.js";var d=e(t(),1),f=n();function p({count:e}){return e>0?(0,f.jsxs)(`span`,{className:`inline-flex items-center gap-1 text-sm text-warning`,children:[(0,f.jsx)(o,{"aria-hidden":!0,className:`size-4 shrink-0`}),e===1?`1 failure`:`${e} failures`]}):(0,f.jsxs)(`span`,{className:`inline-flex items-center gap-1 text-sm text-muted`,children:[(0,f.jsx)(ee,{"aria-hidden":!0,className:`size-4 shrink-0`}),`No failures`]})}var m=(e,t=``)=>`/api/automations/${encodeURIComponent(e)}${t}`,h=r()(e=>({byId:{},deleted:{},put:t=>e(e=>({byId:{...e.byId,[t.id]:t}})),putMany:t=>e(e=>{let n={...e.byId};for(let e of t)n[e.id]=e;return{byId:n}}),remove:t=>e(e=>{let n={...e.byId};return delete n[t],{byId:n,deleted:{...e.deleted,[t]:!0}}})}));function g(e,t){return e?t&&t.updatedAt>e.updatedAt?t:e:t}function _(){let e=l(e=>e.automations),t=h(e=>e.byId),n=h(e=>e.deleted);return(0,d.useMemo)(()=>{let r={};for(let i of new Set([...Object.keys(e),...Object.keys(t)])){if(n[i])continue;let a=g(e[i],t[i]);a&&(r[i]=a)}return r},[e,t,n])}function te(e){let t=l(t=>e?t.automations[e]:void 0),n=h(t=>e?t.byId[e]:void 0);return h(t=>e?t.deleted[e]:void 0)?void 0:g(t,n)}function ne(){let[e,t]=(0,d.useState)({});return{busy:e,toggle:(0,d.useCallback)(async(e,n)=>{t(t=>({...t,[e.id]:!0}));try{let t=await i(m(e.id),{enabled:n});t&&typeof t==`object`&&`id`in t&&h.getState().put(t),u.success(`${e.name} ${n?`enabled`:`disabled`}`)}catch(t){u.error(`Could not ${n?`enable`:`disable`} ${e.name}: ${c(t)}`)}finally{t(t=>({...t,[e.id]:!1}))}},[])}}function re(e){let[t,n]=(0,d.useState)({});return{busy:t,run:(0,d.useCallback)(async t=>{n(e=>({...e,[t.id]:!0}));try{let n=await s(m(t.id,`/run`)),r=n&&typeof n==`object`?n.state:void 0;u.success(r&&r!==`fired`?`${t.name}: occurrence recorded (${r.replace(/_/g,` `)})`:`${t.name} started`),e?.()}catch(e){u.error(`Could not run ${t.name}: ${c(e)}`)}finally{n(e=>({...e,[t.id]:!1}))}},[e])}}var v={manual:`Manual`,once:`Once`,cron:`Schedule`,webhook:`Webhook`,dependency:`Dependency`},y=[{value:`fresh`,label:`Fresh`,explanation:`Fresh: a new task per occurrence`},{value:`continue`,label:`Continue`,explanation:`Continue: reuse the task with the same grouping key`},{value:`watch`,label:`Watch`,explanation:`Watch: only raises attention when something relevant changed`}],b=[{value:`skip`,label:`Skip`,explanation:`Skip: drop the occurrence while earlier work is still open`},{value:`queue_one`,label:`Queue one`,explanation:`Queue one: keep a single pending occurrence and fire it when the open work ends`},{value:`append`,label:`Append`,explanation:`Append: add the new objective to the open task (as a follow-up if it is running)`}],x=[{value:`skip`,label:`Skip`,explanation:`Skip: occurrences missed while the coordinator was down are recorded and skipped`},{value:`run_once`,label:`Run once`,explanation:`Run once: on return, fire the newest missed occurrence once`}];function S(e){return y.find(t=>t.value===e)?.label??e}var ie=[`{{date}}`,`{{time}}`,`{{occurrence}}`,`{{payload.<path>}}`];function C(e){return e.length<=1?e[0]??``:`${e.slice(0,-1).join(`, `)} and ${e[e.length-1]}`}function w(e){return String(e).padStart(2,`0`)}function T(e){let t=e%100;if(t>=11&&t<=13)return`${e}th`;switch(e%10){case 1:return`${e}st`;case 2:return`${e}nd`;case 3:return`${e}rd`;default:return`${e}th`}}function E(e){return e&&e[0].toUpperCase()+e.slice(1)}function D(e){return e.split(`
2
+ `).map(e=>e.trim()).filter(e=>e.length>0)}var O=[`Sunday`,`Monday`,`Tuesday`,`Wednesday`,`Thursday`,`Friday`,`Saturday`],k=[`January`,`February`,`March`,`April`,`May`,`June`,`July`,`August`,`September`,`October`,`November`,`December`],ae=[{name:`Minute`,min:0,max:59},{name:`Hour`,min:0,max:23},{name:`Day of month`,min:1,max:31},{name:`Month`,min:1,max:12,names:k.map(e=>e.slice(0,3).toLowerCase())},{name:`Day of week`,min:0,max:7,names:O.map(e=>e.slice(0,3).toLowerCase())}];function A(e,t){let n=e.toLowerCase();if(t.names){let e=t.names.indexOf(n);if(e>=0)return t.min+e}if(!/^\d+$/.test(e))return`"${e}" is not a number`;let r=Number(e);return r<t.min||r>t.max?`${r} is out of range ${t.min}–${t.max}`:r}function oe(e,t){if(e===`*`||e===`?`)return{any:!0,values:[],step:null,extended:!1};if(/[LW#]/i.test(e)&&!t.names?.some(t=>e.toLowerCase().includes(t)))return/^[0-9A-Za-z*?,\-/#]+$/.test(e)?{any:!1,values:[],step:null,extended:!0}:`"${e}" is not valid`;let n=e.split(`,`),r=new Set,i=null;for(let e of n){if(!e)return`empty list item`;let[a,o,s]=e.split(`/`);if(s!==void 0)return`"${e}" has more than one step`;let c=1;if(o!==void 0){if(!/^\d+$/.test(o)||Number(o)<1)return`"${o}" is not a valid step`;c=Number(o)}let l,u;if(a===`*`||a===`?`)l=t.min,u=t.max;else if(a?.includes(`-`)){let[e,n]=a.split(`-`),r=A(e??``,t),i=A(n??``,t);if(typeof r==`string`)return r;if(typeof i==`string`)return i;if(r>i)return`range ${a} runs backwards`;l=r,u=i}else{let e=A(a??``,t);if(typeof e==`string`)return e;l=e,u=o===void 0?e:t.max}for(let e=l;e<=u;e+=c)r.add(e);n.length===1&&o!==void 0&&!a?.includes(`-`)&&(i={every:c,start:l})}let a=[...r].sort((e,t)=>e-t);t.max===7&&(a=[...new Set(a.map(e=>e%7))].sort((e,t)=>e-t));let o=t.max===7?7:t.max-t.min+1;return a.length===o&&!i?{any:!0,values:[],step:null,extended:!1}:{any:!1,values:a,step:i,extended:!1}}function j(e){let t=e.trim();if(!t)return`Enter a cron expression`;let n=t.split(/\s+/);if(n.length!==5)return`Expected 5 fields (minute hour day-of-month month day-of-week), got ${n.length}`;let r=[];for(let e=0;e<5;e++){let t=ae[e],i=oe(n[e],t);if(typeof i==`string`)return`${t.name}: ${i}`;r.push(i)}return r}function M(e){let t=j(e);return typeof t==`string`?t:null}function N(e){if(!e.trim())return`Enter a timezone`;try{return new Intl.DateTimeFormat(void 0,{timeZone:e.trim()}),null}catch{return`Unknown timezone "${e}"`}}function P(e){for(let t=1;t<e.length;t++)if(e[t]!==e[t-1]+1)return!1;return e.length>0}function F(e,t){if(e.any&&t.any)return{kind:`freq`,text:`Every minute`};if(e.step&&e.step.start===0&&t.any)return{kind:`freq`,text:e.step.every===1?`Every minute`:`Every ${e.step.every} minutes`};if(e.any)return t.step||t.values.length===0?null:P(t.values)?{kind:`freq`,text:`Every minute from ${w(t.values[0])}:00 to ${w(t.values[t.values.length-1])}:59`}:null;if(t.any)return e.step?null:e.values.length===1&&e.values[0]===0?{kind:`freq`,text:`Every hour`}:{kind:`freq`,text:`Every hour at ${C(e.values.map(e=>`:${w(e)}`))}`};if(t.step&&t.step.start===0&&e.values.length===1&&!e.step){let n=e.values[0]===0?``:` at :${w(e.values[0])}`;return{kind:`freq`,text:t.step.every===1?`Every hour${n}`:`Every ${t.step.every} hours${n}`}}if(e.values.length*t.values.length<=6){let n=[];for(let r of t.values)for(let t of e.values)n.push(`${w(r)}:${w(t)}`);return{kind:`at`,text:C(n)}}if(!P(t.values))return null;let n=t.values[0],r=t.values[t.values.length-1];if(e.step&&e.step.start===0)return{kind:`freq`,text:`Every ${e.step.every} minutes from ${w(n)}:00 to ${w(r)}:59`};if(e.values.length===1){let t=w(e.values[0]);return{kind:`freq`,text:`Every hour from ${w(n)}:${t} to ${w(r)}:${t}`}}return null}function I(e,t){return(e+6)%7-(t+6)%7}function L(e,t,n){if(!e.any&&!n.any)return null;let r=t.any?null:C(t.values.map(e=>k[e-1])),i=r?` in ${r}`:``;if(!n.any){let e=[...n.values].sort(I),t=e.join(`,`);if(t===`1,2,3,4,5`)return{lead:`Every weekday${i}`,qual:` on weekdays${i}`};if(t===`6,0`)return{lead:`Every Saturday and Sunday${i}`,qual:` on weekends${i}`};let r=e.map(e=>O[e]);return{lead:`Every ${C(r)}${i}`,qual:` on ${C(r.map(e=>`${e}s`))}${i}`}}if(!e.any){if(e.values.length>5)return null;let t=C(e.values.map(T)),n=r||`every month`;return{lead:`On the ${t} of ${n}`,qual:` on the ${t} of ${n}`}}return{lead:`Every day${i}`,qual:i}}function R(e,t){let n=j(e);if(typeof n==`string`||n.some(e=>e.extended))return null;let[r,i,a,o,s]=n,c=F(r,i),l=L(a,o,s);if(!c||!l)return null;let u=c.kind===`at`?`${l.lead} at ${c.text}`:`${c.text}${l.qual}`;return t?`${u} (${t})`:u}function z(e,t){return R(e,t)??`Cron ${e.trim()} (${t})`}function B(e){let t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString(void 0,{dateStyle:`medium`,timeStyle:`short`})}function V(e,t={}){let n=t.formatDate??B;switch(e.kind){case`manual`:return`Manual`;case`once`:return e.at&&!Number.isNaN(Date.parse(e.at))?`Once at ${n(e.at)}`:`Once (no time chosen)`;case`cron`:return z(e.expr,e.timezone);case`webhook`:return`Webhook`;case`dependency`:{let n=e.taskId?t.taskTitle?.(e.taskId):void 0;return n?`When "${n}" is done`:e.taskId?`When a deleted task is done`:`When a task is done`}}}function H(e,t){let n=e.trigger;switch(n.kind){case`manual`:return`When run manually`;case`webhook`:{let t=e.filters.filter(e=>e.path.trim());return t.length===0?`When a webhook is received`:`When a webhook is received where ${C(t.map(e=>`${e.path.trim()} = "${e.equals}"`))}`}default:return V(n,t)}}function U(e,t={}){let n=e.template.title.trim()||`Untitled task`,r=t.projectName?` in ${t.projectName}`:``,i=t.hostName?` on ${t.hostName}`:` on the project's default host`,a=t.profileName?` with profile ${t.profileName}`:``,o;switch(e.mode){case`continue`:{let t=e.groupingKey?.trim();o=t?`continue the task grouped by "${t}" (or create "${n}")`:`continue the matching task (or create "${n}")`;break}case`watch`:o=`create a watch task "${n}" that only raises attention when something relevant changed`;break;default:o=`create a fresh task "${n}"`}let s=[`${H(e,t)}, ${o}${r}${i}${a}`];if(s.push(e.overlap===`skip`?`skip if previous work is still open`:e.overlap===`queue_one`?`queue one occurrence if previous work is still open`:`append to the open task if previous work is still open`),(e.trigger.kind===`cron`||e.trigger.kind===`once`)&&s.push(e.missed===`skip`?`skip missed runs`:`run missed runs once on return`),e.continuation.enabled){let t=e.continuation;s.push(`continue automatically up to ${t.maxContinuations} times within ${t.maxElapsedMinutes} minutes`)}let c=e.completion.checks;return c&&c.length>0&&s.push(`run ${c.length===1?`1 check`:`${c.length} checks`}`),s.push(e.completion.requireHumanReview?`requires human review`:`no human review required`),E(s.join(`; `))}function W(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||`UTC`}catch{return`UTC`}}function G(e={}){return{name:``,enabled:!0,projectId:e.projectId??``,triggerKind:`manual`,onceAt:``,cronExpr:`0 9 * * 1-5`,cronTimezone:e.timezone??W(),dependencyTaskId:``,filters:[],title:``,objective:``,criteria:``,priority:2,hostId:``,profileId:``,mode:`fresh`,groupingKey:``,overlap:`skip`,missed:`skip`,continuationEnabled:!1,continuationObjective:``,maxContinuations:`5`,maxElapsedMinutes:`60`,maxTokens:``,maxCostUsd:``,checks:``,requireHumanReview:!0}}function K(e){if(!e)return``;let t=new Date(e);return Number.isNaN(t.getTime())?``:`${t.getFullYear()}-${w(t.getMonth()+1)}-${w(t.getDate())}T${w(t.getHours())}:${w(t.getMinutes())}`}function q(e){if(!e)return``;let t=new Date(e);return Number.isNaN(t.getTime())?``:t.toISOString()}function se(e,t={}){let n=G({projectId:e.projectId,timezone:t.timezone}),r=e.trigger,i=e.continuation;return{...n,name:e.name,enabled:e.enabled,triggerKind:r.kind,onceAt:r.kind===`once`?K(r.at):``,cronExpr:r.kind===`cron`?r.expr:n.cronExpr,cronTimezone:r.kind===`cron`?r.timezone:n.cronTimezone,dependencyTaskId:r.kind===`dependency`?r.taskId:``,filters:e.filters.map(e=>({...e})),title:e.template.title,objective:e.template.objective,criteria:e.template.acceptanceCriteria.join(`
3
+ `),priority:e.template.priority,hostId:e.hostId??``,profileId:e.profileId??``,mode:e.mode,groupingKey:e.groupingKey??``,overlap:e.overlap,missed:e.missed,continuationEnabled:i.enabled,continuationObjective:i.enabled?i.objective:``,maxContinuations:i.enabled?String(i.maxContinuations):n.maxContinuations,maxElapsedMinutes:i.enabled?String(i.maxElapsedMinutes):n.maxElapsedMinutes,maxTokens:i.enabled&&i.maxTokens!==null?String(i.maxTokens):``,maxCostUsd:i.enabled&&i.maxCostUsd!==null?String(i.maxCostUsd):``,checks:e.completion.checks?e.completion.checks.join(`
4
+ `):``,requireHumanReview:e.completion.requireHumanReview}}function J(e){return e.trim()===``?NaN:Number(e.trim())}function Y(e){return e.trim()===``?null:Number(e.trim())}function ce(e){switch(e.triggerKind){case`manual`:return{kind:`manual`};case`once`:return{kind:`once`,at:q(e.onceAt)};case`cron`:return{kind:`cron`,expr:e.cronExpr.trim(),timezone:e.cronTimezone.trim()};case`webhook`:return{kind:`webhook`};case`dependency`:return{kind:`dependency`,taskId:e.dependencyTaskId}}}function X(e){let t=D(e.checks);return{name:e.name.trim(),enabled:e.enabled,projectId:e.projectId,trigger:ce(e),filters:e.filters.filter(e=>e.path.trim()||e.equals.trim()).map(e=>({path:e.path.trim(),equals:e.equals})),template:{title:e.title.trim(),objective:e.objective,acceptanceCriteria:D(e.criteria),priority:e.priority},hostId:e.hostId||null,profileId:e.profileId||null,mode:e.mode,groupingKey:e.groupingKey.trim()||null,overlap:e.overlap,missed:e.missed,continuation:e.continuationEnabled?{enabled:!0,objective:e.continuationObjective.trim(),maxContinuations:J(e.maxContinuations),maxElapsedMinutes:J(e.maxElapsedMinutes),maxTokens:Y(e.maxTokens),maxCostUsd:Y(e.maxCostUsd)}:{enabled:!1},completion:{checks:t.length>0?t:null,requireHumanReview:e.requireHumanReview}}}var le={name:`name`,enabled:`enabled`,projectId:`projectId`,trigger:`triggerKind`,"trigger.kind":`triggerKind`,"trigger.at":`onceAt`,"trigger.expr":`cronExpr`,"trigger.timezone":`cronTimezone`,"trigger.taskId":`dependencyTaskId`,"template.title":`title`,"template.objective":`objective`,"template.acceptanceCriteria":`criteria`,"template.priority":`priority`,hostId:`hostId`,profileId:`profileId`,mode:`mode`,groupingKey:`groupingKey`,overlap:`overlap`,missed:`missed`,"continuation.objective":`continuationObjective`,"continuation.maxContinuations":`maxContinuations`,"continuation.maxElapsedMinutes":`maxElapsedMinutes`,"continuation.maxTokens":`maxTokens`,"continuation.maxCostUsd":`maxCostUsd`,"completion.checks":`checks`,"completion.requireHumanReview":`requireHumanReview`};function Z(e){let t=e.map(String);if(t[0]===`filters`&&t.length>=3)return`filters.${Number(t[1])}.${t[2]===`equals`?`equals`:`path`}`;if(t[0]===`template`&&t[1]===`acceptanceCriteria`)return`criteria`;if(t[0]===`completion`&&t[1]===`checks`)return`checks`;for(let e=t.length;e>0;e--){let n=le[t.slice(0,e).join(`.`)];if(n)return n}return`form`}function Q(e,t,n,r){let i=J(e);return!Number.isInteger(i)||i<t||i>n?`${r} must be a whole number from ${t} to ${n}`:null}function ue(e){let t={},n=(e,n)=>{n&&!t[e]&&(t[e]=n)};switch(e.name.trim()||n(`name`,`Name is required`),e.projectId||n(`projectId`,`Choose a project`),e.title.trim()||n(`title`,`Task title is required`),e.triggerKind){case`once`:q(e.onceAt)||n(`onceAt`,`Choose a date and time`);break;case`cron`:n(`cronExpr`,M(e.cronExpr)),n(`cronTimezone`,N(e.cronTimezone));break;case`dependency`:e.dependencyTaskId||n(`dependencyTaskId`,`Choose the task to wait for`)}if(e.filters.forEach((e,t)=>{!e.path.trim()&&e.equals.trim()&&n(`filters.${t}.path`,`Enter a payload path`)}),e.mode===`continue`&&!e.groupingKey.trim()&&n(`groupingKey`,`A grouping key is required to continue an existing task`),e.continuationEnabled){if(e.continuationObjective.trim()||n(`continuationObjective`,`An objective is required when continuation is on`),n(`maxContinuations`,Q(e.maxContinuations,1,50,`Max continuations`)),n(`maxElapsedMinutes`,Q(e.maxElapsedMinutes,1,1440,`Max elapsed minutes`)),e.maxTokens.trim()){let t=J(e.maxTokens);(!Number.isInteger(t)||t<=0)&&n(`maxTokens`,`Max tokens must be a positive whole number`)}if(e.maxCostUsd.trim()){let t=J(e.maxCostUsd);(!Number.isFinite(t)||t<=0)&&n(`maxCostUsd`,`Max cost must be a positive amount`)}}let r=X(e),i=a.safeParse(r);if(!i.success)for(let e of i.error.issues)n(Z(e.path),e.message);return{input:i.success&&Object.keys(t).length===0?i.data:null,errors:t}}function $(e){return e.scheduledFor??e.createdAt}function de(...e){let t=new Map;for(let n of e)for(let e of n??[])t.set(e.id,e);return[...t.values()].sort((e,t)=>t.createdAt.localeCompare(e.createdAt)||$(t).localeCompare($(e)))}function fe(e){if(Array.isArray(e))return e;if(e&&typeof e==`object`){let t=e;if(Array.isArray(t.items))return t.items;if(Array.isArray(t.occurrences))return t.occurrences}return[]}function pe(e,t){return`curl -X POST -H 'Authorization: Bearer ${e}' -H 'Content-Type: application/json' -d '{}' ${t}`}export{p as C,_ as D,te as E,re as O,ue as S,h as T,S as _,v as a,U as b,V as c,G as d,Z as f,de as g,W as h,ie as i,ne as k,se as l,q as m,y as n,pe as o,B as p,b as r,R as s,x as t,X as u,fe as v,m as w,M as x,$ as y};
5
+ //# sourceMappingURL=describe-CVXeUClF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describe-CVXeUClF.js","names":["useMemo","useState","useCallback","__toESM","require_react","require_jsx_runtime","create","apiPatch","AutomationInput","TriangleAlert","apiPost","errorMessage","useSyncStore","toast","CircleCheck","import_react","import_jsx_runtime","FailureCount","count","automationPath","id","suffix","useLocalAutomations","set","s","a","byId","list","newer","b","useMergedAutomations","store","local","deleted","out","useMergedAutomation","fromStore","fromLocal","useToggleEnabled","busy","setBusy","res","enabled","e","useRunNow","state","onDone","TRIGGER_KIND_LABELS","MODE_OPTIONS","OVERLAP_OPTIONS","MISSED_OPTIONS","modeLabel","m","mode","TEMPLATE_VARIABLES","listText","items","pad","n","ordinal","mod100","capitalize","lines","text","l","DAY_NAMES","MONTH_NAMES","CRON_FIELDS","d","parseValue","lower","token","def","idx","parseCronField","src","parts","values","step","part","base","stepStr","extra","every","start","end","av","bv","v","i","sorted","full","parseCron","trimmed","expr","fields","parsed","validateCron","validateTimezone","tz","isContiguous","describeTime","h","at","times","hv","mv","first","last","mm","mondayFirst","describeDays","dom","dow","months","mon","inMonths","days","key","names","ords","of","describeCron","f","time","timezone","describeCronOrRaw","formatAbsolute","iso","describeTrigger","fmt","ctx","trigger","title","triggerClause","t","input","filters","summarizeAutomation","project","host","profile","action","c","checks","localTimezone","emptyDraft","opts","isoToLocalInput","localInputToIso","value","draftFromAutomation","toNumber","toOptionalNumber","draftTrigger","draftToInput","PATH_TO_FIELD","fieldForPath","path","intError","min","max","label","validateDraft","errors","msg","raw","issue","occurrenceTime","o","mergeOccurrences","lists","normalizeHistory","r","curlExample","url"],"sources":["../../src/features/automations/controls.tsx","../../src/features/automations/describe.ts"],"sourcesContent":["import type { Automation } from \"@switchyard/protocol\";\nimport { CircleCheck, TriangleAlert } from \"lucide-react\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport { create } from \"zustand\";\nimport { toast } from \"../../components/ui/Toast\";\nimport { apiPatch, apiPost, errorMessage } from \"../../sync/api\";\nimport { useSyncStore } from \"../../sync/store\";\n\n/* Small building blocks shared by the Automations list and editor. */\n\n/** Failure count as icon + text; warning tone when > 0. */\nexport function FailureCount({ count }: { count: number }) {\n if (count > 0) {\n return (\n <span className=\"inline-flex items-center gap-1 text-sm text-warning\">\n <TriangleAlert aria-hidden className=\"size-4 shrink-0\" />\n {count === 1 ? \"1 failure\" : `${count} failures`}\n </span>\n );\n }\n return (\n <span className=\"inline-flex items-center gap-1 text-sm text-muted\">\n <CircleCheck aria-hidden className=\"size-4 shrink-0\" />\n No failures\n </span>\n );\n}\n\nexport const automationPath = (id: string, suffix = \"\") =>\n `/api/automations/${encodeURIComponent(id)}${suffix}`;\n\n/* ------------------------------------------------------------------ */\n/* Automations acknowledged over REST (create/patch responses, list */\n/* fallback) so views do not wait for the automation.updated event. */\n/* ------------------------------------------------------------------ */\n\ninterface LocalAutomations {\n byId: Record<string, Automation>;\n deleted: Record<string, true>;\n put(a: Automation): void;\n putMany(list: Automation[]): void;\n remove(id: string): void;\n}\n\nexport const useLocalAutomations = create<LocalAutomations>()((set) => ({\n byId: {},\n deleted: {},\n put: (a) => set((s) => ({ byId: { ...s.byId, [a.id]: a } })),\n putMany: (list) =>\n set((s) => {\n const byId = { ...s.byId };\n for (const a of list) byId[a.id] = a;\n return { byId };\n }),\n remove: (id) =>\n set((s) => {\n const byId = { ...s.byId };\n delete byId[id];\n return { byId, deleted: { ...s.deleted, [id]: true } };\n }),\n}));\n\nfunction newer(a: Automation | undefined, b: Automation | undefined): Automation | undefined {\n if (!a) return b;\n if (!b) return a;\n return b.updatedAt > a.updatedAt ? b : a;\n}\n\n/** Store automations merged with REST acknowledgements (newest updatedAt wins). */\nexport function useMergedAutomations(): Record<string, Automation> {\n const store = useSyncStore((s) => s.automations);\n const local = useLocalAutomations((s) => s.byId);\n const deleted = useLocalAutomations((s) => s.deleted);\n return useMemo(() => {\n const out: Record<string, Automation> = {};\n for (const id of new Set([...Object.keys(store), ...Object.keys(local)])) {\n if (deleted[id]) continue;\n const a = newer(store[id], local[id]);\n if (a) out[id] = a;\n }\n return out;\n }, [store, local, deleted]);\n}\n\nexport function useMergedAutomation(id: string | undefined): Automation | undefined {\n const fromStore = useSyncStore((s) => (id ? s.automations[id] : undefined));\n const fromLocal = useLocalAutomations((s) => (id ? s.byId[id] : undefined));\n const deleted = useLocalAutomations((s) => (id ? s.deleted[id] : undefined));\n return deleted ? undefined : newer(fromStore, fromLocal);\n}\n\n/** Enable/disable with no optimistic success: busy until the coordinator acknowledges. */\nexport function useToggleEnabled() {\n const [busy, setBusy] = useState<Record<string, boolean>>({});\n const toggle = useCallback(async (a: Pick<Automation, \"id\" | \"name\">, enabled: boolean) => {\n setBusy((b) => ({ ...b, [a.id]: true }));\n try {\n const res = await apiPatch<Automation | undefined>(automationPath(a.id), { enabled });\n if (res && typeof res === \"object\" && \"id\" in res) useLocalAutomations.getState().put(res);\n toast.success(`${a.name} ${enabled ? \"enabled\" : \"disabled\"}`);\n } catch (e) {\n toast.error(`Could not ${enabled ? \"enable\" : \"disable\"} ${a.name}: ${errorMessage(e)}`);\n } finally {\n setBusy((b) => ({ ...b, [a.id]: false }));\n }\n }, []);\n return { busy, toggle };\n}\n\n/** Manual \"Run now\": busy until acknowledged; never reports success early. */\nexport function useRunNow(onDone?: () => void) {\n const [busy, setBusy] = useState<Record<string, boolean>>({});\n const run = useCallback(\n async (a: Pick<Automation, \"id\" | \"name\">) => {\n setBusy((b) => ({ ...b, [a.id]: true }));\n try {\n const res = await apiPost<{ state?: string } | undefined>(automationPath(a.id, \"/run\"));\n const state = res && typeof res === \"object\" ? res.state : undefined;\n toast.success(\n state && state !== \"fired\"\n ? `${a.name}: occurrence recorded (${state.replace(/_/g, \" \")})`\n : `${a.name} started`,\n );\n onDone?.();\n } catch (e) {\n toast.error(`Could not run ${a.name}: ${errorMessage(e)}`);\n } finally {\n setBusy((b) => ({ ...b, [a.id]: false }));\n }\n },\n [onDone],\n );\n return { busy, run };\n}\n","import {\n type Automation,\n AutomationInput,\n type Occurrence,\n PRIORITY_LABELS,\n type Priority,\n type Trigger,\n} from \"@switchyard/protocol\";\n\n/*\n * Pure helpers for the Automations feature: a small cron describer, trigger\n * and whole-automation summaries, the editor draft <-> AutomationInput\n * conversion with field-level validation, and history merging.\n */\n\n/* ------------------------------------------------------------------ */\n/* Labels and explanations */\n/* ------------------------------------------------------------------ */\n\nexport type AutomationMode = AutomationInput[\"mode\"];\nexport type OverlapPolicy = AutomationInput[\"overlap\"];\nexport type MissedPolicy = AutomationInput[\"missed\"];\nexport type TriggerKind = Trigger[\"kind\"];\n\nexport const TRIGGER_KIND_LABELS: Record<TriggerKind, string> = {\n manual: \"Manual\",\n once: \"Once\",\n cron: \"Schedule\",\n webhook: \"Webhook\",\n dependency: \"Dependency\",\n};\n\nexport const MODE_OPTIONS: { value: AutomationMode; label: string; explanation: string }[] = [\n { value: \"fresh\", label: \"Fresh\", explanation: \"Fresh: a new task per occurrence\" },\n {\n value: \"continue\",\n label: \"Continue\",\n explanation: \"Continue: reuse the task with the same grouping key\",\n },\n {\n value: \"watch\",\n label: \"Watch\",\n explanation: \"Watch: only raises attention when something relevant changed\",\n },\n];\n\nexport const OVERLAP_OPTIONS: { value: OverlapPolicy; label: string; explanation: string }[] = [\n {\n value: \"skip\",\n label: \"Skip\",\n explanation: \"Skip: drop the occurrence while earlier work is still open\",\n },\n {\n value: \"queue_one\",\n label: \"Queue one\",\n explanation: \"Queue one: keep a single pending occurrence and fire it when the open work ends\",\n },\n {\n value: \"append\",\n label: \"Append\",\n explanation: \"Append: add the new objective to the open task (as a follow-up if it is running)\",\n },\n];\n\nexport const MISSED_OPTIONS: { value: MissedPolicy; label: string; explanation: string }[] = [\n {\n value: \"skip\",\n label: \"Skip\",\n explanation: \"Skip: occurrences missed while the coordinator was down are recorded and skipped\",\n },\n {\n value: \"run_once\",\n label: \"Run once\",\n explanation: \"Run once: on return, fire the newest missed occurrence once\",\n },\n];\n\nexport function modeLabel(mode: AutomationMode): string {\n return MODE_OPTIONS.find((m) => m.value === mode)?.label ?? mode;\n}\n\nexport function priorityLabel(p: Priority): string {\n return PRIORITY_LABELS[p] ?? String(p);\n}\n\nexport const TEMPLATE_VARIABLES = [\"{{date}}\", \"{{time}}\", \"{{occurrence}}\", \"{{payload.<path>}}\"];\n\n/* ------------------------------------------------------------------ */\n/* Text utilities */\n/* ------------------------------------------------------------------ */\n\n/** \"a\", \"a and b\", \"a, b and c\". */\nexport function listText(items: string[]): string {\n if (items.length <= 1) return items[0] ?? \"\";\n return `${items.slice(0, -1).join(\", \")} and ${items[items.length - 1]}`;\n}\n\nfunction pad(n: number): string {\n return String(n).padStart(2, \"0\");\n}\n\nexport function ordinal(n: number): string {\n const mod100 = n % 100;\n if (mod100 >= 11 && mod100 <= 13) return `${n}th`;\n switch (n % 10) {\n case 1:\n return `${n}st`;\n case 2:\n return `${n}nd`;\n case 3:\n return `${n}rd`;\n default:\n return `${n}th`;\n }\n}\n\nfunction capitalize(s: string): string {\n return s ? s[0]!.toUpperCase() + s.slice(1) : s;\n}\n\n/** Non-empty trimmed lines. */\nexport function lines(text: string): string[] {\n return text\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n}\n\n/* ------------------------------------------------------------------ */\n/* Cron */\n/* ------------------------------------------------------------------ */\n\nconst DAY_NAMES = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\nconst MONTH_NAMES = [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n];\n\ninterface FieldDef {\n name: string;\n min: number;\n max: number;\n /** Three-letter aliases mapped to `min + index`. */\n names?: string[];\n}\n\nconst CRON_FIELDS: FieldDef[] = [\n { name: \"Minute\", min: 0, max: 59 },\n { name: \"Hour\", min: 0, max: 23 },\n { name: \"Day of month\", min: 1, max: 31 },\n {\n name: \"Month\",\n min: 1,\n max: 12,\n names: MONTH_NAMES.map((m) => m.slice(0, 3).toLowerCase()),\n },\n {\n name: \"Day of week\",\n min: 0,\n max: 7,\n names: DAY_NAMES.map((d) => d.slice(0, 3).toLowerCase()),\n },\n];\n\nexport interface CronField {\n /** `*` (or `?`): every value. */\n any: boolean;\n /** Expanded, sorted, unique values (empty when `any`). */\n values: number[];\n /** Set when the field is a single `*\\/n` or `a/n` step. */\n step: { every: number; start: number } | null;\n /** Uses croner extensions (L, W, #) the describer does not explain. */\n extended: boolean;\n}\n\nfunction parseValue(token: string, def: FieldDef): number | string {\n const lower = token.toLowerCase();\n if (def.names) {\n const idx = def.names.indexOf(lower);\n if (idx >= 0) return def.min + idx;\n }\n if (!/^\\d+$/.test(token)) return `\"${token}\" is not a number`;\n const n = Number(token);\n if (n < def.min || n > def.max) return `${n} is out of range ${def.min}–${def.max}`;\n return n;\n}\n\n/** Parse one cron field; returns an error message for invalid input. */\nexport function parseCronField(src: string, def: FieldDef): CronField | string {\n if (src === \"*\" || src === \"?\") return { any: true, values: [], step: null, extended: false };\n if (/[LW#]/i.test(src) && !def.names?.some((n) => src.toLowerCase().includes(n))) {\n if (!/^[0-9A-Za-z*?,\\-/#]+$/.test(src)) return `\"${src}\" is not valid`;\n return { any: false, values: [], step: null, extended: true };\n }\n const parts = src.split(\",\");\n const values = new Set<number>();\n let step: CronField[\"step\"] = null;\n for (const part of parts) {\n if (!part) return \"empty list item\";\n const [base, stepStr, extra] = part.split(\"/\");\n if (extra !== undefined) return `\"${part}\" has more than one step`;\n let every = 1;\n if (stepStr !== undefined) {\n if (!/^\\d+$/.test(stepStr) || Number(stepStr) < 1) return `\"${stepStr}\" is not a valid step`;\n every = Number(stepStr);\n }\n let start: number;\n let end: number;\n if (base === \"*\" || base === \"?\") {\n start = def.min;\n end = def.max;\n } else if (base?.includes(\"-\")) {\n const [a, b] = base.split(\"-\");\n const av = parseValue(a ?? \"\", def);\n const bv = parseValue(b ?? \"\", def);\n if (typeof av === \"string\") return av;\n if (typeof bv === \"string\") return bv;\n if (av > bv) return `range ${base} runs backwards`;\n start = av;\n end = bv;\n } else {\n const v = parseValue(base ?? \"\", def);\n if (typeof v === \"string\") return v;\n start = v;\n end = stepStr !== undefined ? def.max : v;\n }\n for (let i = start; i <= end; i += every) values.add(i);\n if (parts.length === 1 && stepStr !== undefined && !base?.includes(\"-\")) {\n step = { every, start };\n }\n }\n let sorted = [...values].sort((a, b) => a - b);\n // Day of week: 7 is Sunday.\n if (def.max === 7) sorted = [...new Set(sorted.map((v) => v % 7))].sort((a, b) => a - b);\n const full = def.max === 7 ? 7 : def.max - def.min + 1;\n if (sorted.length === full && !step)\n return { any: true, values: [], step: null, extended: false };\n return { any: false, values: sorted, step, extended: false };\n}\n\nexport function parseCron(expr: string): CronField[] | string {\n const trimmed = expr.trim();\n if (!trimmed) return \"Enter a cron expression\";\n const fields = trimmed.split(/\\s+/);\n if (fields.length !== 5) {\n return `Expected 5 fields (minute hour day-of-month month day-of-week), got ${fields.length}`;\n }\n const out: CronField[] = [];\n for (let i = 0; i < 5; i++) {\n const def = CRON_FIELDS[i]!;\n const parsed = parseCronField(fields[i]!, def);\n if (typeof parsed === \"string\") return `${def.name}: ${parsed}`;\n out.push(parsed);\n }\n return out;\n}\n\n/** Null when valid, otherwise a message. */\nexport function validateCron(expr: string): string | null {\n const parsed = parseCron(expr);\n return typeof parsed === \"string\" ? parsed : null;\n}\n\n/** Null when the IANA timezone is known to this browser. */\nexport function validateTimezone(tz: string): string | null {\n if (!tz.trim()) return \"Enter a timezone\";\n try {\n new Intl.DateTimeFormat(undefined, { timeZone: tz.trim() });\n return null;\n } catch {\n return `Unknown timezone \"${tz}\"`;\n }\n}\n\nfunction isContiguous(values: number[]): boolean {\n for (let i = 1; i < values.length; i++) if (values[i] !== values[i - 1]! + 1) return false;\n return values.length > 0;\n}\n\ntype TimePhrase = { kind: \"at\"; text: string } | { kind: \"freq\"; text: string };\n\nfunction describeTime(m: CronField, h: CronField): TimePhrase | null {\n if (m.any && h.any) return { kind: \"freq\", text: \"Every minute\" };\n if (m.step && m.step.start === 0 && h.any) {\n return {\n kind: \"freq\",\n text: m.step.every === 1 ? \"Every minute\" : `Every ${m.step.every} minutes`,\n };\n }\n if (m.any) {\n if (h.step || h.values.length === 0) return null;\n if (isContiguous(h.values)) {\n return {\n kind: \"freq\",\n text: `Every minute from ${pad(h.values[0]!)}:00 to ${pad(h.values[h.values.length - 1]!)}:59`,\n };\n }\n return null;\n }\n if (h.any) {\n if (m.step) return null;\n if (m.values.length === 1 && m.values[0] === 0) return { kind: \"freq\", text: \"Every hour\" };\n return {\n kind: \"freq\",\n text: `Every hour at ${listText(m.values.map((v) => `:${pad(v)}`))}`,\n };\n }\n if (h.step && h.step.start === 0 && m.values.length === 1 && !m.step) {\n const at = m.values[0] === 0 ? \"\" : ` at :${pad(m.values[0]!)}`;\n return {\n kind: \"freq\",\n text: h.step.every === 1 ? `Every hour${at}` : `Every ${h.step.every} hours${at}`,\n };\n }\n if (m.values.length * h.values.length <= 6) {\n const times: string[] = [];\n for (const hv of h.values) for (const mv of m.values) times.push(`${pad(hv)}:${pad(mv)}`);\n return { kind: \"at\", text: listText(times) };\n }\n if (!isContiguous(h.values)) return null;\n const first = h.values[0]!;\n const last = h.values[h.values.length - 1]!;\n if (m.step && m.step.start === 0) {\n return {\n kind: \"freq\",\n text: `Every ${m.step.every} minutes from ${pad(first)}:00 to ${pad(last)}:59`,\n };\n }\n if (m.values.length === 1) {\n const mm = pad(m.values[0]!);\n return { kind: \"freq\", text: `Every hour from ${pad(first)}:${mm} to ${pad(last)}:${mm}` };\n }\n return null;\n}\n\n/** Monday-first ordering for day-of-week lists. */\nfunction mondayFirst(a: number, b: number): number {\n return ((a + 6) % 7) - ((b + 6) % 7);\n}\n\nfunction describeDays(\n dom: CronField,\n mon: CronField,\n dow: CronField,\n): { lead: string; qual: string } | null {\n if (!dom.any && !dow.any) return null;\n const months = mon.any ? null : listText(mon.values.map((v) => MONTH_NAMES[v - 1]!));\n const inMonths = months ? ` in ${months}` : \"\";\n if (!dow.any) {\n const days = [...dow.values].sort(mondayFirst);\n const key = days.join(\",\");\n if (key === \"1,2,3,4,5\") {\n return { lead: `Every weekday${inMonths}`, qual: ` on weekdays${inMonths}` };\n }\n if (key === \"6,0\") {\n return { lead: `Every Saturday and Sunday${inMonths}`, qual: ` on weekends${inMonths}` };\n }\n const names = days.map((d) => DAY_NAMES[d]!);\n return {\n lead: `Every ${listText(names)}${inMonths}`,\n qual: ` on ${listText(names.map((n) => `${n}s`))}${inMonths}`,\n };\n }\n if (!dom.any) {\n if (dom.values.length > 5) return null;\n const ords = listText(dom.values.map(ordinal));\n const of = months ? months : \"every month\";\n return { lead: `On the ${ords} of ${of}`, qual: ` on the ${ords} of ${of}` };\n }\n return { lead: `Every day${inMonths}`, qual: inMonths };\n}\n\n/**\n * Human description of a 5-field cron expression (\"Every weekday at 09:00\n * (America/Denver)\"), or null when the expression is outside what this small\n * describer covers.\n */\nexport function describeCron(expr: string, timezone?: string): string | null {\n const parsed = parseCron(expr);\n if (typeof parsed === \"string\") return null;\n if (parsed.some((f) => f.extended)) return null;\n const [m, h, dom, mon, dow] = parsed as [CronField, CronField, CronField, CronField, CronField];\n const time = describeTime(m, h);\n const days = describeDays(dom, mon, dow);\n if (!time || !days) return null;\n const text = time.kind === \"at\" ? `${days.lead} at ${time.text}` : `${time.text}${days.qual}`;\n return timezone ? `${text} (${timezone})` : text;\n}\n\n/** describeCron with the raw-expression fallback. */\nexport function describeCronOrRaw(expr: string, timezone: string): string {\n return describeCron(expr, timezone) ?? `Cron ${expr.trim()} (${timezone})`;\n}\n\n/* ------------------------------------------------------------------ */\n/* Summaries */\n/* ------------------------------------------------------------------ */\n\nexport function formatAbsolute(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return iso;\n return d.toLocaleString(undefined, { dateStyle: \"medium\", timeStyle: \"short\" });\n}\n\nexport interface SummaryContext {\n projectName?: string | null;\n /** Name of the explicitly selected host; null/undefined means the project default. */\n hostName?: string | null;\n profileName?: string | null;\n taskTitle?: (taskId: string) => string | undefined;\n formatDate?: (iso: string) => string;\n}\n\n/** Short trigger summary for list rows. */\nexport function describeTrigger(trigger: Trigger, ctx: SummaryContext = {}): string {\n const fmt = ctx.formatDate ?? formatAbsolute;\n switch (trigger.kind) {\n case \"manual\":\n return \"Manual\";\n case \"once\":\n return trigger.at && !Number.isNaN(Date.parse(trigger.at))\n ? `Once at ${fmt(trigger.at)}`\n : \"Once (no time chosen)\";\n case \"cron\":\n return describeCronOrRaw(trigger.expr, trigger.timezone);\n case \"webhook\":\n return \"Webhook\";\n case \"dependency\": {\n const title = trigger.taskId ? ctx.taskTitle?.(trigger.taskId) : undefined;\n if (title) return `When \"${title}\" is done`;\n return trigger.taskId ? \"When a deleted task is done\" : \"When a task is done\";\n }\n }\n}\n\ntype SummaryInput = Pick<\n AutomationInput,\n | \"trigger\"\n | \"filters\"\n | \"template\"\n | \"mode\"\n | \"groupingKey\"\n | \"overlap\"\n | \"missed\"\n | \"continuation\"\n | \"completion\"\n>;\n\nfunction triggerClause(input: SummaryInput, ctx: SummaryContext): string {\n const t = input.trigger;\n switch (t.kind) {\n case \"manual\":\n return \"When run manually\";\n case \"webhook\": {\n const filters = input.filters.filter((f) => f.path.trim());\n if (filters.length === 0) return \"When a webhook is received\";\n return `When a webhook is received where ${listText(\n filters.map((f) => `${f.path.trim()} = \"${f.equals}\"`),\n )}`;\n }\n default:\n return describeTrigger(t, ctx);\n }\n}\n\n/**\n * One-paragraph description of what the automation does, e.g. 'Every weekday\n * at 09:00 (America/Denver), create a fresh task \"Triage\" in Web on my-server; skip\n * if previous work is still open; skip missed runs; requires human review'.\n */\nexport function summarizeAutomation(input: SummaryInput, ctx: SummaryContext = {}): string {\n const title = input.template.title.trim() || \"Untitled task\";\n const project = ctx.projectName ? ` in ${ctx.projectName}` : \"\";\n const host = ctx.hostName ? ` on ${ctx.hostName}` : \" on the project's default host\";\n const profile = ctx.profileName ? ` with profile ${ctx.profileName}` : \"\";\n let action: string;\n switch (input.mode) {\n case \"continue\": {\n const key = input.groupingKey?.trim();\n action = key\n ? `continue the task grouped by \"${key}\" (or create \"${title}\")`\n : `continue the matching task (or create \"${title}\")`;\n break;\n }\n case \"watch\":\n action = `create a watch task \"${title}\" that only raises attention when something relevant changed`;\n break;\n default:\n action = `create a fresh task \"${title}\"`;\n }\n const parts = [`${triggerClause(input, ctx)}, ${action}${project}${host}${profile}`];\n parts.push(\n input.overlap === \"skip\"\n ? \"skip if previous work is still open\"\n : input.overlap === \"queue_one\"\n ? \"queue one occurrence if previous work is still open\"\n : \"append to the open task if previous work is still open\",\n );\n if (input.trigger.kind === \"cron\" || input.trigger.kind === \"once\") {\n parts.push(input.missed === \"skip\" ? \"skip missed runs\" : \"run missed runs once on return\");\n }\n if (input.continuation.enabled) {\n const c = input.continuation;\n parts.push(\n `continue automatically up to ${c.maxContinuations} times within ${c.maxElapsedMinutes} minutes`,\n );\n }\n const checks = input.completion.checks;\n if (checks && checks.length > 0) {\n parts.push(`run ${checks.length === 1 ? \"1 check\" : `${checks.length} checks`}`);\n }\n parts.push(\n input.completion.requireHumanReview ? \"requires human review\" : \"no human review required\",\n );\n return capitalize(parts.join(\"; \"));\n}\n\n/* ------------------------------------------------------------------ */\n/* Editor draft */\n/* ------------------------------------------------------------------ */\n\n/** Form state: strings for inputs so partial edits survive validation. */\nexport interface AutomationDraft {\n name: string;\n enabled: boolean;\n projectId: string;\n triggerKind: TriggerKind;\n /** datetime-local value (local time, no zone). */\n onceAt: string;\n cronExpr: string;\n cronTimezone: string;\n dependencyTaskId: string;\n filters: { path: string; equals: string }[];\n title: string;\n objective: string;\n /** One criterion per line. */\n criteria: string;\n priority: Priority;\n /** \"\" = project default. */\n hostId: string;\n /** \"\" = project default. */\n profileId: string;\n mode: AutomationMode;\n groupingKey: string;\n overlap: OverlapPolicy;\n missed: MissedPolicy;\n continuationEnabled: boolean;\n continuationObjective: string;\n maxContinuations: string;\n maxElapsedMinutes: string;\n maxTokens: string;\n maxCostUsd: string;\n /** One check per line; empty = project default (null). */\n checks: string;\n requireHumanReview: boolean;\n}\n\nexport function localTimezone(): string {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || \"UTC\";\n } catch {\n return \"UTC\";\n }\n}\n\nexport function emptyDraft(opts: { projectId?: string; timezone?: string } = {}): AutomationDraft {\n return {\n name: \"\",\n enabled: true,\n projectId: opts.projectId ?? \"\",\n triggerKind: \"manual\",\n onceAt: \"\",\n cronExpr: \"0 9 * * 1-5\",\n cronTimezone: opts.timezone ?? localTimezone(),\n dependencyTaskId: \"\",\n filters: [],\n title: \"\",\n objective: \"\",\n criteria: \"\",\n priority: 2,\n hostId: \"\",\n profileId: \"\",\n mode: \"fresh\",\n groupingKey: \"\",\n overlap: \"skip\",\n missed: \"skip\",\n continuationEnabled: false,\n continuationObjective: \"\",\n maxContinuations: \"5\",\n maxElapsedMinutes: \"60\",\n maxTokens: \"\",\n maxCostUsd: \"\",\n checks: \"\",\n requireHumanReview: true,\n };\n}\n\n/** ISO -> datetime-local value in this browser's timezone. */\nexport function isoToLocalInput(iso: string | null | undefined): string {\n if (!iso) return \"\";\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return \"\";\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;\n}\n\n/** datetime-local value -> ISO (interpreted as local time), or \"\" when empty/invalid. */\nexport function localInputToIso(value: string): string {\n if (!value) return \"\";\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? \"\" : d.toISOString();\n}\n\nexport function draftFromAutomation(\n a: Automation | AutomationInput,\n opts: { timezone?: string } = {},\n): AutomationDraft {\n const base = emptyDraft({ projectId: a.projectId, timezone: opts.timezone });\n const t = a.trigger;\n const c = a.continuation;\n return {\n ...base,\n name: a.name,\n enabled: a.enabled,\n triggerKind: t.kind,\n onceAt: t.kind === \"once\" ? isoToLocalInput(t.at) : \"\",\n cronExpr: t.kind === \"cron\" ? t.expr : base.cronExpr,\n cronTimezone: t.kind === \"cron\" ? t.timezone : base.cronTimezone,\n dependencyTaskId: t.kind === \"dependency\" ? t.taskId : \"\",\n filters: a.filters.map((f) => ({ ...f })),\n title: a.template.title,\n objective: a.template.objective,\n criteria: a.template.acceptanceCriteria.join(\"\\n\"),\n priority: a.template.priority,\n hostId: a.hostId ?? \"\",\n profileId: a.profileId ?? \"\",\n mode: a.mode,\n groupingKey: a.groupingKey ?? \"\",\n overlap: a.overlap,\n missed: a.missed,\n continuationEnabled: c.enabled,\n continuationObjective: c.enabled ? c.objective : \"\",\n maxContinuations: c.enabled ? String(c.maxContinuations) : base.maxContinuations,\n maxElapsedMinutes: c.enabled ? String(c.maxElapsedMinutes) : base.maxElapsedMinutes,\n maxTokens: c.enabled && c.maxTokens !== null ? String(c.maxTokens) : \"\",\n maxCostUsd: c.enabled && c.maxCostUsd !== null ? String(c.maxCostUsd) : \"\",\n checks: a.completion.checks ? a.completion.checks.join(\"\\n\") : \"\",\n requireHumanReview: a.completion.requireHumanReview,\n };\n}\n\nfunction toNumber(s: string): number {\n return s.trim() === \"\" ? Number.NaN : Number(s.trim());\n}\n\nfunction toOptionalNumber(s: string): number | null {\n return s.trim() === \"\" ? null : Number(s.trim());\n}\n\nfunction draftTrigger(d: AutomationDraft): Trigger {\n switch (d.triggerKind) {\n case \"manual\":\n return { kind: \"manual\" };\n case \"once\":\n return { kind: \"once\", at: localInputToIso(d.onceAt) };\n case \"cron\":\n return { kind: \"cron\", expr: d.cronExpr.trim(), timezone: d.cronTimezone.trim() };\n case \"webhook\":\n return { kind: \"webhook\" };\n case \"dependency\":\n return { kind: \"dependency\", taskId: d.dependencyTaskId };\n }\n}\n\n/**\n * Build the request body from a draft. The result is shaped like an\n * AutomationInput but is not validated (numbers may be NaN, strings empty);\n * run validateDraft before sending it.\n */\nexport function draftToInput(d: AutomationDraft): AutomationInput {\n const checks = lines(d.checks);\n return {\n name: d.name.trim(),\n enabled: d.enabled,\n projectId: d.projectId,\n trigger: draftTrigger(d),\n filters: d.filters\n .filter((f) => f.path.trim() || f.equals.trim())\n .map((f) => ({ path: f.path.trim(), equals: f.equals })),\n template: {\n title: d.title.trim(),\n objective: d.objective,\n acceptanceCriteria: lines(d.criteria),\n priority: d.priority,\n },\n hostId: d.hostId || null,\n profileId: d.profileId || null,\n mode: d.mode,\n groupingKey: d.groupingKey.trim() || null,\n overlap: d.overlap,\n missed: d.missed,\n continuation: d.continuationEnabled\n ? {\n enabled: true,\n objective: d.continuationObjective.trim(),\n maxContinuations: toNumber(d.maxContinuations),\n maxElapsedMinutes: toNumber(d.maxElapsedMinutes),\n maxTokens: toOptionalNumber(d.maxTokens),\n maxCostUsd: toOptionalNumber(d.maxCostUsd),\n }\n : { enabled: false },\n completion: {\n checks: checks.length > 0 ? checks : null,\n requireHumanReview: d.requireHumanReview,\n },\n };\n}\n\n/** Draft field keys that can carry an inline error. */\nexport type DraftField =\n | keyof AutomationDraft\n | `filters.${number}.path`\n | `filters.${number}.equals`\n | \"form\";\n\nexport type DraftErrors = Partial<Record<DraftField, string>>;\n\nconst PATH_TO_FIELD: Record<string, DraftField> = {\n name: \"name\",\n enabled: \"enabled\",\n projectId: \"projectId\",\n trigger: \"triggerKind\",\n \"trigger.kind\": \"triggerKind\",\n \"trigger.at\": \"onceAt\",\n \"trigger.expr\": \"cronExpr\",\n \"trigger.timezone\": \"cronTimezone\",\n \"trigger.taskId\": \"dependencyTaskId\",\n \"template.title\": \"title\",\n \"template.objective\": \"objective\",\n \"template.acceptanceCriteria\": \"criteria\",\n \"template.priority\": \"priority\",\n hostId: \"hostId\",\n profileId: \"profileId\",\n mode: \"mode\",\n groupingKey: \"groupingKey\",\n overlap: \"overlap\",\n missed: \"missed\",\n \"continuation.objective\": \"continuationObjective\",\n \"continuation.maxContinuations\": \"maxContinuations\",\n \"continuation.maxElapsedMinutes\": \"maxElapsedMinutes\",\n \"continuation.maxTokens\": \"maxTokens\",\n \"continuation.maxCostUsd\": \"maxCostUsd\",\n \"completion.checks\": \"checks\",\n \"completion.requireHumanReview\": \"requireHumanReview\",\n};\n\n/** Map a zod issue path to a draft field. */\nexport function fieldForPath(path: readonly PropertyKey[]): DraftField {\n const parts = path.map(String);\n if (parts[0] === \"filters\" && parts.length >= 3) {\n return `filters.${Number(parts[1])}.${parts[2] === \"equals\" ? \"equals\" : \"path\"}`;\n }\n if (parts[0] === \"template\" && parts[1] === \"acceptanceCriteria\") return \"criteria\";\n if (parts[0] === \"completion\" && parts[1] === \"checks\") return \"checks\";\n for (let n = parts.length; n > 0; n--) {\n const f = PATH_TO_FIELD[parts.slice(0, n).join(\".\")];\n if (f) return f;\n }\n return \"form\";\n}\n\nfunction intError(s: string, min: number, max: number, label: string): string | null {\n const n = toNumber(s);\n if (!Number.isInteger(n) || n < min || n > max) {\n return `${label} must be a whole number from ${min} to ${max}`;\n }\n return null;\n}\n\n/**\n * Validate a draft: friendly checks first (required fields, cron, timezone,\n * number ranges), then AutomationInput.safeParse for anything else.\n */\nexport function validateDraft(d: AutomationDraft): {\n input: AutomationInput | null;\n errors: DraftErrors;\n} {\n const errors: DraftErrors = {};\n const set = (f: DraftField, msg: string | null) => {\n if (msg && !errors[f]) errors[f] = msg;\n };\n if (!d.name.trim()) set(\"name\", \"Name is required\");\n if (!d.projectId) set(\"projectId\", \"Choose a project\");\n if (!d.title.trim()) set(\"title\", \"Task title is required\");\n switch (d.triggerKind) {\n case \"once\":\n if (!localInputToIso(d.onceAt)) set(\"onceAt\", \"Choose a date and time\");\n break;\n case \"cron\":\n set(\"cronExpr\", validateCron(d.cronExpr));\n set(\"cronTimezone\", validateTimezone(d.cronTimezone));\n break;\n case \"dependency\":\n if (!d.dependencyTaskId) set(\"dependencyTaskId\", \"Choose the task to wait for\");\n break;\n }\n d.filters.forEach((f, i) => {\n if (!f.path.trim() && f.equals.trim()) set(`filters.${i}.path`, \"Enter a payload path\");\n });\n if (d.mode === \"continue\" && !d.groupingKey.trim()) {\n set(\"groupingKey\", \"A grouping key is required to continue an existing task\");\n }\n if (d.continuationEnabled) {\n if (!d.continuationObjective.trim()) {\n set(\"continuationObjective\", \"An objective is required when continuation is on\");\n }\n set(\"maxContinuations\", intError(d.maxContinuations, 1, 50, \"Max continuations\"));\n set(\"maxElapsedMinutes\", intError(d.maxElapsedMinutes, 1, 1440, \"Max elapsed minutes\"));\n if (d.maxTokens.trim()) {\n const n = toNumber(d.maxTokens);\n if (!Number.isInteger(n) || n <= 0)\n set(\"maxTokens\", \"Max tokens must be a positive whole number\");\n }\n if (d.maxCostUsd.trim()) {\n const n = toNumber(d.maxCostUsd);\n if (!Number.isFinite(n) || n <= 0) set(\"maxCostUsd\", \"Max cost must be a positive amount\");\n }\n }\n const raw = draftToInput(d);\n const parsed = AutomationInput.safeParse(raw);\n if (!parsed.success) {\n for (const issue of parsed.error.issues) set(fieldForPath(issue.path), issue.message);\n }\n const ok = parsed.success && Object.keys(errors).length === 0;\n return { input: ok ? parsed.data : null, errors };\n}\n\n/* ------------------------------------------------------------------ */\n/* History */\n/* ------------------------------------------------------------------ */\n\nexport function occurrenceTime(o: Occurrence): string {\n return o.scheduledFor ?? o.createdAt;\n}\n\n/** Union by id (later lists win), newest first. */\nexport function mergeOccurrences(...lists: (Occurrence[] | undefined | null)[]): Occurrence[] {\n const byId = new Map<string, Occurrence>();\n for (const list of lists) for (const o of list ?? []) byId.set(o.id, o);\n return [...byId.values()].sort(\n (a, b) =>\n b.createdAt.localeCompare(a.createdAt) || occurrenceTime(b).localeCompare(occurrenceTime(a)),\n );\n}\n\n/** Accept `Occurrence[]` or `{items|occurrences: Occurrence[]}`. */\nexport function normalizeHistory(res: unknown): Occurrence[] {\n if (Array.isArray(res)) return res as Occurrence[];\n if (res && typeof res === \"object\") {\n const r = res as { items?: unknown; occurrences?: unknown };\n if (Array.isArray(r.items)) return r.items as Occurrence[];\n if (Array.isArray(r.occurrences)) return r.occurrences as Occurrence[];\n }\n return [];\n}\n\nexport function curlExample(token: string, url: string): string {\n return `curl -X POST -H 'Authorization: Bearer ${token}' -H 'Content-Type: application/json' -d '{}' ${url}`;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{_ as e,a as t,f as n,m as r,n as i,t as a}from"./jsx-runtime-D13aM9HY.js";import{bt as o,c as s,d as c,gt as l,n as u,p as d,v as f,vt as p}from"./states-C773NlHo.js";import{o as m,s as h,t as g}from"./auth-308hUVgi.js";import{t as _}from"./key-round-VDbE9E4X.js";import{r as v}from"./index-BHGZTS2e.js";import{t as y}from"./TextLink-Bub7sMxr.js";import{a as b,c as x,i as S,l as C,n as w,o as T,r as E,s as D,t as O,u as k}from"./logic-B46fwD-8.js";var A={name:`log-in`,size:24,node:[[`path`,{d:`m10 17 5-5-5-5`,key:`1bsop3`}],[`path`,{d:`M15 12H3`,key:`6jk70r`}],[`path`,{d:`M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4`,key:`u53s6r`}]]};A.node;var j=i(A),M=e(r(),1),N=a();function P(){let e=new URLSearchParams(location.hash.replace(/^#/,``)),t=new URLSearchParams(location.search);return e.get(`token`)??t.get(`token`)}function F(){let e=n(),[r]=t(),i=r.get(`next`),a=(0,M.useMemo)(P,[]),l=h(e=>e.status),u=h(e=>e.signedOutReason),d=(0,M.useMemo)(()=>E(),[]),[f,y]=(0,M.useState)(!1),[x,S]=(0,M.useState)(null);return(0,M.useEffect)(()=>{a&&history.replaceState(history.state,``,location.pathname+location.search)},[a]),(0,M.useEffect)(()=>{!a&&l===`authenticated`&&e(m(i),{replace:!0})},[a,l,i,e]),!a&&l===`checking`?(0,N.jsx)(s,{label:`Checking this device…`}):(0,N.jsx)(`div`,{className:`flex min-h-dvh items-start justify-center px-4 safe-pt-10 safe-pb-10 sm:items-center`,children:(0,N.jsxs)(`div`,{className:`w-full max-w-sm rounded-xl border border-border bg-surface p-5 shadow-sm`,children:[(0,N.jsxs)(`div`,{className:`mb-4 flex items-center gap-3`,children:[(0,N.jsx)(`img`,{src:`/icon.svg`,alt:``,className:`size-10 rounded-lg`}),(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`h1`,{className:`text-lg font-semibold`,children:`Switchyard`}),(0,N.jsx)(`p`,{className:`text-sm text-muted`,children:a?`Sign in this browser`:`Sign in`})]})]}),u&&(0,N.jsx)(`p`,{role:`status`,className:`mb-3 rounded-md bg-warning-soft px-3 py-2 text-sm`,children:u}),a?(0,N.jsxs)(`form`,{onSubmit:async t=>{if(t.preventDefault(),a){y(!0),S(null);try{await p(`/api/enroll`,{token:a,name:d.slice(0,80),kind:v()?`pwa`:`browser`}),await g()===`authenticated`?e(m(i),{replace:!0}):S({code:`internal`,message:`Signed in, but the coordinator did not accept the new credential. Try again.`})}catch(e){let t=o(e)?e.code:`internal`;S({code:t,message:O[t]??(e instanceof Error?e.message:String(e))})}finally{y(!1)}}},className:`flex flex-col gap-3`,noValidate:!0,children:[(0,N.jsxs)(`p`,{className:`text-sm`,children:[`This single-use link signs in this browser as “`,d,`”. You can rename it in Settings › Devices.`]}),x&&(0,N.jsxs)(`div`,{role:`alert`,className:`rounded-md border border-border bg-danger-soft px-3 py-2 text-sm`,children:[(0,N.jsx)(`div`,{className:`font-medium`,children:b(x.code)}),(0,N.jsx)(`div`,{className:`text-muted`,children:x.message})]}),(0,N.jsx)(c,{type:`submit`,variant:`primary`,"data-testid":`signin-link`,busy:f,disabled:x?.code===`enrollment_expired`||x?.code===`enrollment_used`,icon:(0,N.jsx)(_,{className:`size-4`}),children:`Sign in`})]}):(0,N.jsx)(R,{next:i})]})})}var I=()=>v()?`pwa`:`browser`;function L({title:e,children:t}){return(0,N.jsxs)(`div`,{role:`alert`,className:`rounded-md border border-border bg-danger-soft px-3 py-2 text-sm`,children:[(0,N.jsxs)(`div`,{className:`flex items-center gap-1.5 font-medium`,children:[(0,N.jsx)(f,{"aria-hidden":!0,className:`size-4 shrink-0`}),e]}),(0,N.jsx)(`div`,{className:`break-words text-muted`,children:t})]})}function R({next:e}){let r=n(),i=(0,M.useMemo)(()=>E().slice(0,80),[]),[a,f]=t(),[h,y]=(0,M.useState)(null),[b,x]=(0,M.useState)(null),[w,T]=(0,M.useState)(0),[D,O]=(0,M.useState)(null),[A,P]=(0,M.useState)(()=>{let e=a.get(`error`);return e?{code:e,message:S(e,a.get(`detail`))}:null}),[F,R]=(0,M.useState)(null),B=(0,M.useRef)(null),V=C();(0,M.useEffect)(()=>{if(!a.has(`error`)&&!a.has(`detail`))return;let e=new URLSearchParams(a);e.delete(`error`),e.delete(`detail`),f(e,{replace:!0})},[]),(0,M.useEffect)(()=>{let e=new AbortController;return x(null),l(`/api/setup`,void 0,e.signal).then(t=>!e.signal.aborted&&y(t)).catch(t=>!e.signal.aborted&&x(t)),()=>e.abort()},[w]);let H=e=>{let t=o(e)?e.code:`internal`;e instanceof Error&&e.name===`NotAllowedError`?P({code:t,message:`Passkey sign-in was canceled or timed out.`}):P({code:t,message:o(e)?S(t,e.message):e instanceof Error?e.message:String(e)})},U=(0,M.useCallback)(async()=>{await g()===`authenticated`?r(m(e),{replace:!0}):P({code:`internal`,message:`Signed in, but the coordinator did not accept the new credential. Try again.`})},[r,e]),W=async()=>{O(`passkey`),P(null);try{let{challengeId:e,options:t}=await p(`/api/auth/passkey/options`),n=await V.run(()=>k({optionsJSON:t}));if(!n.ok){P({code:`canceled`,message:`Passkey sign-in was canceled.`});return}let r=n.value;await p(`/api/auth/passkey/verify`,{challengeId:e,response:r,deviceName:i,kind:I()}),await U()}catch(e){H(e)}finally{O(null)}},G=async()=>{O(`oidc`),P(null);let t=v();try{let n=await p(`/api/auth/oidc/begin`,{deviceName:i,kind:I(),next:m(e),handoff:t});if(t&&n.flowId&&n.pollToken)B.current=n.authorizeUrl,R({flowId:n.flowId,pollToken:n.pollToken}),window.open(n.authorizeUrl,`_blank`);else{location.assign(n.authorizeUrl);return}}catch(e){H(e)}O(null)};if(b)return(0,N.jsx)(u,{title:`Cannot reach Switchyard`,error:b,onRetry:()=>T(e=>e+1)});if(!h)return(0,N.jsx)(s,{label:`Checking this server…`});if(F)return(0,N.jsx)(z,{flowId:F.flowId,pollToken:F.pollToken,authorizeUrl:B.current,onDone:U,onExit:()=>R(null)});let K=h.passkey.registered&&h.passkey.available;return(0,N.jsxs)(`div`,{className:`flex flex-col gap-4 text-sm`,children:[(0,N.jsx)(`p`,{className:`text-base font-medium`,children:`Sign in to Switchyard`}),A&&(0,N.jsx)(L,{title:`Sign-in failed`,children:A.message}),(0,N.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[h.oidc&&(0,N.jsx)(c,{"data-testid":`signin-oidc`,variant:`primary`,busy:D===`oidc`,disabled:D!==null,icon:(0,N.jsx)(j,{className:`size-4`}),onClick:G,children:h.oidc.label}),K&&(0,N.jsx)(c,{"data-testid":`signin-passkey`,variant:h.oidc?`secondary`:`primary`,busy:D===`passkey`,disabled:D!==null,icon:(0,N.jsx)(_,{className:`size-4`}),onClick:W,children:`Sign in with a passkey`}),V.waiting&&(0,N.jsxs)(`div`,{role:`status`,className:`flex items-center gap-2 text-sm text-muted`,children:[(0,N.jsx)(d,{}),(0,N.jsx)(`span`,{className:`flex-1`,children:`Waiting for your passkey…`}),(0,N.jsx)(c,{size:`sm`,variant:`ghost`,onClick:V.cancel,children:`Cancel`})]}),h.passkey.registered&&!h.passkey.available&&h.passkey.reason&&(0,N.jsxs)(`p`,{className:`text-xs text-muted`,children:[`Passkeys are unavailable: `,h.passkey.reason,`.`]})]}),!h.oidc&&!K?(0,N.jsxs)(`p`,{"data-testid":`signin-pair-hint`,className:`text-muted`,children:[`No sign-in method is set up. On the machine running Switchyard, run`,` `,(0,N.jsx)(`code`,{children:`switchyard pair`}),` for a single-use sign-in link.`]}):(0,N.jsxs)(`p`,{className:`text-xs text-muted`,children:[`Locked out? Run `,(0,N.jsx)(`code`,{children:`switchyard pair`}),` on the server for a single-use sign-in link.`]})]})}function z({flowId:e,pollToken:t,authorizeUrl:n,onDone:r,onExit:i}){let[a,s]=(0,M.useState)(()=>x(Date.now())),l=(0,M.useRef)(a);l.current=a;let u=(0,M.useRef)(!1),d=(0,M.useRef)(r);d.current=r;let f=(0,M.useCallback)(e=>{let t=T(l.current,e);return l.current=t,s(t),t},[]),m=(0,M.useCallback)(async()=>{if(!(u.current||l.current.phase!==`waiting`)){u.current=!0;try{let n=await p(`/api/auth/oidc/claim`,{flowId:e,pollToken:t});f({type:`claim`,result:n,now:Date.now()}).phase===`done`&&await d.current()}catch(e){f({type:`claim_error`,code:o(e)?e.code:`network`,message:`This sign-in expired or was already used. Start again.`,now:Date.now()})}finally{u.current=!1}}},[e,t,f]);return(0,M.useEffect)(()=>{let e=()=>document.visibilityState===`visible`,t=setInterval(()=>{f({type:`tick`,now:Date.now()}),D(l.current,e())&&m()},w),n=()=>{D(l.current,e())&&m()};return document.addEventListener(`visibilitychange`,n),()=>{clearInterval(t),document.removeEventListener(`visibilitychange`,n)}},[m,f]),a.phase===`timeout`||a.phase===`failed`||a.phase===`canceled`?(0,N.jsxs)(`div`,{"data-testid":`signin-handoff`,className:`flex flex-col gap-3 text-sm`,children:[(0,N.jsx)(L,{title:a.phase===`timeout`?`Sign-in timed out`:`Sign-in stopped`,children:a.phase===`timeout`?`The sign-in was not finished in time. Start again.`:a.message??`Start again.`}),(0,N.jsx)(c,{onClick:i,children:`Back to sign-in`})]}):(0,N.jsxs)(`div`,{"data-testid":`signin-handoff`,role:`status`,className:`flex flex-col gap-3 text-sm`,children:[(0,N.jsx)(`p`,{className:`font-medium`,children:`Finish signing in, then come back here`}),(0,N.jsx)(`p`,{className:`text-muted`,children:`Complete single sign-on in the page that opened, then return to this app. This screen continues on its own.`}),n&&(0,N.jsx)(y,{href:n,external:!0,children:`Open the sign-in page again`}),(0,N.jsx)(c,{onClick:()=>{f({type:`cancel`}),i()},children:`Cancel`})]})}export{F as default,E as defaultDeviceName};
2
+ //# sourceMappingURL=enroll-jnKygG4L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enroll-jnKygG4L.js","names":["useMemo","useState","useRef","useCallback","__toESM","useSearchParams","useNavigate","require_react","createLucideIcon","require_jsx_runtime","isApiError","LoadingState","Button","apiGet","ErrorState","Spinner","TriangleAlert","apiPost","takeReturnTo","useAuth","checkAuth","KeyRound","isStandalone","TextLink","enrollErrorTitle","startHandoff","enrollErrorText","usePasskeyCeremony","HANDOFF_POLL_MS","handoffReduce","defaultDeviceName","shouldPoll","ERROR_TEXT","startAuthentication","__iconData","LogIn","import_react","import_jsx_runtime","readToken","hash","query","EnrollView","navigate","params","next","token","status","s","signedOutReason","deviceName","busy","setBusy","error","setError","e","err","code","SignInPanel","kindOf","AlertBox","title","setParams","setup","setSetup","loadError","setLoadError","nonce","setNonce","notice","setNotice","handoff","setHandoff","authorizeUrlRef","ceremony","rest","ac","v","fail","finish","signInWithPasskey","result","options","response","signInWithOidc","standalone","res","n","HandoffScreen","passkeyReady","state","setState","stateRef","inFlight","doneRef","onDone","apply","event","poll","flowId","pollToken","visible","timer","onVisible","onExit","authorizeUrl"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/log-in.mjs","../../src/features/enroll/index.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"log-in\",\n size: 24,\n node: [\n [\"path\", { d: \"m10 17 5-5-5-5\", key: \"1bsop3\" }],\n [\"path\", { d: \"M15 12H3\", key: \"6jk70r\" }],\n [\"path\", { d: \"M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4\", key: \"u53s6r\" }]\n ]\n};\n__iconData.node;\nconst LogIn = createLucideIcon(__iconData);\n\nexport { __iconData, LogIn as default };\n//# sourceMappingURL=log-in.mjs.map\n","import { startAuthentication } from \"@simplewebauthn/browser\";\nimport type { OidcBeginResponse, OidcClaimResponse, SetupState } from \"@switchyard/protocol\";\nimport { KeyRound, LogIn, TriangleAlert } from \"lucide-react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useNavigate, useSearchParams } from \"react-router\";\nimport { checkAuth, takeReturnTo, useAuth } from \"../../app/auth\";\nimport { usePasskeyCeremony } from \"../../app/passkeyCeremony\";\nimport { Button } from \"../../components/ui/Button\";\nimport { Spinner } from \"../../components/ui/Spinner\";\nimport { ErrorState, LoadingState } from \"../../components/ui/states\";\nimport { TextLink } from \"../../components/ui/TextLink\";\nimport { isStandalone } from \"../../push\";\nimport { apiGet, apiPost, isApiError } from \"../../sync/api\";\nimport {\n defaultDeviceName,\n ERROR_TEXT,\n enrollErrorText,\n enrollErrorTitle,\n HANDOFF_POLL_MS,\n type HandoffState,\n handoffReduce,\n shouldPoll,\n startHandoff,\n} from \"./logic\";\n\nexport { defaultDeviceName };\n\nfunction readToken(): string | null {\n const hash = new URLSearchParams(location.hash.replace(/^#/, \"\"));\n const query = new URLSearchParams(location.search);\n return hash.get(\"token\") ?? query.get(\"token\");\n}\n\n/**\n * /sign-in: redeem a single-use sign-in link token from `#token=`, or (without\n * one) sign in with single sign-on or a passkey, then\n * return to the deep link that required authentication.\n */\nexport default function EnrollView() {\n const navigate = useNavigate();\n const [params] = useSearchParams();\n const next = params.get(\"next\");\n const token = useMemo(readToken, []);\n const status = useAuth((s) => s.status);\n const signedOutReason = useAuth((s) => s.signedOutReason);\n const deviceName = useMemo(() => defaultDeviceName(), []);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<{ code: string; message: string } | null>(null);\n\n // Keep the token out of the address bar and history.\n useEffect(() => {\n if (token) history.replaceState(history.state, \"\", location.pathname + location.search);\n }, [token]);\n\n // Already enrolled and no token: go back to the deep link.\n useEffect(() => {\n if (!token && status === \"authenticated\") navigate(takeReturnTo(next), { replace: true });\n }, [token, status, next, navigate]);\n\n const submit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!token) return;\n setBusy(true);\n setError(null);\n try {\n await apiPost(\"/api/enroll\", {\n token,\n name: deviceName.slice(0, 80),\n kind: isStandalone() ? \"pwa\" : \"browser\",\n });\n const result = await checkAuth();\n if (result === \"authenticated\") navigate(takeReturnTo(next), { replace: true });\n else\n setError({\n code: \"internal\",\n message: \"Signed in, but the coordinator did not accept the new credential. Try again.\",\n });\n } catch (err) {\n const code = isApiError(err) ? err.code : \"internal\";\n setError({\n code,\n message: ERROR_TEXT[code] ?? (err instanceof Error ? err.message : String(err)),\n });\n } finally {\n setBusy(false);\n }\n };\n\n if (!token && status === \"checking\") return <LoadingState label=\"Checking this device…\" />;\n\n return (\n <div className=\"flex min-h-dvh items-start justify-center px-4 safe-pt-10 safe-pb-10 sm:items-center\">\n <div className=\"w-full max-w-sm rounded-xl border border-border bg-surface p-5 shadow-sm\">\n <div className=\"mb-4 flex items-center gap-3\">\n <img src=\"/icon.svg\" alt=\"\" className=\"size-10 rounded-lg\" />\n <div>\n <h1 className=\"text-lg font-semibold\">Switchyard</h1>\n <p className=\"text-sm text-muted\">{token ? \"Sign in this browser\" : \"Sign in\"}</p>\n </div>\n </div>\n {signedOutReason && (\n <p role=\"status\" className=\"mb-3 rounded-md bg-warning-soft px-3 py-2 text-sm\">\n {signedOutReason}\n </p>\n )}\n {token ? (\n <form onSubmit={submit} className=\"flex flex-col gap-3\" noValidate>\n <p className=\"text-sm\">\n This single-use link signs in this browser as “{deviceName}”. You can rename it in\n Settings › Devices.\n </p>\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-border bg-danger-soft px-3 py-2 text-sm\"\n >\n <div className=\"font-medium\">{enrollErrorTitle(error.code)}</div>\n <div className=\"text-muted\">{error.message}</div>\n </div>\n )}\n <Button\n type=\"submit\"\n variant=\"primary\"\n data-testid=\"signin-link\"\n busy={busy}\n disabled={error?.code === \"enrollment_expired\" || error?.code === \"enrollment_used\"}\n icon={<KeyRound className=\"size-4\" />}\n >\n Sign in\n </Button>\n </form>\n ) : (\n <SignInPanel next={next} />\n )}\n </div>\n </div>\n );\n}\n\ninterface ChallengeResponse {\n challengeId: string;\n options: unknown;\n}\n\nconst kindOf = () => (isStandalone() ? \"pwa\" : \"browser\");\n\nfunction AlertBox({ title, children }: { title: string; children: React.ReactNode }) {\n return (\n <div role=\"alert\" className=\"rounded-md border border-border bg-danger-soft px-3 py-2 text-sm\">\n <div className=\"flex items-center gap-1.5 font-medium\">\n <TriangleAlert aria-hidden className=\"size-4 shrink-0\" />\n {title}\n </div>\n <div className=\"break-words text-muted\">{children}</div>\n </div>\n );\n}\n\n/** The no-token screen: the sign-in options. */\nfunction SignInPanel({ next }: { next: string | null }) {\n const navigate = useNavigate();\n const deviceName = useMemo(() => defaultDeviceName().slice(0, 80), []);\n const [params, setParams] = useSearchParams();\n const [setup, setSetup] = useState<SetupState | null>(null);\n const [loadError, setLoadError] = useState<unknown>(null);\n const [nonce, setNonce] = useState(0);\n const [busy, setBusy] = useState<\"passkey\" | \"oidc\" | null>(null);\n const [notice, setNotice] = useState<{ code: string; message: string } | null>(() => {\n const code = params.get(\"error\");\n return code ? { code, message: enrollErrorText(code, params.get(\"detail\")) } : null;\n });\n const [handoff, setHandoff] = useState<{ flowId: string; pollToken: string } | null>(null);\n const authorizeUrlRef = useRef<string | null>(null);\n const ceremony = usePasskeyCeremony();\n\n // Show a callback error once, then drop it from the address bar.\n // biome-ignore lint/correctness/useExhaustiveDependencies: runs once on mount\n useEffect(() => {\n if (!params.has(\"error\") && !params.has(\"detail\")) return;\n const rest = new URLSearchParams(params);\n rest.delete(\"error\");\n rest.delete(\"detail\");\n setParams(rest, { replace: true });\n }, []);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: `nonce` reloads\n useEffect(() => {\n const ac = new AbortController();\n setLoadError(null);\n apiGet<SetupState>(\"/api/setup\", undefined, ac.signal)\n .then((v) => !ac.signal.aborted && setSetup(v))\n .catch((e: unknown) => !ac.signal.aborted && setLoadError(e));\n return () => ac.abort();\n }, [nonce]);\n\n const fail = (err: unknown) => {\n const code = isApiError(err) ? err.code : \"internal\";\n // A canceled or dismissed WebAuthn prompt is not an error worth shouting about.\n if (err instanceof Error && err.name === \"NotAllowedError\") {\n setNotice({ code, message: \"Passkey sign-in was canceled or timed out.\" });\n return;\n }\n setNotice({\n code,\n message: isApiError(err)\n ? enrollErrorText(code, err.message)\n : err instanceof Error\n ? err.message\n : String(err),\n });\n };\n\n const finish = useCallback(async () => {\n const result = await checkAuth();\n if (result === \"authenticated\") navigate(takeReturnTo(next), { replace: true });\n else\n setNotice({\n code: \"internal\",\n message: \"Signed in, but the coordinator did not accept the new credential. Try again.\",\n });\n }, [navigate, next]);\n\n const signInWithPasskey = async () => {\n setBusy(\"passkey\");\n setNotice(null);\n try {\n const { challengeId, options } = await apiPost<ChallengeResponse>(\n \"/api/auth/passkey/options\",\n );\n const result = await ceremony.run(() =>\n startAuthentication({\n optionsJSON: options as Parameters<typeof startAuthentication>[0][\"optionsJSON\"],\n }),\n );\n if (!result.ok) {\n setNotice({ code: \"canceled\", message: \"Passkey sign-in was canceled.\" });\n return;\n }\n const response = result.value;\n await apiPost(\"/api/auth/passkey/verify\", {\n challengeId,\n response,\n deviceName,\n kind: kindOf(),\n });\n await finish();\n } catch (err) {\n fail(err);\n } finally {\n setBusy(null);\n }\n };\n\n const signInWithOidc = async () => {\n setBusy(\"oidc\");\n setNotice(null);\n const standalone = isStandalone();\n try {\n const res = await apiPost<OidcBeginResponse>(\"/api/auth/oidc/begin\", {\n deviceName,\n kind: kindOf(),\n next: takeReturnTo(next),\n handoff: standalone,\n });\n if (standalone && res.flowId && res.pollToken) {\n // Home Screen apps have their own cookie jar: finish in the browser, claim here.\n authorizeUrlRef.current = res.authorizeUrl;\n setHandoff({ flowId: res.flowId, pollToken: res.pollToken });\n window.open(res.authorizeUrl, \"_blank\");\n } else {\n location.assign(res.authorizeUrl);\n return; // leaving the page; keep the button busy\n }\n } catch (err) {\n fail(err);\n }\n setBusy(null);\n };\n\n if (loadError) {\n return (\n <ErrorState\n title=\"Cannot reach Switchyard\"\n error={loadError}\n onRetry={() => setNonce((n) => n + 1)}\n />\n );\n }\n if (!setup) return <LoadingState label=\"Checking this server…\" />;\n\n if (handoff) {\n return (\n <HandoffScreen\n flowId={handoff.flowId}\n pollToken={handoff.pollToken}\n authorizeUrl={authorizeUrlRef.current}\n onDone={finish}\n onExit={() => setHandoff(null)}\n />\n );\n }\n\n const passkeyReady = setup.passkey.registered && setup.passkey.available;\n return (\n <div className=\"flex flex-col gap-4 text-sm\">\n <p className=\"text-base font-medium\">Sign in to Switchyard</p>\n {notice && <AlertBox title=\"Sign-in failed\">{notice.message}</AlertBox>}\n <div className=\"flex flex-col gap-2\">\n {setup.oidc && (\n <Button\n data-testid=\"signin-oidc\"\n variant=\"primary\"\n busy={busy === \"oidc\"}\n disabled={busy !== null}\n icon={<LogIn className=\"size-4\" />}\n onClick={signInWithOidc}\n >\n {setup.oidc.label}\n </Button>\n )}\n {passkeyReady && (\n <Button\n data-testid=\"signin-passkey\"\n variant={setup.oidc ? \"secondary\" : \"primary\"}\n busy={busy === \"passkey\"}\n disabled={busy !== null}\n icon={<KeyRound className=\"size-4\" />}\n onClick={signInWithPasskey}\n >\n Sign in with a passkey\n </Button>\n )}\n {ceremony.waiting && (\n <div role=\"status\" className=\"flex items-center gap-2 text-sm text-muted\">\n <Spinner />\n <span className=\"flex-1\">Waiting for your passkey…</span>\n <Button size=\"sm\" variant=\"ghost\" onClick={ceremony.cancel}>\n Cancel\n </Button>\n </div>\n )}\n {setup.passkey.registered && !setup.passkey.available && setup.passkey.reason && (\n <p className=\"text-xs text-muted\">Passkeys are unavailable: {setup.passkey.reason}.</p>\n )}\n </div>\n {!setup.oidc && !passkeyReady ? (\n <p data-testid=\"signin-pair-hint\" className=\"text-muted\">\n No sign-in method is set up. On the machine running Switchyard, run{\" \"}\n <code>switchyard pair</code> for a single-use sign-in link.\n </p>\n ) : (\n <p className=\"text-xs text-muted\">\n Locked out? Run <code>switchyard pair</code> on the server for a single-use sign-in link.\n </p>\n )}\n </div>\n );\n}\n\n/** PWA single sign-on handoff: finish in the browser, claim the credential here. */\nfunction HandoffScreen({\n flowId,\n pollToken,\n authorizeUrl,\n onDone,\n onExit,\n}: {\n flowId: string;\n pollToken: string;\n authorizeUrl: string | null;\n onDone: () => Promise<void>;\n onExit: () => void;\n}) {\n const [state, setState] = useState<HandoffState>(() => startHandoff(Date.now()));\n const stateRef = useRef(state);\n stateRef.current = state;\n const inFlight = useRef(false);\n const doneRef = useRef(onDone);\n doneRef.current = onDone;\n\n const apply = useCallback((event: Parameters<typeof handoffReduce>[1]) => {\n const next = handoffReduce(stateRef.current, event);\n stateRef.current = next;\n setState(next);\n return next;\n }, []);\n\n const poll = useCallback(async () => {\n if (inFlight.current || stateRef.current.phase !== \"waiting\") return;\n inFlight.current = true;\n try {\n const result = await apiPost<OidcClaimResponse>(\"/api/auth/oidc/claim\", {\n flowId,\n pollToken,\n });\n const next = apply({ type: \"claim\", result, now: Date.now() });\n if (next.phase === \"done\") await doneRef.current();\n } catch (err) {\n apply({\n type: \"claim_error\",\n code: isApiError(err) ? err.code : \"network\",\n message: \"This sign-in expired or was already used. Start again.\",\n now: Date.now(),\n });\n } finally {\n inFlight.current = false;\n }\n }, [flowId, pollToken, apply]);\n\n useEffect(() => {\n const visible = () => document.visibilityState === \"visible\";\n const timer = setInterval(() => {\n apply({ type: \"tick\", now: Date.now() });\n if (shouldPoll(stateRef.current, visible())) void poll();\n }, HANDOFF_POLL_MS);\n const onVisible = () => {\n if (shouldPoll(stateRef.current, visible())) void poll();\n };\n document.addEventListener(\"visibilitychange\", onVisible);\n return () => {\n clearInterval(timer);\n document.removeEventListener(\"visibilitychange\", onVisible);\n };\n }, [poll, apply]);\n\n if (state.phase === \"timeout\" || state.phase === \"failed\" || state.phase === \"canceled\") {\n return (\n <div data-testid=\"signin-handoff\" className=\"flex flex-col gap-3 text-sm\">\n <AlertBox title={state.phase === \"timeout\" ? \"Sign-in timed out\" : \"Sign-in stopped\"}>\n {state.phase === \"timeout\"\n ? \"The sign-in was not finished in time. Start again.\"\n : (state.message ?? \"Start again.\")}\n </AlertBox>\n <Button onClick={onExit}>Back to sign-in</Button>\n </div>\n );\n }\n return (\n <div data-testid=\"signin-handoff\" role=\"status\" className=\"flex flex-col gap-3 text-sm\">\n <p className=\"font-medium\">Finish signing in, then come back here</p>\n <p className=\"text-muted\">\n Complete single sign-on in the page that opened, then return to this app. This screen\n continues on its own.\n </p>\n {authorizeUrl && (\n <TextLink href={authorizeUrl} external>\n Open the sign-in page again\n </TextLink>\n )}\n <Button\n onClick={() => {\n apply({ type: \"cancel\" });\n onExit();\n }}\n >\n Cancel\n </Button>\n </div>\n );\n}\n"],"ignoreList":[0],"mappings":"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"}