@typhons/sandbox-tools 0.6.3 → 0.6.5
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/package.json +4 -2
- package/relay-client.js +11 -11
- package/sandbox-start-all.sh +9 -0
- package/start-webchat.sh +53 -0
- package/webchat-index.html +1591 -0
- package/webchat-server.cjs +34 -0
- package/package.json~ +0 -19
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";var Lt=(o,e)=>()=>(e||o((e={exports:{}}).exports,e),e.exports);var At=Lt((ee,Tt)=>{"use strict";var q=require("fs"),{StringDecoder:at}=require("string_decoder"),Ht=o=>o.replace(/[^a-zA-Z0-9]/g,"-"),vt=o=>String(o??"").replace(/\x1b\[[0-9;?]*[ -\/]*[@-~]/g,"").replace(/\x1b/g,"");function ut(o,e=8e3){if(o==null)return"";if(typeof o=="string")return o.length>e?o.slice(0,e)+`
|
|
3
|
+
\u2026 (${o.length-e} more chars)`:o;if(!Array.isArray(o))return"";let f=[];for(let r of o)r&&r.type==="text"&&r.text&&f.push(r.text);return ut(f.join(`
|
|
4
|
+
`),e)}function ct(o,e=4){let f=[];if(!Array.isArray(o))return f;for(let r of o){if(f.length>=e)break;r&&r.type==="image"&&r.source&&r.source.type==="base64"&&f.push(`data:${r.source.media_type};base64,${r.source.data}`)}return f}var z=(o,e)=>(o.match(new RegExp(`<${e}(?:\\s[^>]*)?>([\\s\\S]*?)</${e}>`))||[])[1];function Q(o,e,f){let r=vt(e).replace(/[ \t]+$/gm,"").replace(/^\n+|\s+$/g,"");if(!r)return;let l=r.length>4e3?r.slice(0,4e3)+"\u2026":r;o.push({kind:"notice",text:l,pre:/\n/.test(l),ts:f})}var Jt=/<(system-reminder|ide_opened_file|session-start-hook|fork-boilerplate|tick)(?:\s[^>]*)?(?:\/>|>[\s\S]*?<\/\1>)/g;function tt(o,e,f,r=[]){let l=(e||"").trim();if(/^\[Request interrupted/.test(l)){o.push({kind:"notice",notice:"interrupted",text:"interrupted",ts:f});return}if(/^<(command-name|command-message|command-args)>/.test(l)){let u=z(l,"command-name");u&&u.trim()&&o.push({kind:"notice",text:u.trim(),ts:f});return}if(/^<local-command-(stdout|stderr)>/.test(l)){let u=z(l,"local-command-stdout")||"",a=z(l,"local-command-stderr")||"";Q(o,u+(a?`
|
|
5
|
+
`+a:""),f);return}if(/^Caveat: |^<local-command-caveat>/.test(l))return;if(/^<task-notification[\s>]/.test(l)){let u=z(l,"summary"),a=(z(l,"result")||"").replace(/\s+/g," ").trim();if(u&&u.trim()){let v=u.trim();a&&(v+=" \u2014 "+(a.length>160?a.slice(0,160)+"\u2026":a)),o.push({kind:"notice",text:v,ts:f})}return}if(/<bash-input>|^<bash-std(out|err)>/.test(l)){let u=z(l,"bash-input");u&&u.trim()&&o.push({kind:"notice",text:"$ "+u.trim(),ts:f});let a=z(l,"bash-stdout")||"",v=z(l,"bash-stderr")||"";Q(o,a+(v?`
|
|
6
|
+
`+v:""),f);return}if(/^<user-prompt-submit-hook[\s>]/.test(l)){let u=(z(l,"user-prompt-submit-hook")||"").trim();u&&Q(o,"hook: "+u,f);return}let m=l.match(/^<(teammate-message|channel-message|cross-session-message)(\s[^>]*)?>/);if(m){let u=((m[2]||"").match(/(?:teammate_id|channel|session|from)="([^"]*)"/)||[])[1],a=(z(l,m[1])||"").trim();Q(o,`${u||m[1]}: ${a}`,f);return}let S=(e||"").replace(Jt,"").trim();!S&&!r.length||o.push({kind:"user",text:S,images:r,ts:f})}function It(o){let e=[];if(!o||typeof o!="object"||o.isSidechain)return e;let f=o.timestamp;if(o.type==="system")return o.subtype==="compact_boundary"&&e.push({kind:"notice",text:"context compacted",ts:f}),e;if(o.type==="attachment"){let u=o.attachment;if(u&&u.type==="queued_command"&&!u.isMeta&&!o.isMeta&&(u.commandMode==="prompt"||u.commandMode==="task-notification")){let a=u.prompt,v=typeof a=="string"?a:Array.isArray(a)?a.filter(w=>w&&w.type==="text"&&w.text).map(w=>w.text).join(`
|
|
7
|
+
`):"",D=Array.isArray(a)?ct(a):[];(v||D.length)&&tt(e,v,f,D)}return e}if(o.type!=="user"&&o.type!=="assistant")return e;let r=o.message;if(!r)return e;if(o.type==="assistant"){let u=Array.isArray(r.content)?r.content:[{type:"text",text:String(r.content??"")}];for(let a of u)a&&(a.type==="text"&&a.text&&a.text.trim()?e.push({kind:"assistant",md:a.text,ts:f,model:r.model}):a.type==="thinking"&&a.thinking&&a.thinking.trim()?e.push({kind:"thinking",text:a.thinking,ts:f}):a.type==="tool_use"&&e.push({kind:"tool_use",id:a.id,name:a.name,input:a.input,ts:f}));return e}if(o.isMeta)return e;let l=r.content;if(typeof l=="string")return tt(e,l,f),e;if(!Array.isArray(l))return e;let m=[],S=[];for(let u of l)if(u)if(u.type==="tool_result"){let a=ut(u.content,6e3),v=!!u.is_error&&/want to proceed with this tool use|tool use was rejected|declined to answer/i.test(a);e.push({kind:"tool_result",id:u.tool_use_id,isError:!!u.is_error&&!v,declined:v,text:a,images:ct(u.content),ts:f})}else u.type==="text"&&u.text?m.push(u.text):u.type==="image"&&u.source&&u.source.type==="base64"&&S.push(`data:${u.source.media_type};base64,${u.source.data}`);return tt(e,m.join(`
|
|
8
|
+
|
|
9
|
+
`),f,S),e}function Et(o){let e=String(o??"").split(`
|
|
10
|
+
`).map(S=>S.replace(/\s+$/,""));e=e.filter(S=>!/[▐▛▜▌▝▘█]/.test(S));let f=0;for(let S=e.length-1;S>=0;S--)if(/^❯\s+\S/.test(e[S])){f=S+1;break}let r=e.slice(f),l=S=>!S.trim()||/^[\s─—–-]+$/.test(S)||/^─.*─$/.test(S.trim());for(;r.length&&l(r[0]);)r.shift();for(;r.length&&l(r[r.length-1]);)r.pop();let m=r.join(`
|
|
11
|
+
`).replace(/\n{3,}/g,`
|
|
12
|
+
|
|
13
|
+
`);return m.length>6e3?m.slice(-6e3):m}var bt={"\u2190":"Left","\u2192":"Right","\u2191":"Up","\u2193":"Down",enter:"Enter",return:"Enter",esc:"Escape",escape:"Escape",tab:"Tab",space:"Space"};function _t(o){let e={keys:[],filter:!1};if(!o)return e;let f=new Set;for(let r of String(o).split(/\s+·\s+/).map(l=>l.trim()).filter(Boolean)){let l=r.match(/^(.*?)\s+to\s+(.+)$/i),m=(l?l[1]:r).trim(),S=(l?l[2]:r).trim();if(/^type$/i.test(m)){e.filter=!0;continue}for(let u of m.split(/\s*\/\s*/).map(a=>a.trim()).filter(Boolean)){let a=bt[u.toLowerCase()]||bt[u];if(!a&&/^[a-z0-9]$/i.test(u)&&(a=u),!a)continue;let v=a+"|"+S;f.has(v)||(f.add(v),e.keys.push({key:a,face:u,label:S}))}}return e}function $t(o){let e=String(o||"").split(`
|
|
14
|
+
`),f=w=>/( to |·)/i.test(w)&&/(←|→|↑|↓|\benter\b|\besc\b|\btab\b)/i.test(w),r=-1,l=0;for(let w=0;w<e.length;w++){if(f(e[w]))continue;let I=e[w].trim().split(/\s{2,}/).filter(Boolean);I.length>=2&&I.every(B=>B.length<=16)&&I.length>l&&(l=I.length,r=w)}if(r<0)return null;let m=e[r],S=[],u=0;for(let w of m.split(/\s{2,}/)){let I=w.trim();if(!I)continue;let B=m.indexOf(w,u);S.push({label:I,col:B}),u=B+w.length}if(S.length<2)return null;let a=-1;for(let w=r-1;w>=0&&w>=r-3;w--){let I=e[w].search(/[▲▴◆●]/);if(I>=0){a=I;break}}let v=0;if(a>=0){let w=1/0;S.forEach((I,B)=>{let L=Math.abs(I.col-a);L<w&&(w=L,v=B)})}return{title:(e.find((w,I)=>w.trim()&&I!==r&&!f(w))||"").trim(),options:S.map(w=>w.label),current:v}}function Wt(o){let e=Et(o);if(!e)return null;let f=e.split(`
|
|
15
|
+
`),r="";for(let a=f.length-1;a>=0;a--){let v=f[a].trim();if(/(←|→|↑|↓|\besc\b|\benter\b|\btab\b|\btype\b)/i.test(v)&&/( to |·)/i.test(v)){r=v;break}}let{keys:l,filter:m}=_t(r),u=l.some(a=>a.key==="Left")&&l.some(a=>a.key==="Right")?$t(e):null;return{text:e,keys:l,filter:m,legend:r,slider:u}}function Gt(o){let e=String(o||"").replace(/\s+$/,"").split(`
|
|
16
|
+
`),f=/^(\s*)(❯\s*)?(\d+)\.\s+(.+?)\s*$/,r=/^[\s─—-]{8,}$/,l=/^[\s☐☑☒○●◉▶❯•‣*]+/,m=[];for(let y=0;y<e.length;y++){let T=e[y].match(f);T&&m.push({i:y,cursor:!!T[2],n:parseInt(T[3],10),raw:T[4]})}if(m.length<2)return null;let S=4,u=[[m[0]]];for(let y=1;y<m.length;y++)m[y].i-m[y-1].i<=S?u[u.length-1].push(m[y]):u.push([m[y]]);let a=u.filter(y=>y.length>=2).pop();if(!a)return null;let v=a[0].i,D=a[a.length-1].i,w=a.map((y,T)=>{let A=/✔/.test(y.raw),F=y.raw.search(/\s{2,}/),C=(F>0?y.raw.slice(0,F):y.raw).replace(/\s*✔\s*/g," ").trim(),U=F>0?y.raw.slice(F).replace(/\s*✔\s*/g," ").trim():"";if(!U){let R=T+1<a.length?a[T+1].i:D+S,W=[];for(let k=y.i+1;k<R&&k<e.length;k++){let j=e[k].trim();!j||r.test(j)||f.test(e[k])||/\b(enter|esc|space|tab|ctrl\+[a-z]|shift\+tab)\b/i.test(j)&&/(·| to )/i.test(j)||W.push(j)}U=W.join(" ")}return{n:y.n,label:C,desc:U,selected:y.cursor,current:A}}),I=[];for(let y=v-1,T=0;y>=0&&T<5;y--){let A=e[y].trim();if(r.test(A)){if(I.length)break;continue}if(!A){if(I.length)break;continue}if(A.startsWith("\u276F"))break;I.unshift(A.replace(l,"").trim()),T++}let B=I[0]||"",L=I.slice(1).join(" "),J="";for(let y=D+1;y<Math.min(e.length,D+7);y++){let T=e[y].trim();if(T.startsWith("\u276F"))break;if(/\b(enter|esc|space|tab)\b/i.test(T)&&/(·| to )/i.test(T)){J=T;break}}let N="";{let y=Math.max(0,v-60);for(let C=v-1;C>=y;C--)if(/^❯(\s|$)/.test(e[C].trim())){y=C+1;break}for(let C=y;C<v;C++)if(r.test(e[C].trim())){y=C+1;break}let T=new Set(I),A=/^[\s─—╌┄┈╍-]+$/,F=[];for(let C=y;C<v;C++){let U=e[C].replace(/\s+$/,""),R=U.trim();if(!R||A.test(R)){F.push("");continue}T.has(R.replace(l,"").trim())||F.push(U)}N=F.join(`
|
|
17
|
+
`).replace(/\n{3,}/g,`
|
|
18
|
+
|
|
19
|
+
`).trim(),N.length>4e3&&(N=N.slice(0,4e3)+"\u2026"),N.length<80&&(N="")}return{title:B,body:L,options:w,hint:J,context:N}}var lt=class{constructor({onItem:e,onReset:f,maxItems:r=5e3}={}){this.onItem=e||(()=>{}),this.onReset=f||(()=>{}),this.maxItems=r,this.items=[],this.seq=0,this.seenUuids=new Set,this.file=null,this.offset=0,this.partial="",this.decoder=new at("utf8")}setFile(e){e!==this.file&&(this.file=e,this.offset=0,this.partial="",this.items=[],this.seenUuids=new Set,this.decoder=new at("utf8"),this.onReset())}pump(){if(!this.file)return;let e;try{e=q.statSync(this.file)}catch{return}if(e.size<this.offset&&(this.offset=0,this.partial="",this.decoder=new at("utf8")),e.size===this.offset)return;let f=q.openSync(this.file,"r");try{for(;this.offset<e.size;){let r=Math.min(e.size-this.offset,4194304),l=Buffer.alloc(r),m=q.readSync(f,l,0,r,this.offset);if(m<=0)break;this.offset+=m,this.ingest(this.decoder.write(l.subarray(0,m)))}}finally{q.closeSync(f)}}ingest(e){let f=(this.partial+e).split(`
|
|
20
|
+
`);this.partial=f.pop()||"";for(let r of f){if(!r.trim())continue;let l;try{l=JSON.parse(r)}catch{continue}if(l.uuid){if(this.seenUuids.has(l.uuid))continue;this.seenUuids.add(l.uuid)}for(let m of It(l))m.i=++this.seq,this.items.push(m),this.onItem(m)}this.items.length>this.maxItems&&(this.items=this.items.slice(-Math.floor(this.maxItems*.8))),this.seenUuids.size>this.maxItems*4&&this.seenUuids.clear()}};Tt.exports={sanitizeCwd:Ht,stripAnsi:vt,textOfBlocks:ut,imagesOfBlocks:ct,userTextItems:tt,extractItems:It,parseDialog:Gt,extractPanel:Et,parseKeyLegend:_t,parseSlider:$t,parseGenericView:Wt,Tailer:lt}});var Kt=require("http"),$=require("fs"),P=require("path"),Xt=require("os"),ft=require("crypto"),{execFileSync:O}=require("child_process"),{sanitizeCwd:Ct,parseDialog:Pt,parseGenericView:Vt,Tailer:Yt,extractItems:Zt}=At(),qt=["index.html","webchat-index.html"].map(o=>P.join(__dirname,o)).find(o=>{try{return $.statSync(o).isFile()}catch{return!1}}),Qt=(()=>{try{return ft.createHash("md5").update($.readFileSync(__filename)).digest("hex")}catch{return null}})(),Ot=o=>new Promise(e=>setTimeout(e,o));function jt(o={}){let e=o.port??8777,f=o.tmux||"rc-chat",r=o.transcript||null,l=o.home||Xt.homedir(),m=P.join(l,".webchat"),S=P.join(m,"uploads");$.mkdirSync(S,{recursive:!0});let u=P.join(m,"token"),a=o.token||process.env.WEBCHAT_TOKEN||"";if(!a){try{a=$.readFileSync(u,"utf8").trim()}catch{}a||(a=ft.randomBytes(16).toString("hex"),$.writeFileSync(u,a,{mode:384}))}function v(s){let t={};for(let c of $.readdirSync("/proc"))if(/^\d+$/.test(c))try{let h=$.readFileSync(`/proc/${c}/stat`,"utf8").match(/\)\s+\S\s+(\d+)/);h&&(t[c]=h[1])}catch{}let n={};for(let[c,g]of Object.entries(t))(n[g]=n[g]||[]).push(c);let d=new Set,i=[String(s)];for(;i.length;){let c=i.shift();for(let g of n[c]||[])d.has(g)||(d.add(g),i.push(g))}return d}function D(){let s=P.join(l,".claude","sessions"),t=[],n=[];try{n=$.readdirSync(s)}catch{return t}for(let d of n)if(d.endsWith(".json"))try{let i=JSON.parse($.readFileSync(P.join(s,d),"utf8"));i&&i.pid&&typeof i.sessionId=="string"&&typeof i.cwd=="string"&&t.push(i)}catch{}return t}function w(s){let t=null;try{for(let n of $.readdirSync(s)){if(!n.endsWith(".jsonl")||n.startsWith("agent-"))continue;let d=$.statSync(P.join(s,n));(!t||d.mtimeMs>t.mtimeMs)&&(t={file:P.join(s,n),mtimeMs:d.mtimeMs})}}catch{}return t?.file||null}function I(s){for(let t of s)try{let n=$.readFileSync(`/proc/${t}/cmdline`,"utf8").split("\0");if(/(^|\/)claude(\b|$)/.test(n.slice(0,2).join(" ")))return!0}catch{}return!1}let B=":%:";function L(s){let t=["#{session_name}","#{pane_id}","#{pane_pid}","#{pane_current_path}"].join(B),n=O("tmux",["list-panes","-a","-F",t],{encoding:"utf8"}).trim().split(`
|
|
21
|
+
`).filter(Boolean).map(d=>{let i=d.split(B);return{tmux:i[0],paneId:i[1],panePid:i[2],paneCwd:i.slice(3).join(B)}});return s==null?n:n.filter(d=>d.tmux===s)}function J(s,t,{requireClaude:n}={}){let d=v(s.panePid);d.add(String(s.panePid));for(let c of t)if(d.has(String(c.pid))){try{process.kill(c.pid,0)}catch{continue}return{paneId:s.paneId,transcript:P.join(l,".claude","projects",Ct(c.cwd),`${c.sessionId}.jsonl`),sessionId:c.sessionId,cwd:c.cwd,registryStatus:c.status,reg:c}}if(n&&!I(d)||!s.paneCwd)return null;let i=w(P.join(l,".claude","projects",Ct(s.paneCwd)));return i?{paneId:s.paneId,transcript:i,sessionId:P.basename(i,".jsonl"),cwd:s.paneCwd,registryStatus:null,viaFallback:!0,reg:null}:null}function N(s){let t=d=>{let i=String(d).replace(/\s+/g," ").trim();return i.length>90?i.slice(0,90)+"\u2026":i},n;try{n=$.openSync(s,"r")}catch{return null}try{let d=Buffer.alloc(131072),i=$.readSync(n,d,0,d.length,0),c=null;for(let g of d.toString("utf8",0,i).split(`
|
|
22
|
+
`)){if(!g.trim())continue;let h;try{h=JSON.parse(g)}catch{continue}if(h.type==="summary"&&typeof h.summary=="string"&&h.summary.trim())return t(h.summary);if(!c&&h.type==="user"){let x=Zt(h).find(M=>M.kind==="user"&&M.text);x&&(c=t(x.text))}}return c}catch{return null}finally{try{$.closeSync(n)}catch{}}}function y(){let s;try{s=L(null)}catch{return[]}let t=D(),n=[],d=new Set;for(let i of s){if(d.has(i.tmux))continue;let c=null;try{c=J(i,t,{requireClaude:!0})}catch{}c&&(d.add(i.tmux),n.push({tmux:i.tmux,sessionId:c.sessionId,name:c.reg?.name||null,cwd:c.cwd,status:c.reg?.status||null,startedAt:c.reg?.startedAt||null,updatedAt:c.reg?.updatedAt||null,viaFallback:!!c.viaFallback,summary:N(c.transcript)}))}return n}function T(s,t){let n={tmux:s,discovered:{ok:!1,reason:"starting up"},sseClients:new Set,lastUsed:Date.now()};function d(p){let b=`data: ${JSON.stringify(p)}
|
|
23
|
+
|
|
24
|
+
`;for(let _ of n.sseClients)try{_.write(b)}catch{}}let i=0,c=null,g=new Yt({onItem:p=>{if(p.kind==="tool_use"&&p.name==="AskUserQuestion"?c={id:p.id,input:p.input}:(p.kind==="tool_result"&&c&&p.id===c.id||p.kind==="notice"&&p.notice==="interrupted")&&(c=null),p.kind==="notice"&&p.notice==="interrupted"&&Date.now()-i<1e4){let b=g.items.indexOf(p);b>=0&&g.items.splice(b,1);return}d({type:"item",item:p})},onReset:()=>{c=null,d({type:"reset",session:rt()})}});n.tailer=g;function h(){if(t){n.discovered={ok:!0,paneId:null,transcript:t,sessionId:P.basename(t,".jsonl"),cwd:null,registryStatus:null,pinned:!0};return}let p;try{p=L(s)}catch{p=[]}if(!p.length){n.discovered={ok:!1,reason:`tmux session "${s}" not found`};return}let b=D();for(let _ of p){let E=J(_,b);if(E){n.discovered={ok:!0,...E,reg:void 0};return}}n.discovered={ok:!1,reason:`no claude session found in tmux "${s}" yet`}}function x(){n.discovered.ok&&g.setFile(n.discovered.transcript),g.pump()}let M="unknown",et=null,pt=null,ht="";function mt(p){for(let b of D())if(b.sessionId===p)return b.status;return null}function nt(){return O("tmux",["capture-pane","-p","-t",n.discovered.paneId],{encoding:"utf8"})}let X=!1;function Dt(){try{let p=n.discovered.paneId,[b,_,E]=O("tmux",["display","-p","-t",p,"#{session_attached} #{window_width} #{window_height}"],{encoding:"utf8"}).trim().split(" ").map(Number);if(b>0){X&>();return}if(E>=45&&_>=100)return;O("tmux",["set-option","-w","-t",p,"window-size","manual"]),O("tmux",["resize-window","-t",p,"-x",String(Math.max(_||0,120)),"-y","50"]),X=!0}catch{}}function gt(){try{O("tmux",["set-option","-w","-u","-t",n.discovered.paneId,"window-size"])}catch{}X=!1}function Ft(){let p="unknown",b=null,_=null;if(n.discovered.ok&&!n.discovered.pinned){let H=mt(n.discovered.sessionId);if(H==="busy"||H==="waiting"||H==="idle")p=H;else if(n.discovered.paneId)try{p=/esc to interrupt/i.test(nt())?"busy":"idle"}catch{}if(p==="waiting"&&n.discovered.paneId)try{Dt();let Y=nt();b=Pt(Y),b||(_=Vt(Y))}catch{}else X&>()}let E=JSON.stringify([p,b,_]);E!==ht&&(ht=E,M=p,et=b,pt=_,d({type:"status",status:St()}))}let st=!1;function it(p){try{O("tmux",["display","-p","-t",p,"#{pane_in_mode}"],{encoding:"utf8"}).trim()==="1"&&O("tmux",["send-keys","-X","-t",p,"cancel"])}catch{}}function V(){if(!n.discovered.ok)throw new Error(n.discovered.reason||"no session");if(!n.discovered.paneId)throw new Error("no tmux pane (transcript pinned?)");return n.discovered.paneId}async function Rt(p){let b=V();if(it(b),st&&M!=="busy"){for(let Z=0;Z<3;Z++){try{O("tmux",["send-keys","-t",b,"Escape"])}catch{}await Ot(120)}st=!1}O("tmux",["load-buffer","-b","webchat","-"],{input:p}),O("tmux",["paste-buffer","-p","-d","-b","webchat","-t",b]);let _=g.seq,E=p.trim(),H=()=>{try{O("tmux",["send-keys","-t",b,"Enter"])}catch{}};setTimeout(H,250);let Y=0,kt=setInterval(()=>{let Z=g.items.some(ot=>ot.i>_&&ot.kind==="user"&&ot.text===E),xt=n.discovered.ok&&!n.discovered.pinned?mt(n.discovered.sessionId):null;if(Z||xt==="busy"||xt==="waiting"||++Y>2){clearInterval(kt);return}H()},1200);kt.unref?.()}let yt=new Set(["Up","Down","Left","Right","Enter","Escape","Space","Tab","BSpace","PageUp","PageDown","Home","End"]);async function wt(p){for(let _ of p){let E=String(_);if(!yt.has(E)&&(E.length<1||E.length>2e3||/[\x00-\x08\x0a-\x1f\x7f]/.test(E)))throw new Error(`key not allowed: ${JSON.stringify(E).slice(0,40)}`)}let b=V();it(b);for(let _ of p){let E=String(_);O("tmux",["send-keys","-t",b,...yt.has(E)?[E]:["-l",E]]),await Ot(80)}}async function zt(p){V();let b=Pt(nt()),_=b&&b.options.find(E=>/type something/i.test(E.label));if(!_)throw new Error("no free-text option in the current dialog");await wt(_.selected?[p,"Enter"]:[String(_.n),p,"Enter"])}function Nt(){let p=V();it(p),O("tmux",["send-keys","-t",p,"Escape"]),st=!0,i=Date.now();let b={kind:"notice",notice:"interrupted",text:"interrupted",ts:new Date().toISOString(),i:++g.seq};g.items.push(b),d({type:"item",item:b})}function rt(){return n.discovered.ok?{ok:!0,sessionId:n.discovered.sessionId,cwd:n.discovered.cwd,tmux:s,viaFallback:!!n.discovered.viaFallback,pinned:!!n.discovered.pinned}:{ok:!1,reason:n.discovered.reason,tmux:s}}function St(){return{state:M,build:Qt,working:M==="busy",dialog:M==="waiting"?et:null,raw:M==="waiting"&&!et?pt:null,ask:M==="waiting"&&c?c.input:null,session:rt()}}function Ut(){for(let p of n.sseClients)try{p.end()}catch{}n.sseClients.clear()}return Object.assign(n,{broadcast:d,discover:h,pump:x,pollState:Ft,injectMessage:Rt,sendKeys:wt,answerFreeText:zt,injectInterrupt:Nt,publicSessionInfo:rt,statusPayload:St,destroy:Ut}),n}let A=new Map;function F(s){let t=A.get(s);if(!t){t=T(s,s===f?r:null),A.set(s,t);try{t.discover(),t.pump()}catch(n){console.error("[webchat] discover failed:",n.message)}}return t.lastUsed=Date.now(),t}let C=F(f);function U(){for(let[s,t]of A)s!==f&&!t.sseClients.size&&Date.now()-t.lastUsed>10*60*1e3&&(t.destroy(),A.delete(s))}function R(s,t,n){let d=0,i=!1,c=[],g=(h,x)=>{i||(i=!0,n(h,x))};s.on("data",h=>{if(!i){if(d+=h.length,d>t){g(new Error("body too large")),s.destroy();return}c.push(h)}}),s.on("end",()=>g(null,Buffer.concat(c))),s.on("error",h=>g(h))}function W(s,t){let n=String(s.headers.authorization||""),d=/^Bearer\s+/i.test(n)?n.replace(/^Bearer\s+/i,"").trim():"",i=(s.headers.cookie||"").split(/;\s*/).find(x=>x.startsWith("webchat_token=")),c=i?i.slice(14):"",g=t.searchParams.get("token")||"",h=x=>x.length===a.length&&ft.timingSafeEqual(Buffer.from(x),Buffer.from(a));try{return d&&h(d)||g&&h(g)||c&&h(c)}catch{return!1}}let k=(s,t,n)=>{s.writeHead(t,{"Content-Type":"application/json"}),s.end(JSON.stringify(n))};function j(s,t){let n=s.searchParams.get("s");return n?/^[A-Za-z0-9_.@+-]{1,64}$/.test(n)?F(n):(k(t,400,{error:"bad session name"}),null):C}let K=Kt.createServer((s,t)=>{let n;try{n=new URL(s.url,"http://x")}catch{t.writeHead(400),t.end();return}let d=n.pathname;if(d==="/"){if(!W(s,n)){t.writeHead(403,{"Content-Type":"text/plain"}),t.end("missing or bad token \u2014 open the link that includes ?token=\u2026");return}t.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Set-Cookie":`webchat_token=${a}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=31536000`,"Cache-Control":"no-store"});try{t.end($.readFileSync(qt))}catch{t.end("webchat UI file missing")}return}if(!d.startsWith("/api/")){t.writeHead(404),t.end();return}if(!W(s,n))return k(t,403,{error:"unauthorized"});if(d==="/api/sessions"&&s.method==="GET")try{return k(t,200,{sessions:y()})}catch(i){return k(t,500,{error:String(i.message||i)})}if(d==="/api/events"&&s.method==="GET"){let i=j(n,t);if(!i)return;t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-store",Connection:"keep-alive","X-Accel-Buffering":"no"}),t.write(`retry: 2000
|
|
25
|
+
|
|
26
|
+
`),t.write(`data: ${JSON.stringify({type:"status",status:i.statusPayload()})}
|
|
27
|
+
|
|
28
|
+
`);for(let g of i.tailer.items.slice(-1200))t.write(`data: ${JSON.stringify({type:"item",item:g,backlog:!0})}
|
|
29
|
+
|
|
30
|
+
`);t.write(`data: ${JSON.stringify({type:"backlog_done"})}
|
|
31
|
+
|
|
32
|
+
`),i.sseClients.add(t);let c=setInterval(()=>{try{t.write(`data: {"type":"hb"}
|
|
33
|
+
|
|
34
|
+
`)}catch{}},15e3);s.on("close",()=>{clearInterval(c),i.sseClients.delete(t),i.lastUsed=Date.now()});return}if(d==="/api/status"&&s.method==="GET"){let i=j(n,t);return i?k(t,200,i.statusPayload()):void 0}if(d==="/api/items"&&s.method==="GET"){let i=j(n,t);return i?k(t,200,{status:i.statusPayload(),items:i.tailer.items.slice(-Number(n.searchParams.get("n")||500))}):void 0}if(d==="/api/send"&&s.method==="POST"){let i=j(n,t);if(!i)return;R(s,1*1024*1024,(c,g)=>{if(c)return k(t,413,{error:"too large"});let h;try{if({text:h}=JSON.parse(g.toString("utf8")),!h||typeof h!="string"||!h.trim())throw new Error("empty message")}catch(x){return k(t,400,{error:String(x.message||x)})}i.injectMessage(h).then(()=>k(t,200,{ok:!0})).catch(x=>k(t,400,{error:String(x.message||x)}))});return}if(d==="/api/keys"&&s.method==="POST"){let i=j(n,t);if(!i)return;R(s,64*1024,(c,g)=>{if(c)return k(t,413,{error:"too large"});let h;try{if({keys:h}=JSON.parse(g.toString("utf8")),!Array.isArray(h)||!h.length||h.length>16)throw new Error("keys must be a non-empty array (max 16)")}catch(x){return k(t,400,{error:String(x.message||x)})}i.sendKeys(h.map(String)).then(()=>k(t,200,{ok:!0})).catch(x=>k(t,400,{error:String(x.message||x)}))});return}if(d==="/api/answer"&&s.method==="POST"){let i=j(n,t);if(!i)return;R(s,1*1024*1024,(c,g)=>{if(c)return k(t,413,{error:"too large"});let h;try{if({text:h}=JSON.parse(g.toString("utf8")),!h||typeof h!="string"||!h.trim())throw new Error("empty answer")}catch(x){return k(t,400,{error:String(x.message||x)})}i.answerFreeText(h).then(()=>k(t,200,{ok:!0})).catch(x=>k(t,400,{error:String(x.message||x)}))});return}if(d==="/api/interrupt"&&s.method==="POST"){let i=j(n,t);if(!i)return;try{i.injectInterrupt(),k(t,200,{ok:!0})}catch(c){k(t,400,{error:String(c.message||c)})}return}if(d==="/api/file"&&s.method==="GET")try{let i=n.searchParams.get("p")||"";if(!P.isAbsolute(i))throw new Error("absolute path required");let c=$.statSync(i);if(!c.isFile())throw new Error("not a regular file");if(n.searchParams.get("raw")){if(c.size>20*1024*1024)throw new Error("file too large (20MB max)");let g={png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",bmp:"image/bmp",ico:"image/x-icon",avif:"image/avif",pdf:"application/pdf"},h=P.extname(i).slice(1).toLowerCase();t.writeHead(200,{"Content-Type":g[h]||"application/octet-stream","Content-Length":c.size,"Cache-Control":"no-store"}),$.createReadStream(i).pipe(t);return}if(c.size>2*1024*1024)throw new Error("file too large to preview (2MB max)");return k(t,200,{path:i,content:$.readFileSync(i,"utf8")})}catch(i){return k(t,404,{error:String(i.message||i)})}if(d==="/api/upload"&&s.method==="POST"){R(s,30*1024*1024,(i,c)=>{if(i)return k(t,413,{error:"file too large (30MB max)"});try{let{name:g,data:h}=JSON.parse(c.toString("utf8")),x=String(g||"file").replace(/[^a-zA-Z0-9._-]/g,"_").replace(/\.{2,}/g,"_").slice(-80),M=P.join(S,`${Date.now().toString(36)}-${x}`);$.writeFileSync(M,Buffer.from(h,"base64"),{mode:384}),k(t,200,{path:M})}catch(g){k(t,400,{error:String(g.message||g)})}});return}k(t,404,{error:"not found"})}),dt=[],G=s=>{try{s()}catch(t){console.error("[webchat]",t.message)}};function Mt(s){for(let t of A.values())G(()=>{t.discover(),t.pump()});dt.push(setInterval(()=>{for(let t of A.values())G(()=>t.discover())},2e3),setInterval(()=>{for(let t of A.values())G(()=>t.pump())},150),setInterval(()=>{for(let t of A.values())G(()=>t.pollState())},700),setInterval(()=>G(U),60*1e3)),K.listen(e,"0.0.0.0",()=>s&&s(K.address().port))}function Bt(){for(let s of dt)clearInterval(s);for(let s of A.values())s.destroy();K.close()}return{server:K,start:Mt,stop:Bt,token:a,sessions:A,listSessions:y,get tailer(){return C.tailer},get discovered(){return C.discovered}}}module.exports={createApp:jt};if(require.main===module){process.on("uncaughtException",r=>console.error("[webchat] uncaughtException:",r&&r.stack||r)),process.on("unhandledRejection",r=>console.error("[webchat] unhandledRejection:",r&&r.stack||r));let o=process.argv.slice(2),e=(r,l)=>{let m=o.indexOf(r);return m>=0&&o[m+1]?o[m+1]:l},f=jt({port:parseInt(process.env.WEBCHAT_PORT||e("--port","8777"),10),tmux:process.env.WEBCHAT_TMUX||e("--tmux","rc-chat"),transcript:e("--transcript",null)});f.start(r=>{console.log(`[webchat] listening on :${r}, default tmux session "${process.env.WEBCHAT_TMUX||e("--tmux","rc-chat")}"`),console.log(`[webchat] token: ${f.token}`),console.log(`[webchat] open: http://localhost:${r}/?token=${f.token}`)})}
|
package/package.json~
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@typhons/sandbox-tools",
|
|
3
|
-
"version": "0.6",
|
|
4
|
-
"type": "module",
|
|
5
|
-
"bin": {
|
|
6
|
-
"sandbox-relay-client": "./relay-client.js",
|
|
7
|
-
"sandbox-session-proxy": "./session-proxy.js",
|
|
8
|
-
"sandbox-watchdog": "./sandbox-watchdog.sh",
|
|
9
|
-
"sandbox-start-all": "./sandbox-start-all.sh",
|
|
10
|
-
"check-claude-active": "./check-claude-active.js",
|
|
11
|
-
"stop-agents": "./stop-agents.js",
|
|
12
|
-
"sandbox-clone": "./clone.js",
|
|
13
|
-
"typhons-share-mount": "./typhons-share-mount.js"
|
|
14
|
-
},
|
|
15
|
-
"dependencies": {
|
|
16
|
-
"ws": "^8.17.0",
|
|
17
|
-
"@homebridge/node-pty-prebuilt-multiarch": "^0.13.1"
|
|
18
|
-
}
|
|
19
|
-
}
|