itwillsync 1.6.2 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hub/daemon.js +393 -27
- package/dist/hub/daemon.js.map +1 -1
- package/dist/hub/dashboard/assets/index-CUdWjWFv.css +1 -0
- package/dist/hub/dashboard/assets/index-hWUVy-IH.js +2 -0
- package/dist/hub/dashboard/index.html +61 -4
- package/dist/index.js +68 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/hub/dashboard/assets/index-C0aiYBkq.js +0 -2
- package/dist/hub/dashboard/assets/index-D6z7Ixhf.css +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*{margin:0;padding:0;box-sizing:border-box}html,body{height:100%;width:100%;background:#1a1a2e;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;color:#e0e0e0;overflow-x:hidden;-webkit-overflow-scrolling:touch}#header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#16213e;border-bottom:1px solid #0f3460;position:sticky;top:0;z-index:10}.header-left{display:flex;align-items:center;gap:10px}.logo{font-size:16px;font-weight:700;color:#e94560;letter-spacing:-.5px}#status-dot{width:8px;height:8px;border-radius:50%;background:#e74c3c;transition:background .3s ease}#status-dot.connected{background:#2ecc71}#status-dot.reconnecting{background:#f39c12;animation:pulse 1s infinite}.header-right{display:flex;align-items:center;gap:12px}#session-count{font-size:13px;color:#a0a0b0}.header-icon-btn{background:none;border:1px solid #2a2a44;border-radius:8px;color:#a0a0b0;width:32px;height:32px;display:flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:color .15s,border-color .15s}.header-icon-btn:active{color:#e94560;border-color:#e94560}#session-list{padding:12px 12px 80px;display:flex;flex-direction:column;gap:12px}#empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:60vh;color:#606080;text-align:center;gap:8px}#empty-state .empty-icon{font-size:48px;margin-bottom:8px;opacity:.5}#empty-state p{font-size:16px}#empty-state .empty-hint{font-size:13px;color:#505070}#empty-state code{background:#252540;padding:2px 8px;border-radius:4px;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;font-size:12px;color:#e94560}.session-card{background:#16213e;border:1px solid #0f3460;border-radius:12px;padding:14px 16px;cursor:pointer;transition:transform .15s ease,border-color .2s ease;-webkit-tap-highlight-color:transparent}.session-card:active{transform:scale(.98)}.session-card:hover{border-color:#e94560}.session-card.attention{border-color:#e94560;animation:attention-glow 2s ease-in-out infinite}@keyframes attention-glow{0%,to{box-shadow:0 0 #e9456000}50%{box-shadow:0 0 12px 2px #e945604d}}.card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.card-agent{display:flex;align-items:center;gap:8px}.agent-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0}.agent-dot.active{background:#2ecc71}.agent-dot.idle{background:#f39c12}.agent-dot.attention{background:#e94560;animation:pulse 1s infinite}.agent-name{font-size:15px;font-weight:600;color:#f0f0f0}.card-uptime{font-size:12px;color:#707090;font-variant-numeric:tabular-nums}.card-cwd{font-size:12px;color:#808098;margin-bottom:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace}.card-preview{background:#112;border-radius:6px;padding:8px 10px;margin-bottom:10px;overflow:hidden;min-height:24px;max-height:90px}.card-preview-text{font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;font-size:11px;line-height:1.4;color:#a0a0b0;white-space:pre;overflow:hidden;text-overflow:ellipsis;margin:0}.card-preview-text.empty{color:#505070;font-style:italic}.card-status{display:flex;align-items:center;gap:6px;font-size:12px;color:#a0a0b0}.status-badge{padding:2px 8px;border-radius:10px;font-size:11px;font-weight:500;text-transform:uppercase;letter-spacing:.5px}.status-badge.active{background:#2ecc7126;color:#2ecc71}.status-badge.idle{background:#f39c1226;color:#f39c12}.status-badge.attention{background:#e9456026;color:#e94560}.attention-badge{padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;background:#e9456033;color:#e94560;animation:attention-pulse 2s ease-in-out infinite}@keyframes attention-pulse{0%,to{opacity:1}50%{opacity:.6}}.card-actions{display:flex;gap:6px;margin-top:10px;padding-top:10px;border-top:1px solid #0f3460}.action-btn{flex:1;padding:6px 0;border:1px solid #2a2a44;border-radius:6px;background:#1e1e36;color:#a0a0b0;font-size:12px;font-weight:500;font-family:inherit;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s,color .15s}.action-btn:active{background:#2a2a44}.action-btn.open{color:#2ecc71;border-color:#2ecc714d}.action-btn.stop{color:#e94560;border-color:#e945604d}.action-btn.stop:active{background:#e9456026}.confirm-overlay{display:flex;align-items:center;justify-content:center;gap:10px;padding:10px;margin-top:8px;background:#2d1810;border:1px solid #e94560;border-radius:8px;animation:fadeIn .15s ease}.confirm-msg{font-size:13px;color:#f0f0f0;flex:1}.confirm-btn{padding:5px 14px;border:none;border-radius:5px;font-size:12px;font-weight:600;font-family:inherit;cursor:pointer}.confirm-btn.yes{background:#e94560;color:#fff}.confirm-btn.no{background:#2a2a44;color:#a0a0b0}.rename-input{background:#112;border:1px solid #e94560;border-radius:4px;color:#f0f0f0;font-size:15px;font-weight:600;font-family:inherit;padding:2px 6px;outline:none;width:120px}.card-metadata{margin-top:8px;padding:8px 10px;background:#112;border-radius:6px;animation:fadeIn .2s ease}.card-metadata.hidden{display:none}.meta-row{display:flex;justify-content:space-between;padding:3px 0;font-size:11px}.meta-label{color:#707090;font-weight:500}.meta-value{color:#c0c0d0;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;font-size:11px;text-align:right;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(min-width:768px){#session-list{max-width:600px;margin:0 auto}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.4}}@keyframes fadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.session-card{animation:fadeIn .3s ease}#reconnect-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:#1a1a2ef2;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:100;color:#e0e0e0;font-size:16px;gap:12px}#reconnect-overlay .spinner{width:32px;height:32px;border:3px solid #0f3460;border-top-color:#e94560;border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}#fab-create{position:fixed;bottom:24px;right:24px;width:56px;height:56px;border-radius:50%;background:#e94560;color:#fff;font-size:28px;font-weight:300;border:none;cursor:pointer;box-shadow:0 4px 16px #e9456066;z-index:20;-webkit-tap-highlight-color:transparent;transition:transform .15s,box-shadow .15s;display:flex;align-items:center;justify-content:center;line-height:1}#fab-create:active{transform:scale(.92)}.modal-overlay{position:fixed;inset:0;background:#0a0a14d9;z-index:50;display:flex;align-items:flex-end;justify-content:center;animation:fadeIn .15s ease}.modal-overlay.hidden{display:none}.modal-content{background:#16213e;border-radius:16px 16px 0 0;width:100%;max-width:480px;max-height:85vh;max-height:85dvh;overflow-y:auto;padding:20px;animation:slideUp .2s ease}@keyframes slideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}.modal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}.modal-title{font-size:18px;font-weight:600;color:#f0f0f0}.modal-close{background:none;border:none;color:#707090;font-size:24px;cursor:pointer;padding:4px 8px;line-height:1}.form-label{display:block;font-size:13px;font-weight:500;color:#a0a0b0;margin-bottom:6px;margin-top:16px}.form-label:first-of-type{margin-top:0}.form-input{width:100%;padding:10px 12px;background:#112;border:1px solid #2a2a44;border-radius:8px;color:#f0f0f0;font-size:15px;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;outline:none;transition:border-color .15s}.form-input:focus{border-color:#e94560}.form-input.hidden{display:none}.chip-row{display:flex;gap:6px;margin-top:8px;flex-wrap:wrap}.chip{padding:5px 12px;background:#1e1e36;border:1px solid #2a2a44;border-radius:16px;font-size:12px;color:#c0c0d0;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s,border-color .15s;white-space:nowrap;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace}.chip:active,.chip.selected{background:#e9456026;border-color:#e94560;color:#e94560}.dir-display{padding:10px 12px;background:#112;border:1px solid #2a2a44;border-radius:8px;font-size:13px;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;color:#c0c0d0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dir-actions{display:flex;gap:8px;margin-top:10px}.dir-action-btn{flex:1;padding:8px 0;background:#1e1e36;border:1px solid #2a2a44;border-radius:8px;color:#a0a0b0;font-size:13px;font-family:inherit;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.dir-action-btn:active{background:#2a2a44}.btn-create{width:100%;margin-top:20px;padding:14px;background:#e94560;color:#fff;border:none;border-radius:10px;font-size:16px;font-weight:600;font-family:inherit;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:opacity .15s}.btn-create:disabled{opacity:.4;cursor:default}.btn-create:not(:disabled):active{opacity:.8}.create-error{margin-top:12px;padding:8px 12px;background:#e9456026;border-radius:6px;font-size:13px;color:#e94560}.create-error.hidden{display:none}.create-spinner{display:flex;flex-direction:column;align-items:center;gap:12px;padding:32px 0;color:#a0a0b0;font-size:14px}.create-spinner.hidden{display:none}.browse-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.browse-back{background:none;border:none;color:#a0a0b0;font-size:14px;font-family:inherit;cursor:pointer;padding:4px 0}.browse-select{padding:6px 14px;background:#e94560;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:500;font-family:inherit;cursor:pointer}.browse-breadcrumb{display:flex;gap:4px;align-items:center;margin-bottom:12px;font-size:13px;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;color:#808098;flex-wrap:wrap}.breadcrumb-segment{cursor:pointer;color:#a0a0b0;transition:color .15s}.breadcrumb-segment:hover,.breadcrumb-segment:active{color:#e94560}.breadcrumb-sep{color:#505070}.browse-list{display:flex;flex-direction:column;gap:2px;max-height:50vh;max-height:50dvh;overflow-y:auto}.browse-item{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;background:#112;border-radius:6px;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .1s}.browse-item:active{background:#1e1e36}.browse-item-name{font-size:14px;color:#d0d0e0;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace}.browse-item-arrow{color:#505070;font-size:14px}.browse-empty{text-align:center;padding:24px;color:#505070;font-size:13px}.hidden{display:none!important}.setting-row{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;background:#112;border-radius:10px}.setting-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.setting-icon{font-size:22px;flex-shrink:0}.setting-label{font-size:15px;font-weight:600;color:#f0f0f0}.setting-sublabel{font-size:11px;color:#707090;margin-top:2px}.toggle-btn{width:48px;height:28px;border-radius:14px;background:#2a2a44;border:none;cursor:pointer;position:relative;flex-shrink:0;transition:background .25s ease;-webkit-tap-highlight-color:transparent}.toggle-btn[aria-checked=true]{background:#2ecc71}.toggle-btn:disabled{opacity:.4;cursor:default}.toggle-knob{display:block;width:22px;height:22px;border-radius:50%;background:#707090;position:absolute;top:3px;left:3px;transition:transform .25s ease,background .25s ease}.toggle-btn[aria-checked=true] .toggle-knob{transform:translate(20px);background:#fff}.setting-warning{margin-top:12px;padding:10px 14px;background:#f39c121a;border:1px solid rgba(243,156,18,.25);border-radius:8px;font-size:12px;color:#f39c12;line-height:1.5}.setting-note{margin-top:8px;font-size:11px;color:#505070;text-align:center}.setting-tip{margin-top:10px;padding:8px 12px;background:#0f346066;border-radius:6px;font-size:12px;color:#808098;line-height:1.5}#sleep-password-section{margin-top:16px;padding-top:16px;border-top:1px solid #0f3460}#sleep-password-section .form-label{margin-top:0}.sleep-actions{display:flex;gap:10px;margin-top:14px}.sleep-actions .action-btn{flex:1;padding:12px;font-size:14px}.sleep-actions .btn-create{flex:1;margin-top:0;padding:12px;font-size:14px}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))o(s);new MutationObserver(s=>{for(const a of s)if(a.type==="childList")for(const c of a.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&o(c)}).observe(document,{childList:!0,subtree:!0});function n(s){const a={};return s.integrity&&(a.integrity=s.integrity),s.referrerPolicy&&(a.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?a.credentials="include":s.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function o(s){if(s.ep)return;s.ep=!0;const a=n(s);fetch(s.href,a)}})();function Z(e){const t=Math.floor(e/1e3);if(t<60)return`${t}s`;const n=Math.floor(t/60);if(n<60)return`${n}m`;const o=Math.floor(n/60),s=n%60;return`${o}h ${s}m`}function Be(e){const t=e.match(/^\/(?:Users|home)\/[^/]+/)?.[0];return t?"~"+e.slice(t.length):e}function Se(e){return e<1024?`${e} KB`:`${(e/1024).toFixed(1)} MB`}function Ne(e,t,n){const o=document.createElement("div");o.className=`session-card${e.status==="attention"?" attention":""}`,o.dataset.sessionId=e.id;const s=Z(Date.now()-e.connectedAt),a=Be(e.cwd),c=document.createElement("div");c.className="card-header";const i=document.createElement("div");i.className="card-agent";const y=document.createElement("div");y.className=`agent-dot ${e.status}`;const W=document.createElement("span");W.className="agent-name",W.textContent=e.name||e.agent,i.appendChild(y),i.appendChild(W);const _=document.createElement("span");_.className="card-uptime",_.textContent=s,c.appendChild(i),c.appendChild(_);const j=document.createElement("div");j.className="card-cwd",j.textContent=a;const N=document.createElement("div");N.className="card-preview";const K=document.createElement("pre");K.className="card-preview-text",K.textContent="Waiting for output...",N.appendChild(K),N.addEventListener("click",l=>{l.stopPropagation(),n.onOpen(e)});const x=document.createElement("div");x.className="card-status";const P=document.createElement("span");P.className=`status-badge ${e.status}`,P.textContent=e.status,e.status==="attention"&&(P.style.display="none");const $=document.createElement("span");$.className="attention-badge",$.textContent="Needs your attention",e.status!=="attention"&&($.style.display="none"),x.appendChild(P),x.appendChild($);const g=document.createElement("div");g.className="card-actions";const M=document.createElement("button");M.className="action-btn stop",M.textContent="Stop",M.addEventListener("click",l=>{l.stopPropagation(),xe(o,e.id,n.onStop)});const A=document.createElement("button");A.className="action-btn rename",A.textContent="Rename",A.addEventListener("click",l=>{l.stopPropagation(),Pe(o,e.id,n.onRename)});const O=document.createElement("button");O.className="action-btn info",O.textContent="Info",O.addEventListener("click",l=>{l.stopPropagation(),n.onInfo(e.id)});const T=document.createElement("button");T.className="action-btn open",T.textContent="Open",T.addEventListener("click",l=>{l.stopPropagation(),n.onOpen(e)}),g.appendChild(T),g.appendChild(A),g.appendChild(O),g.appendChild(M);const ie=document.createElement("div");return ie.className="card-metadata hidden",o.addEventListener("click",()=>{n.onOpen(e)}),o.appendChild(c),o.appendChild(j),o.appendChild(N),o.appendChild(x),o.appendChild(g),o.appendChild(ie),o}function xe(e,t,n){e.querySelector(".confirm-overlay")?.remove();const o=document.createElement("div");o.className="confirm-overlay";const s=document.createElement("span");s.className="confirm-msg",s.textContent="Stop this session?";const a=document.createElement("button");a.className="confirm-btn yes",a.textContent="Yes",a.addEventListener("click",i=>{i.stopPropagation(),n(t),o.remove()});const c=document.createElement("button");c.className="confirm-btn no",c.textContent="No",c.addEventListener("click",i=>{i.stopPropagation(),o.remove()}),o.addEventListener("click",i=>i.stopPropagation()),o.appendChild(s),o.appendChild(a),o.appendChild(c),e.appendChild(o)}function Pe(e,t,n){const o=e.querySelector(".agent-name");if(!o)return;const s=o.textContent||"",a=document.createElement("input");a.className="rename-input",a.type="text",a.value=s;const c=()=>{const i=a.value.trim();i&&i!==s&&n(t,i),o.textContent=i||s,o.style.display="",a.remove()};a.addEventListener("keydown",i=>{i.stopPropagation(),i.key==="Enter"?c():i.key==="Escape"&&(o.textContent=s,o.style.display="",a.remove())}),a.addEventListener("blur",c),a.addEventListener("click",i=>i.stopPropagation()),o.style.display="none",o.parentElement?.insertBefore(a,o.nextSibling),a.focus(),a.select()}function be(e,t){const n=e.querySelector(".agent-dot"),o=e.querySelector(".status-badge"),s=e.querySelector(".card-uptime"),a=e.querySelector(".agent-name");n&&(n.className=`agent-dot ${t.status}`),o&&(o.className=`status-badge ${t.status}`,o.textContent=t.status),s&&(s.textContent=Z(Date.now()-t.connectedAt)),a&&!a.style.display&&(a.textContent=t.name||t.agent);const c=t.status==="attention";o&&(o.style.display=c?"none":"");const i=e.querySelector(".attention-badge");i&&(i.style.display=c?"":"none"),t.status==="attention"?e.classList.add("attention"):e.classList.remove("attention")}function $e(e,t){const n=e.querySelector(".card-preview-text");n&&(t.length>0?(n.textContent=t.join(`
|
|
2
|
+
`),n.classList.remove("empty")):(n.textContent="Waiting for output...",n.classList.add("empty")))}function Me(e,t){const n=e.querySelector(".card-metadata");if(!n)return;for(;n.firstChild;)n.removeChild(n.firstChild);const o=[["PID",String(t.pid)],["Agent",t.agent],["Port",String(t.port)],["Directory",t.cwd],["Memory",Se(t.memoryKB)],["Uptime",Z(t.uptimeMs)]];for(const[s,a]of o){const c=document.createElement("div");c.className="meta-row";const i=document.createElement("span");i.className="meta-label",i.textContent=s;const y=document.createElement("span");y.className="meta-value",y.textContent=a,c.appendChild(i),c.appendChild(y),n.appendChild(c)}n.classList.toggle("hidden")}let d=null,de=!1;function Ae(){if(!de){de=!0;try{d=new AudioContext,d.state==="suspended"&&d.resume()}catch{}}}function re(){if(!d)return;d.state==="suspended"&&d.resume();const e=d.currentTime,t=d.createOscillator(),n=d.createGain();t.frequency.value=587.33,t.type="sine",n.gain.setValueAtTime(.3,e),n.gain.exponentialRampToValueAtTime(.01,e+.3),t.connect(n),n.connect(d.destination),t.start(e),t.stop(e+.3);const o=d.createOscillator(),s=d.createGain();o.frequency.value=880,o.type="sine",s.gain.setValueAtTime(.3,e+.15),s.gain.exponentialRampToValueAtTime(.01,e+.45),o.connect(s),s.connect(d.destination),o.start(e+.15),o.stop(e+.45)}const le=120*1e3,h=new Map;function me(e){if(h.has(e))return;re();const t=setTimeout(function n(){re();const o=h.get(e);o&&(o.timerId=setTimeout(n,le))},le);h.set(e,{timerId:t})}function Y(e){const t=h.get(e);t&&(clearTimeout(t.timerId),h.delete(e))}function ue(){for(const e of h.values())clearTimeout(e.timerId);h.clear()}let z=null,k,u,I,f,b,D,H,pe;function Oe(e){z=e;const t=document.getElementById("btn-settings");k=document.getElementById("settings-modal");const n=document.getElementById("settings-modal-close");u=document.getElementById("sleep-toggle"),I=document.getElementById("sleep-password-section"),f=document.getElementById("sleep-password"),b=document.getElementById("sleep-error"),D=document.getElementById("sleep-spinner"),H=document.getElementById("sleep-unsupported"),pe=document.getElementById("sleep-enable");const o=document.getElementById("sleep-cancel");t.addEventListener("click",()=>{k.classList.remove("hidden")}),n.addEventListener("click",he),k.addEventListener("click",s=>{s.target===k&&he()}),u.addEventListener("click",()=>{if(u.disabled)return;u.getAttribute("aria-checked")==="true"?z?.({type:"disable-sleep-prevention"}):(I.classList.remove("hidden"),b.classList.add("hidden"),f.value="",f.focus())}),pe.addEventListener("click",fe),f.addEventListener("keydown",s=>{s.key==="Enter"&&(s.preventDefault(),fe())}),o.addEventListener("click",ee)}function ee(){I.classList.add("hidden"),f.value="",b.classList.add("hidden")}function fe(){const e=f.value;if(!e){Ce("Password is required");return}f.value="",b.classList.add("hidden"),I.classList.add("hidden"),D.classList.remove("hidden"),z?.({type:"enable-sleep-prevention",password:e})}function he(){k.classList.add("hidden"),ee()}function Ce(e){b.textContent=e,b.classList.remove("hidden")}function Te(e){if(D.classList.add("hidden"),!e.supported){u.disabled=!0,u.setAttribute("aria-checked","false"),H.classList.remove("hidden");return}H.classList.add("hidden"),u.disabled=!1,u.setAttribute("aria-checked",e.enabled?"true":"false"),ee()}function Re(e){D.classList.add("hidden"),I.classList.remove("hidden"),Ce(e),u.setAttribute("aria-checked","false")}const Ue=new URLSearchParams(window.location.search),V=Ue.get("token");if(!V)throw document.body.textContent="Missing authentication token.",new Error("No token in URL");const qe=window.location.protocol==="https:"?"wss:":"ws:",De=`${qe}//${window.location.host}?token=${V}`,we=window.location.hostname,w=new Map,p=new Map,Ve=document.getElementById("session-list"),X=document.getElementById("empty-state"),Fe=document.getElementById("session-count"),ye=document.getElementById("status-dot");let G=null;function R(){Ae(),document.removeEventListener("click",R),document.removeEventListener("touchstart",R)}document.addEventListener("click",R);document.addEventListener("touchstart",R);function v(e){r&&r.readyState===WebSocket.OPEN&&r.send(JSON.stringify(e))}Oe(v);const We={onOpen(e){const t=w.get(e.id)||e;t.status==="attention"&&(v({type:"clear-attention",sessionId:t.id}),Y(t.id));const n=window.location.href,o=`http://${we}:${t.port}?token=${t.token}&hub=${encodeURIComponent(n)}`;window.location.href=o},onStop(e){v({type:"stop-session",sessionId:e})},onRename(e,t){v({type:"rename-session",sessionId:e,name:t})},onInfo(e){v({type:"get-metadata",sessionId:e})}};function Le(){const e=w.size;Fe.textContent=`${e} session${e!==1?"s":""}`,e===0?X.style.display="flex":X.style.display="none"}function ge(e){w.set(e.id,e);const t=Ne(e,we,We);p.set(e.id,t),Ve.insertBefore(t,X),Le()}function Ee(e){w.delete(e);const t=p.get(e);t&&(t.remove(),p.delete(e)),Le()}function _e(e){w.set(e.id,e);const t=p.get(e.id);t&&be(t,e)}function je(){for(const[e,t]of w){const n=p.get(e);n&&be(n,t)}}const te=document.getElementById("fab-create"),B=document.getElementById("create-modal"),Ke=document.getElementById("modal-close"),C=document.getElementById("tool-input"),ve=document.getElementById("tool-chips"),ke=document.getElementById("dir-selected"),Ge=document.getElementById("btn-browse"),Ie=document.getElementById("btn-create-session"),U=document.getElementById("create-error"),L=document.getElementById("create-form-view"),F=document.getElementById("browse-view"),J=document.getElementById("browse-breadcrumb"),E=document.getElementById("browse-list"),Je=document.getElementById("browse-back"),Ye=document.getElementById("browse-select"),ne=document.getElementById("create-spinner");let S="~",Q="~";function m(e,t,n){const o=document.createElement(e);return o.className=t,o.textContent=n,o}function ze(){B.classList.remove("hidden"),L.classList.remove("hidden"),F.classList.add("hidden"),ne.classList.add("hidden"),U.classList.add("hidden"),C.value="",S="~",ke.textContent="~",se(),He(),C.focus()}function oe(){B.classList.add("hidden")}function se(){Ie.disabled=!C.value.trim()}async function He(){try{const t=await(await fetch(`/api/tool-history?token=${V}`)).json();ve.replaceChildren();for(const n of t.tools.slice(0,6)){const o=m("span","chip",n);o.addEventListener("click",()=>{C.value=n,se()}),ve.appendChild(o)}}catch{}}async function ae(e){Q=e,E.replaceChildren(m("div","browse-empty","Loading..."));try{const n=await(await fetch(`/api/browse?path=${encodeURIComponent(e)}&token=${V}`)).json();if(n.error){E.replaceChildren(m("div","browse-empty",n.error));return}const o=n.path||e;Q=o,Xe(o);const s=n.entries;if(s.length===0){E.replaceChildren(m("div","browse-empty","No subdirectories"));return}E.replaceChildren();for(const a of s){const c=document.createElement("div");c.className="browse-item",c.appendChild(m("span","browse-item-name",a)),c.appendChild(m("span","browse-item-arrow","›")),c.addEventListener("click",()=>ae(`${o}/${a}`)),E.appendChild(c)}}catch{E.replaceChildren(m("div","browse-empty","Failed to load"))}}function Xe(e){J.replaceChildren();const t=e.split("/").filter(Boolean);for(let n=0;n<t.length;n++){n>0&&J.appendChild(m("span","breadcrumb-sep"," / "));const o=m("span","breadcrumb-segment",t[n]),s=t.slice(0,n+1).join("/");o.addEventListener("click",()=>ae(s)),J.appendChild(o)}}function Qe(){const e=C.value.trim();e&&(U.classList.add("hidden"),L.classList.add("hidden"),ne.classList.remove("hidden"),v({type:"create-session",tool:e,cwd:S}))}te.addEventListener("click",ze);Ke.addEventListener("click",oe);B.addEventListener("click",e=>{e.target===B&&oe()});C.addEventListener("input",se);Ie.addEventListener("click",Qe);Ge.addEventListener("click",()=>{L.classList.add("hidden"),F.classList.remove("hidden"),ae(S)});Je.addEventListener("click",()=>{F.classList.add("hidden"),L.classList.remove("hidden")});Ye.addEventListener("click",()=>{S=Q,ke.textContent=S,F.classList.add("hidden"),L.classList.remove("hidden")});let r=null,q=0;const Ze=1e4;function ce(){r=new WebSocket(De),r.onopen=()=>{ye.className="connected",q=0,G&&clearInterval(G),G=setInterval(je,1e4)},r.onmessage=e=>{try{const t=JSON.parse(e.data);switch(t.type){case"sessions":{ue();for(const n of p.keys())Ee(n);for(const n of t.sessions)ge(n),n.status==="attention"&&me(n.id);break}case"session-added":{ge(t.session),B.classList.contains("hidden")||oe();break}case"session-removed":{const n=t.sessionId;Y(n),Ee(n);break}case"session-updated":{const n=t.session;_e(n),n.status==="attention"?me(n.id):Y(n.id);break}case"preview":{const n=p.get(t.sessionId);n&&$e(n,t.lines);break}case"metadata":{const n=p.get(t.sessionId);n&&Me(n,t.metadata);break}case"session-creating":break;case"session-create-error":{ne.classList.add("hidden"),L.classList.remove("hidden"),U.textContent=t.error,U.classList.remove("hidden");break}case"sleep-state":{Te(t.state);break}case"sleep-error":{Re(t.error);break}case"operation-error":{console.warn(`Operation "${t.operation}" failed for session ${t.sessionId}: ${t.error}`);break}}}catch{}},r.onclose=()=>{ye.className="reconnecting",ue(),et()},r.onerror=()=>{r?.close()}}function et(){const e=Math.min(1e3*Math.pow(1.5,q),Ze);q++,setTimeout(ce,e)}document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&r?.readyState!==WebSocket.OPEN&&(q=0,ce())});ce();document.addEventListener("focusin",e=>{const t=e.target;t.matches("input, textarea")&&(te.style.display="none",setTimeout(()=>{t.scrollIntoView({block:"center",behavior:"smooth"})},300))});document.addEventListener("focusout",()=>{te.style.display=""});
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=no" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=no, interactive-widget=resizes-content" />
|
|
6
6
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
7
7
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
8
8
|
<meta name="theme-color" content="#1a1a2e" />
|
|
9
9
|
<title>itwillsync Dashboard</title>
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-hWUVy-IH.js"></script>
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CUdWjWFv.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<header id="header">
|
|
@@ -16,7 +16,15 @@
|
|
|
16
16
|
<span class="logo">itwillsync</span>
|
|
17
17
|
<span id="status-dot"></span>
|
|
18
18
|
</div>
|
|
19
|
-
<
|
|
19
|
+
<div class="header-right">
|
|
20
|
+
<span id="session-count">0 sessions</span>
|
|
21
|
+
<button id="btn-settings" class="header-icon-btn" aria-label="Settings">
|
|
22
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
23
|
+
<circle cx="12" cy="12" r="3"/>
|
|
24
|
+
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
|
25
|
+
</svg>
|
|
26
|
+
</button>
|
|
27
|
+
</div>
|
|
20
28
|
</header>
|
|
21
29
|
|
|
22
30
|
<main id="session-list">
|
|
@@ -72,5 +80,54 @@
|
|
|
72
80
|
</div>
|
|
73
81
|
</div>
|
|
74
82
|
|
|
83
|
+
<!-- Settings Modal -->
|
|
84
|
+
<div id="settings-modal" class="modal-overlay hidden">
|
|
85
|
+
<div class="modal-content">
|
|
86
|
+
<div class="modal-header">
|
|
87
|
+
<span class="modal-title">Settings</span>
|
|
88
|
+
<button class="modal-close" id="settings-modal-close">×</button>
|
|
89
|
+
</div>
|
|
90
|
+
<div id="settings-body">
|
|
91
|
+
<div class="setting-row">
|
|
92
|
+
<div class="setting-info">
|
|
93
|
+
<span class="setting-icon">☕</span>
|
|
94
|
+
<div>
|
|
95
|
+
<div class="setting-label">Prevent Sleep</div>
|
|
96
|
+
<div class="setting-sublabel">Keep computer awake even with lid closed</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
<button id="sleep-toggle" class="toggle-btn" role="switch" aria-checked="false">
|
|
100
|
+
<span class="toggle-knob"></span>
|
|
101
|
+
</button>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div class="setting-warning">
|
|
105
|
+
⚠️ <strong>Battery warning:</strong> Your laptop may shut down if running on battery. Keep it plugged in.
|
|
106
|
+
</div>
|
|
107
|
+
<div class="setting-note">Requires admin password · Auto-reverts when hub stops</div>
|
|
108
|
+
<div class="setting-tip">💻 Enable this while you're near your laptop — you'll need to type your system password.</div>
|
|
109
|
+
|
|
110
|
+
<div id="sleep-password-section" class="hidden">
|
|
111
|
+
<label class="form-label">System password</label>
|
|
112
|
+
<input type="password" id="sleep-password" class="form-input" placeholder="Enter your password" autocomplete="off" />
|
|
113
|
+
<div id="sleep-error" class="create-error hidden"></div>
|
|
114
|
+
<div class="sleep-actions">
|
|
115
|
+
<button id="sleep-cancel" class="action-btn">Cancel</button>
|
|
116
|
+
<button id="sleep-enable" class="btn-create">Enable</button>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div id="sleep-spinner" class="create-spinner hidden">
|
|
121
|
+
<div class="spinner"></div>
|
|
122
|
+
<span>Applying...</span>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div id="sleep-unsupported" class="setting-note hidden">
|
|
126
|
+
Sleep prevention is not available on this platform.
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
75
132
|
</body>
|
|
76
133
|
</html>
|
package/dist/index.js
CHANGED
|
@@ -3772,19 +3772,26 @@ function validateToken(provided, expected) {
|
|
|
3772
3772
|
import { networkInterfaces } from "os";
|
|
3773
3773
|
import { createServer } from "net";
|
|
3774
3774
|
|
|
3775
|
-
// src/
|
|
3775
|
+
// src/exec-utils.ts
|
|
3776
3776
|
import { execFile } from "child_process";
|
|
3777
|
-
function
|
|
3777
|
+
function execFileAsync(cmd, args, opts) {
|
|
3778
3778
|
return new Promise((resolve, reject) => {
|
|
3779
|
-
execFile(
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3779
|
+
execFile(
|
|
3780
|
+
cmd,
|
|
3781
|
+
args,
|
|
3782
|
+
{ timeout: opts?.timeout ?? 5e3 },
|
|
3783
|
+
(error, stdout, stderr) => {
|
|
3784
|
+
if (error) {
|
|
3785
|
+
reject(error);
|
|
3786
|
+
} else {
|
|
3787
|
+
resolve({ stdout: stdout.trim(), stderr: stderr.trim() });
|
|
3788
|
+
}
|
|
3784
3789
|
}
|
|
3785
|
-
|
|
3790
|
+
);
|
|
3786
3791
|
});
|
|
3787
3792
|
}
|
|
3793
|
+
|
|
3794
|
+
// src/tailscale.ts
|
|
3788
3795
|
function getTailscalePaths() {
|
|
3789
3796
|
const paths = ["tailscale"];
|
|
3790
3797
|
if (process.platform === "darwin") {
|
|
@@ -3796,7 +3803,7 @@ async function tryExec(args) {
|
|
|
3796
3803
|
let lastError = null;
|
|
3797
3804
|
for (const bin of getTailscalePaths()) {
|
|
3798
3805
|
try {
|
|
3799
|
-
const result = await
|
|
3806
|
+
const result = await execFileAsync(bin, args);
|
|
3800
3807
|
return { status: "success", ...result };
|
|
3801
3808
|
} catch (err) {
|
|
3802
3809
|
if (err.code === "ENOENT") {
|
|
@@ -4744,6 +4751,40 @@ Hub Management:
|
|
|
4744
4751
|
`);
|
|
4745
4752
|
}
|
|
4746
4753
|
|
|
4754
|
+
// src/resolve-command.ts
|
|
4755
|
+
async function resolveCommand(command) {
|
|
4756
|
+
if (process.platform !== "win32") {
|
|
4757
|
+
return command;
|
|
4758
|
+
}
|
|
4759
|
+
if (/^[a-zA-Z]:[/\\]/.test(command) || command.startsWith("\\\\")) {
|
|
4760
|
+
return command;
|
|
4761
|
+
}
|
|
4762
|
+
try {
|
|
4763
|
+
const { stdout } = await execFileAsync("where.exe", [command]);
|
|
4764
|
+
const firstMatch = stdout.split("\n")[0]?.trim();
|
|
4765
|
+
if (firstMatch) {
|
|
4766
|
+
return firstMatch;
|
|
4767
|
+
}
|
|
4768
|
+
throw new Error(`where.exe returned empty output for "${command}"`);
|
|
4769
|
+
} catch (err) {
|
|
4770
|
+
if (err.code === "ENOENT") {
|
|
4771
|
+
return command;
|
|
4772
|
+
}
|
|
4773
|
+
throw new Error(
|
|
4774
|
+
`Could not find "${command}" on this system.
|
|
4775
|
+
|
|
4776
|
+
To fix this:
|
|
4777
|
+
1. Make sure "${command}" is installed
|
|
4778
|
+
2. Open a new terminal and run: ${command} --version
|
|
4779
|
+
3. If that works, try running itwillsync again
|
|
4780
|
+
|
|
4781
|
+
If "${command}" was just installed, you may need to restart your
|
|
4782
|
+
terminal so Windows can find it in your PATH.`,
|
|
4783
|
+
{ cause: err }
|
|
4784
|
+
);
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4787
|
+
|
|
4747
4788
|
// src/index.ts
|
|
4748
4789
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
4749
4790
|
import { join as join7, dirname as dirname4 } from "path";
|
|
@@ -4883,14 +4924,7 @@ async function main() {
|
|
|
4883
4924
|
}
|
|
4884
4925
|
const headless = options.headless;
|
|
4885
4926
|
const config = loadConfig();
|
|
4886
|
-
|
|
4887
|
-
if (options.tailscale) {
|
|
4888
|
-
networkingMode = "tailscale";
|
|
4889
|
-
} else if (options.local) {
|
|
4890
|
-
networkingMode = "local";
|
|
4891
|
-
} else {
|
|
4892
|
-
networkingMode = config.networkingMode;
|
|
4893
|
-
}
|
|
4927
|
+
const networkingMode = options.tailscale ? "tailscale" : options.local ? "local" : config.networkingMode;
|
|
4894
4928
|
const [cmd, ...cmdArgs] = options.command;
|
|
4895
4929
|
const isFirstSession = await ensureHub();
|
|
4896
4930
|
const hubConfig = getHubConfig();
|
|
@@ -4900,7 +4934,23 @@ async function main() {
|
|
|
4900
4934
|
const ip = await resolveSessionIP(networkingMode, options.localhost);
|
|
4901
4935
|
const __dirname2 = dirname4(fileURLToPath4(import.meta.url));
|
|
4902
4936
|
const webClientPath = join7(__dirname2, "web-client");
|
|
4903
|
-
const
|
|
4937
|
+
const resolvedCmd = await resolveCommand(cmd);
|
|
4938
|
+
let ptyManager;
|
|
4939
|
+
try {
|
|
4940
|
+
ptyManager = new PtyManager(resolvedCmd, cmdArgs);
|
|
4941
|
+
} catch {
|
|
4942
|
+
console.error(
|
|
4943
|
+
`
|
|
4944
|
+
Could not start "${cmd}".
|
|
4945
|
+
|
|
4946
|
+
To fix this:
|
|
4947
|
+
1. Make sure "${cmd}" is installed
|
|
4948
|
+
2. Open a new terminal and run: ${cmd} --version
|
|
4949
|
+
3. If that works, try running itwillsync again
|
|
4950
|
+
`
|
|
4951
|
+
);
|
|
4952
|
+
process.exit(1);
|
|
4953
|
+
}
|
|
4904
4954
|
const sessionId = `${cmd}-${Date.now().toString(36)}`;
|
|
4905
4955
|
const sessionLogger = new SessionLogger(sessionId);
|
|
4906
4956
|
const server = createSyncServer({
|