itwillsync 1.3.7 → 1.5.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 +149 -15
- package/dist/hub/daemon.js.map +1 -1
- package/dist/hub/dashboard/assets/{index-DgUZUPW_.js → index-DdOxsvuU.js} +1 -1
- package/dist/hub/dashboard/index.html +1 -1
- package/dist/index.js +166 -31
- package/dist/index.js.map +1 -1
- package/dist/web-client/assets/{index-CWuNNUhm.js → index-Bg1a3YQa.js} +17 -17
- package/dist/web-client/assets/index-CmAz03xC.css +1 -0
- package/dist/web-client/index.html +2 -2
- package/package.json +3 -3
- package/dist/web-client/assets/index-xsTGRLqf.css +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))o(a);new MutationObserver(a=>{for(const s of a)if(s.type==="childList")for(const i of s.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&o(i)}).observe(document,{childList:!0,subtree:!0});function n(a){const s={};return a.integrity&&(s.integrity=a.integrity),a.referrerPolicy&&(s.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?s.credentials="include":a.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function o(a){if(a.ep)return;a.ep=!0;const s=n(a);fetch(a.href,s)}})();function B(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),a=n%60;return`${o}h ${a}m`}function z(e){const t=e.match(/^\/(?:Users|home)\/[^/]+/)?.[0];return t?"~"+e.slice(t.length):e}function X(e){return e<1024?`${e} KB`:`${(e/1024).toFixed(1)} MB`}function H(e,t,n){const o=document.createElement("div");o.className=`session-card${e.status==="attention"?" attention":""}`,o.dataset.sessionId=e.id;const a=B(Date.now()-e.connectedAt),s=z(e.cwd),i=document.createElement("div");i.className="card-header";const c=document.createElement("div");c.className="card-agent";const p=document.createElement("div");p.className=`agent-dot ${e.status}`;const P=document.createElement("span");P.className="agent-name",P.textContent=e.name||e.agent,c.appendChild(p),c.appendChild(P);const k=document.createElement("span");k.className="card-uptime",k.textContent=a,i.appendChild(c),i.appendChild(k);const I=document.createElement("div");I.className="card-cwd",I.textContent=s;const h=document.createElement("div");h.className="card-preview";const $=document.createElement("pre");$.className="card-preview-text",$.textContent="Waiting for output...",h.appendChild($),h.addEventListener("click",l=>{l.stopPropagation(),n.onOpen(e)});const g=document.createElement("div");g.className="card-status";const E=document.createElement("span");E.className=`status-badge ${e.status}`,E.textContent=e.status,e.status==="attention"&&(E.style.display="none");const v=document.createElement("span");v.className="attention-badge",v.textContent="Needs your attention",e.status!=="attention"&&(v.style.display="none"),g.appendChild(E),g.appendChild(v);const f=document.createElement("div");f.className="card-actions";const C=document.createElement("button");C.className="action-btn stop",C.textContent="Stop",C.addEventListener("click",l=>{l.stopPropagation(),Q(o,e.id,n.onStop)});const w=document.createElement("button");w.className="action-btn rename",w.textContent="Rename",w.addEventListener("click",l=>{l.stopPropagation(),Z(o,e.id,n.onRename)});const N=document.createElement("button");N.className="action-btn info",N.textContent="Info",N.addEventListener("click",l=>{l.stopPropagation(),n.onInfo(e.id)});const b=document.createElement("button");b.className="action-btn open",b.textContent="Open",b.addEventListener("click",l=>{l.stopPropagation(),n.onOpen(e)}),f.appendChild(b),f.appendChild(w),f.appendChild(N),f.appendChild(C);const R=document.createElement("div");return R.className="card-metadata hidden",o.addEventListener("click",()=>{n.onOpen(e)}),o.appendChild(i),o.appendChild(I),o.appendChild(h),o.appendChild(g),o.appendChild(f),o.appendChild(R),o}function Q(e,t,n){e.querySelector(".confirm-overlay")?.remove();const o=document.createElement("div");o.className="confirm-overlay";const a=document.createElement("span");a.className="confirm-msg",a.textContent="Stop this session?";const s=document.createElement("button");s.className="confirm-btn yes",s.textContent="Yes",s.addEventListener("click",c=>{c.stopPropagation(),n(t),o.remove()});const i=document.createElement("button");i.className="confirm-btn no",i.textContent="No",i.addEventListener("click",c=>{c.stopPropagation(),o.remove()}),o.addEventListener("click",c=>c.stopPropagation()),o.appendChild(a),o.appendChild(s),o.appendChild(i),e.appendChild(o)}function Z(e,t,n){const o=e.querySelector(".agent-name");if(!o)return;const a=o.textContent||"",s=document.createElement("input");s.className="rename-input",s.type="text",s.value=a;const i=()=>{const c=s.value.trim();c&&c!==a&&n(t,c),o.textContent=c||a,o.style.display="",s.remove()};s.addEventListener("keydown",c=>{c.stopPropagation(),c.key==="Enter"?i():c.key==="Escape"&&(o.textContent=a,o.style.display="",s.remove())}),s.addEventListener("blur",i),s.addEventListener("click",c=>c.stopPropagation()),o.style.display="none",o.parentElement?.insertBefore(s,o.nextSibling),s.focus(),s.select()}function G(e,t){const n=e.querySelector(".agent-dot"),o=e.querySelector(".status-badge"),a=e.querySelector(".card-uptime"),s=e.querySelector(".agent-name");n&&(n.className=`agent-dot ${t.status}`),o&&(o.className=`status-badge ${t.status}`,o.textContent=t.status),a&&(a.textContent=B(Date.now()-t.connectedAt)),s&&!s.style.display&&(s.textContent=t.name||t.agent);const i=t.status==="attention";o&&(o.style.display=i?"none":"");const c=e.querySelector(".attention-badge");c&&(c.style.display=i?"":"none"),t.status==="attention"?e.classList.add("attention"):e.classList.remove("attention")}function ee(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 te(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",X(t.memoryKB)],["Uptime",B(t.uptimeMs)]];for(const[a,s]of o){const i=document.createElement("div");i.className="meta-row";const c=document.createElement("span");c.className="meta-label",c.textContent=a;const p=document.createElement("span");p.className="meta-value",p.textContent=s,i.appendChild(c),i.appendChild(p),n.appendChild(i)}n.classList.toggle("hidden")}let r=null,q=!1;function ne(){if(!q){q=!0;try{r=new AudioContext,r.state==="suspended"&&r.resume()}catch{}}}function U(){if(!r)return;r.state==="suspended"&&r.resume();const e=r.currentTime,t=r.createOscillator(),n=r.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(r.destination),t.start(e),t.stop(e+.3);const o=r.createOscillator(),a=r.createGain();o.frequency.value=880,o.type="sine",a.gain.setValueAtTime(.3,e+.15),a.gain.exponentialRampToValueAtTime(.01,e+.45),o.connect(a),a.connect(r.destination),o.start(e+.15),o.stop(e+.45)}const D=120*1e3,u=new Map;function
|
|
2
|
+
`),n.classList.remove("empty")):(n.textContent="Waiting for output...",n.classList.add("empty")))}function te(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",X(t.memoryKB)],["Uptime",B(t.uptimeMs)]];for(const[a,s]of o){const i=document.createElement("div");i.className="meta-row";const c=document.createElement("span");c.className="meta-label",c.textContent=a;const p=document.createElement("span");p.className="meta-value",p.textContent=s,i.appendChild(c),i.appendChild(p),n.appendChild(i)}n.classList.toggle("hidden")}let r=null,q=!1;function ne(){if(!q){q=!0;try{r=new AudioContext,r.state==="suspended"&&r.resume()}catch{}}}function U(){if(!r)return;r.state==="suspended"&&r.resume();const e=r.currentTime,t=r.createOscillator(),n=r.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(r.destination),t.start(e),t.stop(e+.3);const o=r.createOscillator(),a=r.createGain();o.frequency.value=880,o.type="sine",a.gain.setValueAtTime(.3,e+.15),a.gain.exponentialRampToValueAtTime(.01,e+.45),o.connect(a),a.connect(r.destination),o.start(e+.15),o.stop(e+.45)}const D=120*1e3,u=new Map;function V(e){if(u.has(e))return;U();const t=setTimeout(function n(){U();const o=u.get(e);o&&(o.timerId=setTimeout(n,D))},D);u.set(e,{timerId:t})}function A(e){const t=u.get(e);t&&(clearTimeout(t.timerId),u.delete(e))}function W(){for(const e of u.values())clearTimeout(e.timerId);u.clear()}const oe=new URLSearchParams(window.location.search),J=oe.get("token");if(!J)throw document.body.textContent="Missing authentication token.",new Error("No token in URL");const ae=window.location.protocol==="https:"?"wss:":"ws:",se=`${ae}//${window.location.host}?token=${J}`,Y=window.location.hostname,y=new Map,m=new Map,ce=document.getElementById("session-list"),O=document.getElementById("empty-state"),ie=document.getElementById("session-count"),_=document.getElementById("status-dot");let M=null;function x(){ne(),document.removeEventListener("click",x),document.removeEventListener("touchstart",x)}document.addEventListener("click",x);document.addEventListener("touchstart",x);function S(e){d&&d.readyState===WebSocket.OPEN&&d.send(JSON.stringify(e))}const re={onOpen(e){const t=y.get(e.id)||e;t.status==="attention"&&(S({type:"clear-attention",sessionId:t.id}),A(t.id));const n=window.location.href,o=`http://${Y}:${t.port}?token=${t.token}&hub=${encodeURIComponent(n)}`;window.open(o,`session-${t.id}`)},onStop(e){S({type:"stop-session",sessionId:e})},onRename(e,t){S({type:"rename-session",sessionId:e,name:t})},onInfo(e){S({type:"get-metadata",sessionId:e})}};function j(){const e=y.size;ie.textContent=`${e} session${e!==1?"s":""}`,e===0?O.style.display="flex":O.style.display="none"}function F(e){y.set(e.id,e);const t=H(e,Y,re);m.set(e.id,t),ce.insertBefore(t,O),j()}function K(e){y.delete(e);const t=m.get(e);t&&(t.remove(),m.delete(e)),j()}function de(e){y.set(e.id,e);const t=m.get(e.id);t&&G(t,e)}function le(){for(const[e,t]of y){const n=m.get(e);n&&G(n,t)}}let d=null,L=0;const me=1e4;function T(){d=new WebSocket(se),d.onopen=()=>{_.className="connected",L=0,M&&clearInterval(M),M=setInterval(le,1e4)},d.onmessage=e=>{try{const t=JSON.parse(e.data);switch(t.type){case"sessions":{W();for(const n of m.keys())K(n);for(const n of t.sessions)F(n),n.status==="attention"&&V(n.id);break}case"session-added":{F(t.session);break}case"session-removed":{const n=t.sessionId;A(n),K(n);break}case"session-updated":{const n=t.session;de(n),n.status==="attention"?V(n.id):A(n.id);break}case"preview":{const n=m.get(t.sessionId);n&&ee(n,t.lines);break}case"metadata":{const n=m.get(t.sessionId);n&&te(n,t.metadata);break}case"operation-error":{console.warn(`Operation "${t.operation}" failed for session ${t.sessionId}: ${t.error}`);break}}}catch{}},d.onclose=()=>{_.className="reconnecting",W(),ue()},d.onerror=()=>{d?.close()}}function ue(){const e=Math.min(1e3*Math.pow(1.5,L),me);L++,setTimeout(T,e)}document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&d?.readyState!==WebSocket.OPEN&&(L=0,T())});T();
|
|
@@ -7,7 +7,7 @@
|
|
|
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-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-DdOxsvuU.js"></script>
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/assets/index-Erqx_a0N.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
package/dist/index.js
CHANGED
|
@@ -3732,9 +3732,11 @@ var PtyManager = class {
|
|
|
3732
3732
|
*/
|
|
3733
3733
|
resize(cols, rows) {
|
|
3734
3734
|
try {
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
this.
|
|
3735
|
+
const clampedCols = Math.max(1, Math.min(500, Math.floor(cols)));
|
|
3736
|
+
const clampedRows = Math.max(1, Math.min(200, Math.floor(rows)));
|
|
3737
|
+
this.ptyProcess.resize(clampedCols, clampedRows);
|
|
3738
|
+
this._cols = clampedCols;
|
|
3739
|
+
this._rows = clampedRows;
|
|
3738
3740
|
} catch {
|
|
3739
3741
|
}
|
|
3740
3742
|
}
|
|
@@ -3937,12 +3939,25 @@ async function serveStaticFile(webClientPath, filePath, req, res) {
|
|
|
3937
3939
|
}
|
|
3938
3940
|
}
|
|
3939
3941
|
var PING_INTERVAL_MS = 3e4;
|
|
3940
|
-
var
|
|
3942
|
+
var DEFAULT_SCROLLBACK_SIZE = 10485760;
|
|
3943
|
+
var DEFAULT_CLIENT_BUFFER_LIMIT = 262144;
|
|
3941
3944
|
function createSyncServer(options) {
|
|
3942
|
-
const {
|
|
3945
|
+
const {
|
|
3946
|
+
ptyManager,
|
|
3947
|
+
token,
|
|
3948
|
+
webClientPath,
|
|
3949
|
+
host,
|
|
3950
|
+
port,
|
|
3951
|
+
resizePolicy = "last-writer-wins",
|
|
3952
|
+
scrollbackBufferSize = DEFAULT_SCROLLBACK_SIZE,
|
|
3953
|
+
clientBufferLimit = DEFAULT_CLIENT_BUFFER_LIMIT,
|
|
3954
|
+
logger
|
|
3955
|
+
} = options;
|
|
3943
3956
|
const clients = /* @__PURE__ */ new Set();
|
|
3944
3957
|
const aliveMap = /* @__PURE__ */ new WeakMap();
|
|
3958
|
+
const dropCounters = /* @__PURE__ */ new WeakMap();
|
|
3945
3959
|
let scrollbackBuffer = "";
|
|
3960
|
+
let scrollbackBytes = 0;
|
|
3946
3961
|
let seq = 0;
|
|
3947
3962
|
const httpServer = createServer2(async (req, res) => {
|
|
3948
3963
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
@@ -3979,9 +3994,12 @@ function createSyncServer(options) {
|
|
|
3979
3994
|
wssServer.on("connection", (ws) => {
|
|
3980
3995
|
clients.add(ws);
|
|
3981
3996
|
aliveMap.set(ws, true);
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3997
|
+
let syncReceived = false;
|
|
3998
|
+
const fallbackTimer = setTimeout(() => {
|
|
3999
|
+
if (!syncReceived && scrollbackBuffer.length > 0) {
|
|
4000
|
+
ws.send(JSON.stringify({ type: "data", data: scrollbackBuffer, seq }));
|
|
4001
|
+
}
|
|
4002
|
+
}, 150);
|
|
3985
4003
|
ws.send(JSON.stringify({ type: "resize", cols: ptyManager.cols, rows: ptyManager.rows }));
|
|
3986
4004
|
ws.on("pong", () => {
|
|
3987
4005
|
aliveMap.set(ws, true);
|
|
@@ -3992,10 +4010,32 @@ function createSyncServer(options) {
|
|
|
3992
4010
|
if (message.type === "input" && typeof message.data === "string") {
|
|
3993
4011
|
ptyManager.write(message.data);
|
|
3994
4012
|
} else if (message.type === "resize" && typeof message.cols === "number" && typeof message.rows === "number") {
|
|
3995
|
-
if (
|
|
4013
|
+
if (resizePolicy === "last-writer-wins") {
|
|
3996
4014
|
ptyManager.resize(message.cols, message.rows);
|
|
4015
|
+
const resizeMsg = JSON.stringify({ type: "resize", cols: ptyManager.cols, rows: ptyManager.rows });
|
|
4016
|
+
for (const c of clients) {
|
|
4017
|
+
if (c.readyState === c.OPEN) {
|
|
4018
|
+
c.send(resizeMsg);
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
} else if (message.type === "sync" && typeof message.lastSeq === "number") {
|
|
4023
|
+
syncReceived = true;
|
|
4024
|
+
clearTimeout(fallbackTimer);
|
|
4025
|
+
if (message.lastSeq === -1 || message.lastSeq < seq - scrollbackBuffer.length) {
|
|
4026
|
+
if (scrollbackBuffer.length > 0) {
|
|
4027
|
+
ws.send(JSON.stringify({ type: "data", data: scrollbackBuffer, seq }));
|
|
4028
|
+
}
|
|
4029
|
+
} else {
|
|
4030
|
+
const missed = seq - message.lastSeq;
|
|
4031
|
+
if (missed > 0 && scrollbackBuffer.length > 0) {
|
|
4032
|
+
const delta = missed <= scrollbackBuffer.length ? scrollbackBuffer.slice(-missed) : scrollbackBuffer;
|
|
4033
|
+
ws.send(JSON.stringify({ type: "data", data: delta, seq }));
|
|
4034
|
+
}
|
|
3997
4035
|
}
|
|
3998
4036
|
} else if (message.type === "resume" && typeof message.lastSeq === "number") {
|
|
4037
|
+
syncReceived = true;
|
|
4038
|
+
clearTimeout(fallbackTimer);
|
|
3999
4039
|
const missed = seq - message.lastSeq;
|
|
4000
4040
|
if (missed > 0 && scrollbackBuffer.length > 0) {
|
|
4001
4041
|
const delta = missed <= scrollbackBuffer.length ? scrollbackBuffer.slice(-missed) : scrollbackBuffer;
|
|
@@ -4013,16 +4053,31 @@ function createSyncServer(options) {
|
|
|
4013
4053
|
});
|
|
4014
4054
|
});
|
|
4015
4055
|
ptyManager.onData((data) => {
|
|
4056
|
+
if (logger) logger.write(data);
|
|
4016
4057
|
seq += data.length;
|
|
4017
4058
|
scrollbackBuffer += data;
|
|
4018
|
-
|
|
4019
|
-
|
|
4059
|
+
scrollbackBytes += Buffer.byteLength(data, "utf-8");
|
|
4060
|
+
if (scrollbackBytes > scrollbackBufferSize) {
|
|
4061
|
+
const target = Math.floor(scrollbackBufferSize * 0.5);
|
|
4062
|
+
const excessBytes = scrollbackBytes - target;
|
|
4063
|
+
const avgBytesPerChar = scrollbackBytes / scrollbackBuffer.length;
|
|
4064
|
+
const trimChars = Math.min(Math.ceil(excessBytes / avgBytesPerChar), scrollbackBuffer.length);
|
|
4065
|
+
const trimmed = scrollbackBuffer.slice(0, trimChars);
|
|
4066
|
+
scrollbackBuffer = scrollbackBuffer.slice(trimChars);
|
|
4067
|
+
scrollbackBytes -= Buffer.byteLength(trimmed, "utf-8");
|
|
4020
4068
|
}
|
|
4021
4069
|
const msg = JSON.stringify({ type: "data", data, seq });
|
|
4022
4070
|
for (const client of clients) {
|
|
4023
|
-
if (client.readyState
|
|
4024
|
-
|
|
4071
|
+
if (client.readyState !== client.OPEN) continue;
|
|
4072
|
+
if (client.bufferedAmount > clientBufferLimit) {
|
|
4073
|
+
const count = (dropCounters.get(client) || 0) + 1;
|
|
4074
|
+
dropCounters.set(client, count);
|
|
4075
|
+
if (count === 100 || count > 100 && count % 1e3 === 0) {
|
|
4076
|
+
console.warn(` Warning: Dropped ${count} messages for slow client`);
|
|
4077
|
+
}
|
|
4078
|
+
continue;
|
|
4025
4079
|
}
|
|
4080
|
+
client.send(msg);
|
|
4026
4081
|
}
|
|
4027
4082
|
});
|
|
4028
4083
|
httpServer.listen(port, host);
|
|
@@ -4038,8 +4093,9 @@ function createSyncServer(options) {
|
|
|
4038
4093
|
wssServer.close();
|
|
4039
4094
|
httpServer.close();
|
|
4040
4095
|
},
|
|
4041
|
-
|
|
4042
|
-
|
|
4096
|
+
resizeFromLocal(cols, rows) {
|
|
4097
|
+
ptyManager.resize(cols, rows);
|
|
4098
|
+
const msg = JSON.stringify({ type: "resize", cols: ptyManager.cols, rows: ptyManager.rows });
|
|
4043
4099
|
for (const client of clients) {
|
|
4044
4100
|
if (client.readyState === client.OPEN) {
|
|
4045
4101
|
client.send(msg);
|
|
@@ -4072,7 +4128,17 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
|
4072
4128
|
import { homedir } from "os";
|
|
4073
4129
|
import { join as join3 } from "path";
|
|
4074
4130
|
var DEFAULT_CONFIG = {
|
|
4075
|
-
networkingMode: "local"
|
|
4131
|
+
networkingMode: "local",
|
|
4132
|
+
scrollbackBufferSize: 10485760,
|
|
4133
|
+
// 10MB
|
|
4134
|
+
maxSessions: 20,
|
|
4135
|
+
idleTimeoutMs: 864e5,
|
|
4136
|
+
// 24 hours
|
|
4137
|
+
logRetentionDays: 30,
|
|
4138
|
+
clientBufferLimit: 262144,
|
|
4139
|
+
// 256KB
|
|
4140
|
+
maxTerminalCols: 500,
|
|
4141
|
+
maxTerminalRows: 200
|
|
4076
4142
|
};
|
|
4077
4143
|
function getConfigDir() {
|
|
4078
4144
|
return process.env.ITWILLSYNC_CONFIG_DIR || join3(homedir(), ".itwillsync");
|
|
@@ -4102,6 +4168,65 @@ function saveConfig(config) {
|
|
|
4102
4168
|
);
|
|
4103
4169
|
}
|
|
4104
4170
|
|
|
4171
|
+
// src/session-logger.ts
|
|
4172
|
+
import { createWriteStream, mkdirSync as mkdirSync2, unlinkSync } from "fs";
|
|
4173
|
+
import { createGzip } from "zlib";
|
|
4174
|
+
import { pipeline } from "stream/promises";
|
|
4175
|
+
import { createReadStream } from "fs";
|
|
4176
|
+
import { join as join4 } from "path";
|
|
4177
|
+
var FLUSH_INTERVAL_MS = 100;
|
|
4178
|
+
var BUFFER_SIZE = 4096;
|
|
4179
|
+
function getLogsDir() {
|
|
4180
|
+
return join4(getConfigDir(), "logs");
|
|
4181
|
+
}
|
|
4182
|
+
var SessionLogger = class {
|
|
4183
|
+
buffer = "";
|
|
4184
|
+
stream;
|
|
4185
|
+
logPath;
|
|
4186
|
+
flushTimer;
|
|
4187
|
+
closed = false;
|
|
4188
|
+
constructor(sessionId) {
|
|
4189
|
+
const logsDir = getLogsDir();
|
|
4190
|
+
mkdirSync2(logsDir, { recursive: true });
|
|
4191
|
+
this.logPath = join4(logsDir, `${sessionId}.log`);
|
|
4192
|
+
this.stream = createWriteStream(this.logPath, { flags: "a" });
|
|
4193
|
+
this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
4194
|
+
}
|
|
4195
|
+
write(data) {
|
|
4196
|
+
if (this.closed) return;
|
|
4197
|
+
this.buffer += data;
|
|
4198
|
+
if (this.buffer.length >= BUFFER_SIZE) {
|
|
4199
|
+
this.flush();
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
flush() {
|
|
4203
|
+
if (this.buffer.length === 0 || this.closed) return;
|
|
4204
|
+
this.stream.write(this.buffer);
|
|
4205
|
+
this.buffer = "";
|
|
4206
|
+
}
|
|
4207
|
+
async close() {
|
|
4208
|
+
if (this.closed) return;
|
|
4209
|
+
this.closed = true;
|
|
4210
|
+
clearInterval(this.flushTimer);
|
|
4211
|
+
this.flush();
|
|
4212
|
+
await new Promise((resolve) => {
|
|
4213
|
+
this.stream.end(() => resolve());
|
|
4214
|
+
});
|
|
4215
|
+
try {
|
|
4216
|
+
const gzPath = this.logPath + ".gz";
|
|
4217
|
+
const source = createReadStream(this.logPath);
|
|
4218
|
+
const gzip = createGzip();
|
|
4219
|
+
const dest = createWriteStream(gzPath);
|
|
4220
|
+
await pipeline(source, gzip, dest);
|
|
4221
|
+
try {
|
|
4222
|
+
unlinkSync(this.logPath);
|
|
4223
|
+
} catch {
|
|
4224
|
+
}
|
|
4225
|
+
} catch {
|
|
4226
|
+
}
|
|
4227
|
+
}
|
|
4228
|
+
};
|
|
4229
|
+
|
|
4105
4230
|
// src/wizard.ts
|
|
4106
4231
|
import * as p from "@clack/prompts";
|
|
4107
4232
|
async function runSetupWizard() {
|
|
@@ -4173,23 +4298,23 @@ async function runSetupWizard() {
|
|
|
4173
4298
|
// src/cli-options.ts
|
|
4174
4299
|
import { readFileSync as readFileSync3 } from "fs";
|
|
4175
4300
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4176
|
-
import { dirname as dirname3, join as
|
|
4301
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
4177
4302
|
|
|
4178
4303
|
// src/hub-client.ts
|
|
4179
4304
|
import { spawn as spawn2 } from "child_process";
|
|
4180
|
-
import { readFileSync as readFileSync2, existsSync as existsSync2, unlinkSync } from "fs";
|
|
4305
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
4181
4306
|
import { homedir as homedir2 } from "os";
|
|
4182
|
-
import { join as
|
|
4307
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
4183
4308
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4184
4309
|
import { request } from "http";
|
|
4185
4310
|
var HUB_INTERNAL_PORT = 7963;
|
|
4186
4311
|
var HUB_EXTERNAL_PORT = 7962;
|
|
4187
4312
|
var SESSION_PORT_START = 7964;
|
|
4188
4313
|
function getHubDir() {
|
|
4189
|
-
return process.env.ITWILLSYNC_CONFIG_DIR ||
|
|
4314
|
+
return process.env.ITWILLSYNC_CONFIG_DIR || join5(homedir2(), ".itwillsync");
|
|
4190
4315
|
}
|
|
4191
4316
|
function getHubConfigPath() {
|
|
4192
|
-
return
|
|
4317
|
+
return join5(getHubDir(), "hub.json");
|
|
4193
4318
|
}
|
|
4194
4319
|
async function discoverHub() {
|
|
4195
4320
|
return new Promise((resolve) => {
|
|
@@ -4272,18 +4397,18 @@ async function killStaleHub() {
|
|
|
4272
4397
|
}
|
|
4273
4398
|
const hubDir = getHubDir();
|
|
4274
4399
|
try {
|
|
4275
|
-
|
|
4400
|
+
unlinkSync2(join5(hubDir, "hub.json"));
|
|
4276
4401
|
} catch {
|
|
4277
4402
|
}
|
|
4278
4403
|
try {
|
|
4279
|
-
|
|
4404
|
+
unlinkSync2(join5(hubDir, "hub.pid"));
|
|
4280
4405
|
} catch {
|
|
4281
4406
|
}
|
|
4282
4407
|
return true;
|
|
4283
4408
|
}
|
|
4284
4409
|
async function spawnHub() {
|
|
4285
4410
|
const __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
4286
|
-
const hubPath =
|
|
4411
|
+
const hubPath = join5(__dirname2, "hub", "daemon.js");
|
|
4287
4412
|
return new Promise((resolve, reject) => {
|
|
4288
4413
|
const child = spawn2("node", [hubPath], {
|
|
4289
4414
|
detached: true,
|
|
@@ -4478,7 +4603,7 @@ async function sendHeartbeat(sessionId) {
|
|
|
4478
4603
|
var __filename = fileURLToPath3(import.meta.url);
|
|
4479
4604
|
var __dirname = dirname3(__filename);
|
|
4480
4605
|
var { version } = JSON.parse(
|
|
4481
|
-
readFileSync3(
|
|
4606
|
+
readFileSync3(join6(__dirname, "..", "package.json"), "utf-8")
|
|
4482
4607
|
);
|
|
4483
4608
|
var DEFAULT_PORT = SESSION_PORT_START;
|
|
4484
4609
|
function parseArgs(argv) {
|
|
@@ -4594,7 +4719,7 @@ Hub Management:
|
|
|
4594
4719
|
|
|
4595
4720
|
// src/index.ts
|
|
4596
4721
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
4597
|
-
import { join as
|
|
4722
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
4598
4723
|
import { spawn as spawn3 } from "child_process";
|
|
4599
4724
|
function preventSleep() {
|
|
4600
4725
|
try {
|
|
@@ -4729,13 +4854,13 @@ async function main() {
|
|
|
4729
4854
|
printHelp();
|
|
4730
4855
|
process.exit(1);
|
|
4731
4856
|
}
|
|
4857
|
+
const config = loadConfig();
|
|
4732
4858
|
let networkingMode = "local";
|
|
4733
4859
|
if (options.tailscale) {
|
|
4734
4860
|
networkingMode = "tailscale";
|
|
4735
4861
|
} else if (options.local) {
|
|
4736
4862
|
networkingMode = "local";
|
|
4737
4863
|
} else {
|
|
4738
|
-
const config = loadConfig();
|
|
4739
4864
|
networkingMode = config.networkingMode;
|
|
4740
4865
|
}
|
|
4741
4866
|
const [cmd, ...cmdArgs] = options.command;
|
|
@@ -4746,15 +4871,20 @@ async function main() {
|
|
|
4746
4871
|
const host = options.localhost ? "127.0.0.1" : "0.0.0.0";
|
|
4747
4872
|
const ip = await resolveSessionIP(networkingMode, options.localhost);
|
|
4748
4873
|
const __dirname2 = dirname4(fileURLToPath4(import.meta.url));
|
|
4749
|
-
const webClientPath =
|
|
4874
|
+
const webClientPath = join7(__dirname2, "web-client");
|
|
4750
4875
|
const ptyManager = new PtyManager(cmd, cmdArgs);
|
|
4876
|
+
const sessionId = `${cmd}-${Date.now().toString(36)}`;
|
|
4877
|
+
const sessionLogger = new SessionLogger(sessionId);
|
|
4751
4878
|
const server = createSyncServer({
|
|
4752
4879
|
ptyManager,
|
|
4753
4880
|
token,
|
|
4754
4881
|
webClientPath,
|
|
4755
4882
|
host,
|
|
4756
4883
|
port,
|
|
4757
|
-
|
|
4884
|
+
resizePolicy: "last-writer-wins",
|
|
4885
|
+
scrollbackBufferSize: config.scrollbackBufferSize,
|
|
4886
|
+
clientBufferLimit: config.clientBufferLimit,
|
|
4887
|
+
logger: sessionLogger
|
|
4758
4888
|
});
|
|
4759
4889
|
let registeredSession = null;
|
|
4760
4890
|
let heartbeatInterval = null;
|
|
@@ -4805,6 +4935,11 @@ async function main() {
|
|
|
4805
4935
|
process.stdin.resume();
|
|
4806
4936
|
process.stdin.setEncoding("utf-8");
|
|
4807
4937
|
process.stdin.on("data", (data) => {
|
|
4938
|
+
if (process.stdout.columns && process.stdout.rows) {
|
|
4939
|
+
if (process.stdout.columns !== ptyManager.cols || process.stdout.rows !== ptyManager.rows) {
|
|
4940
|
+
server.resizeFromLocal(process.stdout.columns, process.stdout.rows);
|
|
4941
|
+
}
|
|
4942
|
+
}
|
|
4808
4943
|
ptyManager.write(data);
|
|
4809
4944
|
});
|
|
4810
4945
|
ptyManager.onData((data) => {
|
|
@@ -4812,8 +4947,7 @@ async function main() {
|
|
|
4812
4947
|
});
|
|
4813
4948
|
function handleResize() {
|
|
4814
4949
|
if (process.stdout.columns && process.stdout.rows) {
|
|
4815
|
-
|
|
4816
|
-
server.broadcastResize(process.stdout.columns, process.stdout.rows);
|
|
4950
|
+
server.resizeFromLocal(process.stdout.columns, process.stdout.rows);
|
|
4817
4951
|
}
|
|
4818
4952
|
}
|
|
4819
4953
|
process.stdout.on("resize", handleResize);
|
|
@@ -4835,6 +4969,7 @@ async function main() {
|
|
|
4835
4969
|
sleepGuard?.kill();
|
|
4836
4970
|
server.close();
|
|
4837
4971
|
ptyManager.kill();
|
|
4972
|
+
await sessionLogger.close();
|
|
4838
4973
|
}
|
|
4839
4974
|
ptyManager.onExit(async (exitCode) => {
|
|
4840
4975
|
console.log(`
|