aicodeman 1.9.7 → 1.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +1 -1
  2. package/dist/session.d.ts.map +1 -1
  3. package/dist/session.js +9 -9
  4. package/dist/session.js.map +1 -1
  5. package/dist/utils/claude-cli-resolver.d.ts +11 -0
  6. package/dist/utils/claude-cli-resolver.d.ts.map +1 -1
  7. package/dist/utils/claude-cli-resolver.js +14 -0
  8. package/dist/utils/claude-cli-resolver.js.map +1 -1
  9. package/dist/utils/index.d.ts +2 -1
  10. package/dist/utils/index.d.ts.map +1 -1
  11. package/dist/utils/index.js +2 -1
  12. package/dist/utils/index.js.map +1 -1
  13. package/dist/utils/node-pty-repair.d.ts +62 -0
  14. package/dist/utils/node-pty-repair.d.ts.map +1 -0
  15. package/dist/utils/node-pty-repair.js +149 -0
  16. package/dist/utils/node-pty-repair.js.map +1 -0
  17. package/dist/web/public/admin-ui.js.gz +0 -0
  18. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  19. package/dist/web/public/{app.5805dde4.js → app.67693130.js} +4 -4
  20. package/dist/web/public/app.67693130.js.br +0 -0
  21. package/dist/web/public/{app.5805dde4.js.gz → app.67693130.js.gz} +0 -0
  22. package/dist/web/public/constants.e7175b95.js.gz +0 -0
  23. package/dist/web/public/cron-ui.js.gz +0 -0
  24. package/dist/web/public/entrance-animations.js.gz +0 -0
  25. package/dist/web/public/i18n.8a202f9d.js +1 -0
  26. package/dist/web/public/i18n.8a202f9d.js.br +0 -0
  27. package/dist/web/public/i18n.8a202f9d.js.gz +0 -0
  28. package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
  29. package/dist/web/public/index.html +24 -7
  30. package/dist/web/public/index.html.br +0 -0
  31. package/dist/web/public/index.html.gz +0 -0
  32. package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
  33. package/dist/web/public/keyboard-accessory.9936227d.js.gz +0 -0
  34. package/dist/web/public/mobile-handlers.bc2b8c1d.js.gz +0 -0
  35. package/dist/web/public/mobile-overview.js +684 -0
  36. package/dist/web/public/mobile-overview.js.br +0 -0
  37. package/dist/web/public/mobile-overview.js.gz +0 -0
  38. package/dist/web/public/{mobile.f8466b0a.css → mobile.e56d787a.css} +1 -1
  39. package/dist/web/public/mobile.e56d787a.css.br +0 -0
  40. package/dist/web/public/mobile.e56d787a.css.gz +0 -0
  41. package/dist/web/public/notification-manager.5d229063.js.gz +0 -0
  42. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  43. package/dist/web/public/panels-ui.600881ae.js.gz +0 -0
  44. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  45. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  46. package/dist/web/public/respawn-ui.2d249da9.js.gz +0 -0
  47. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  48. package/dist/web/public/{session-ui.aab84498.js → session-ui.dbc88443.js} +1 -1
  49. package/dist/web/public/session-ui.dbc88443.js.br +0 -0
  50. package/dist/web/public/session-ui.dbc88443.js.gz +0 -0
  51. package/dist/web/public/settings-ui.726e1c19.js +67 -0
  52. package/dist/web/public/settings-ui.726e1c19.js.br +0 -0
  53. package/dist/web/public/settings-ui.726e1c19.js.gz +0 -0
  54. package/dist/web/public/styles.7e638f69.css.gz +0 -0
  55. package/dist/web/public/subagent-windows.4cc8b005.js.gz +0 -0
  56. package/dist/web/public/sw.js.gz +0 -0
  57. package/dist/web/public/terminal-ui.edf4d683.js +3 -0
  58. package/dist/web/public/terminal-ui.edf4d683.js.br +0 -0
  59. package/dist/web/public/terminal-ui.edf4d683.js.gz +0 -0
  60. package/dist/web/public/ultracode-panel.js.gz +0 -0
  61. package/dist/web/public/ultracode-windows.js.gz +0 -0
  62. package/dist/web/public/upload.html.gz +0 -0
  63. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  64. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  65. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  66. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  67. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  68. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  69. package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
  70. package/dist/web/public/vendor/xterm.css.gz +0 -0
  71. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  72. package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
  73. package/dist/web/public/webview-tabs.js.gz +0 -0
  74. package/package.json +3 -1
  75. package/scripts/fix-node-pty.mjs +261 -0
  76. package/scripts/postinstall.js +21 -24
  77. package/dist/web/public/app.5805dde4.js.br +0 -0
  78. package/dist/web/public/i18n.04d07c98.js +0 -1
  79. package/dist/web/public/i18n.04d07c98.js.br +0 -0
  80. package/dist/web/public/i18n.04d07c98.js.gz +0 -0
  81. package/dist/web/public/mobile.f8466b0a.css.br +0 -0
  82. package/dist/web/public/mobile.f8466b0a.css.gz +0 -0
  83. package/dist/web/public/session-ui.aab84498.js.br +0 -0
  84. package/dist/web/public/session-ui.aab84498.js.gz +0 -0
  85. package/dist/web/public/settings-ui.1e126e75.js +0 -67
  86. package/dist/web/public/settings-ui.1e126e75.js.br +0 -0
  87. package/dist/web/public/settings-ui.1e126e75.js.gz +0 -0
  88. package/dist/web/public/terminal-ui.f3a51974.js +0 -3
  89. package/dist/web/public/terminal-ui.f3a51974.js.br +0 -0
  90. package/dist/web/public/terminal-ui.f3a51974.js.gz +0 -0
Binary file
@@ -0,0 +1,3 @@
1
+ "use strict";(function(e){const t=/^\x1b\[[\?>=]?[\d;]*[cnR]$/,s=/^\x1b\][\d;]*[^\x07\x1b]*(?:\x07|\x1b\\)$/;function a(y){return t.test(y)||s.test(y)}function n(y){return a(y)}const l={og:{background:"#0d0d0d",foreground:"#e0e0e0",cursor:"#e0e0e0",cursorAccent:"#0d0d0d",selection:"rgba(255,255,255,0.3)",black:"#0d0d0d",red:"#ff6b6b",green:"#51cf66",yellow:"#ffd43b",blue:"#339af0",magenta:"#cc5de8",cyan:"#22b8cf",white:"#e0e0e0",brightBlack:"#495057",brightRed:"#ff8787",brightGreen:"#69db7c",brightYellow:"#ffe066",brightBlue:"#5c7cfa",brightMagenta:"#da77f2",brightCyan:"#66d9e8",brightWhite:"#ffffff"},"daylight-green":{background:"#161b23",foreground:"#dfe6ef",cursor:"#2fd3aa",cursorAccent:"#161b23",selection:"rgba(47,211,170,0.22)",black:"#161b23",red:"#ff8585",green:"#34d8a0",yellow:"#f0c25a",blue:"#5cc6e8",magenta:"#c79af2",cyan:"#2bcbbb",white:"#dfe6ef",brightBlack:"#5b6675",brightRed:"#ffa0a0",brightGreen:"#5fe6b8",brightYellow:"#ffd884",brightBlue:"#82d4ee",brightMagenta:"#d6b3f7",brightCyan:"#5ee0d4",brightWhite:"#f3f6fa"},"daylight-blue":{background:"#161b23",foreground:"#dfe6ef",cursor:"#38b6f0",cursorAccent:"#161b23",selection:"rgba(56,182,240,0.22)",black:"#161b23",red:"#ff8585",green:"#34d8a0",yellow:"#f0c25a",blue:"#5cc6e8",magenta:"#c79af2",cyan:"#2bcbbb",white:"#dfe6ef",brightBlack:"#5b6675",brightRed:"#ffa0a0",brightGreen:"#5fe6b8",brightYellow:"#ffd884",brightBlue:"#82d4ee",brightMagenta:"#d6b3f7",brightCyan:"#5ee0d4",brightWhite:"#f3f6fa"},"paper-gray":{background:"#f6f8fa",foreground:"#1f2328",cursor:"#0969da",cursorAccent:"#ffffff",selection:"rgba(9,105,218,0.2)",black:"#24292f",red:"#cf222e",green:"#1a7f37",yellow:"#9a6700",blue:"#0969da",magenta:"#8250df",cyan:"#1b7c83",white:"#59636e",brightBlack:"#6e7781",brightRed:"#a40e26",brightGreen:"#116329",brightYellow:"#7d4e00",brightBlue:"#0550ae",brightMagenta:"#6639ba",brightCyan:"#116b75",brightWhite:"#1f2328"},"solarized-light":{background:"#fdf6e3",foreground:"#586e75",cursor:"#147ba3",cursorAccent:"#fdf6e3",selection:"rgba(38,139,210,0.2)",black:"#eee8d5",red:"#dc322f",green:"#758600",yellow:"#9b7800",blue:"#147ba3",magenta:"#d33682",cyan:"#2a9189",white:"#073642",brightBlack:"#93a1a1",brightRed:"#cb4b16",brightGreen:"#657b83",brightYellow:"#586e75",brightBlue:"#268bd2",brightMagenta:"#6c71c4",brightCyan:"#2aa198",brightWhite:"#002b36"},"catppuccin-latte":{background:"#eff1f5",foreground:"#4c4f69",cursor:"#1e66f5",cursorAccent:"#ffffff",selection:"rgba(30,102,245,0.18)",black:"#5c5f77",red:"#d20f39",green:"#3b8f2b",yellow:"#a86605",blue:"#1e66f5",magenta:"#8839ef",cyan:"#177f86",white:"#6c6f85",brightBlack:"#7c7f93",brightRed:"#b50930",brightGreen:"#2f7622",brightYellow:"#8b5604",brightBlue:"#174fbf",brightMagenta:"#6f2bc5",brightCyan:"#116b71",brightWhite:"#4c4f69"},"rose-pine-dawn":{background:"#faf4ed",foreground:"#575279",cursor:"#286983",cursorAccent:"#fffaf3",selection:"rgba(40,105,131,0.2)",black:"#575279",red:"#b4637a",green:"#286983",yellow:"#96681f",blue:"#477f91",magenta:"#907aa9",cyan:"#3f7f8b",white:"#6e6a86",brightBlack:"#797593",brightRed:"#984d66",brightGreen:"#1f5266",brightYellow:"#7d5417",brightBlue:"#386b7c",brightMagenta:"#765f90",brightCyan:"#326b76",brightWhite:"#575279"}},h=new Set(["paper-gray","solarized-light","catppuccin-latte","rose-pine-dawn"]);function u(){return typeof document<"u"&&document.documentElement.dataset.skin||"daylight-blue"}function c(){const y=u();return l[y]||l["daylight-blue"]}function m(y=u()){return h.has(y)}e.CodemanTerminalInput={isTerminalQueryResponse:a,shouldSuppressTerminalQueryResponse:n,USER_SCROLL_STICKY_SUPPRESS_MS:1500,TOUCH_COMPAT_MOUSE_SUPPRESS_MS:450},e.CODEMAN_XTERM_THEMES=l,e.codemanCurrentXtermTheme=c,e.codemanCurrentSkinIsLight=m})(window),Object.assign(CodemanApp.prototype,{initTerminal(){const e=parseInt(localStorage.getItem("codeman-scrollback")),t=Number.isFinite(e)&&e>0?Math.max(e,DEFAULT_SCROLLBACK):DEFAULT_SCROLLBACK;if(this.terminal=new Terminal({theme:{...window.codemanCurrentXtermTheme()},fontFamily:'"Fira Code", "Cascadia Code", "JetBrains Mono", "SF Mono", Monaco, monospace',fontSize:MobileDetection.getDeviceType()==="mobile"?10:14,lineHeight:1.2,cursorBlink:!1,cursorStyle:"block",minimumContrastRatio:window.codemanCurrentSkinIsLight()?4.5:1,scrollback:t,allowTransparency:!0,allowProposedApi:!0}),this.fitAddon=new FitAddon.FitAddon,this.terminal.loadAddon(this.fitAddon),this._xtermSnapshots=new Map,typeof SerializeAddon<"u")try{this._serializeAddon=new SerializeAddon.SerializeAddon,this.terminal.loadAddon(this._serializeAddon)}catch{this._serializeAddon=null}if(typeof Unicode11Addon<"u")try{const r=new Unicode11Addon.Unicode11Addon;this.terminal.loadAddon(r),this.terminal.unicode.activeVersion="11"}catch{}const s=document.getElementById("terminalContainer");this.terminal.open(s),this._installMobileTapMouseGuard(),this.terminal.attachCustomKeyEventHandler(r=>{if(r.isComposing||r.keyCode===229||r.altKey&&!r.ctrlKey&&!r.shiftKey&&/^(Digit[1-9]|BracketLeft|BracketRight|KeyK)$/.test(r.code||"")||r.type==="keydown"&&this.shouldOpenCommandPaletteFromShortcut?.(r))return!1;if((r.ctrlKey||r.metaKey)&&r.key==="v"&&r.type==="keydown")return this.activeSessionId&&this._handleImagePaste&&this._handleImagePaste(),!1;if(r.key==="Enter"&&(r.shiftKey||r.ctrlKey)&&r.type==="keydown"){if(this.activeSessionId)if(this._localEchoEnabled){const _=this._localEchoOverlay?.pendingText||"";this._localEchoOverlay?.clear(),this._localEchoOverlay?.suppressBufferDetection(),this._flushedOffsets?.delete(this.activeSessionId),this._flushedTexts?.delete(this.activeSessionId),_&&(this._pendingInput+=_,w()),setTimeout(()=>{fetch(`/api/sessions/${this.activeSessionId}/send-key`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:r.ctrlKey?"C-Enter":"S-Enter"})})},_?80:0)}else fetch(`/api/sessions/${this.activeSessionId}/send-key`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:r.ctrlKey?"C-Enter":"S-Enter"})});return!1}return!0});{const r=s.querySelector(".xterm-helper-textarea");if(r&&MobileDetection.isTouchDevice()){let _=!1,d=0;r.addEventListener("compositionstart",()=>{_=!0}),r.addEventListener("compositionend",()=>{_=!1}),r.addEventListener("keydown",g=>{!g.isComposing&&g.keyCode!==229&&(d=Date.now())}),r.addEventListener("input",g=>{if(_||g.isComposing||g.inputType!=="insertText"||!g.data||Date.now()-d<50)return;const b=g.data,T=this._localEchoOverlay?.pendingText||"";Promise.resolve().then(()=>{if(this._lastTerminalData?.data===b&&performance.now()-this._lastTerminalData.time<100){r.value="";return}const v=this._localEchoOverlay?.pendingText||"";if(this._localEchoEnabled&&v.length>T.length&&v.endsWith(b)){r.value="";return}const f=r.value;!f||f.trim()===""&&b!==" "||(this.terminal._core.coreService.triggerDataEvent(b,!0),r.value="")})})}}this._webglAddon=null;const i=new URLSearchParams(location.search),o=(()=>{try{const r=localStorage.getItem("codeman-webgl-disabled");if(!r)return!1;const{at:_}=JSON.parse(r);return Date.now()-_>WEBGL_FALLBACK.STICKY_EXPIRY_MS?(localStorage.removeItem("codeman-webgl-disabled"),!1):!0}catch{return!1}})(),a=this.loadAppSettingsFromStorage(),n=this.getDefaultSettings(),l=a.webglRendererEnabled??n.webglRendererEnabled,{skip:h,clearSticky:u}=shouldSkipWebGL({deviceType:MobileDetection.getDeviceType(),noWebglParam:i.has("nowebgl"),forceParam:i.get("webgl")==="force",stickyDisabled:o,userPrefEnabled:l});if(u)try{localStorage.removeItem("codeman-webgl-disabled")}catch{}if(h&&o&&console.log("[CRASH-DIAG] WebGL sticky-disabled from prior stalls \u2014 DOM renderer in use. Re-enable: ?webgl=force"),!h)if(typeof WebglAddon<"u")this._initWebGL();else{const r=document.createElement("script");r.src="vendor/xterm-addon-webgl.min.js",r.onload=()=>this._initWebGL(),r.onerror=()=>console.warn("[CRASH-DIAG] Failed to load WebGL addon \u2014 using canvas renderer"),document.head.appendChild(r)}this._localEchoOverlay=new LocalEchoOverlay(this.terminal),MobileDetection.isTouchDevice()&&(this.terminal.onCursorMove(()=>this._syncMobileHelperTextareaToCursor()),this.terminal.onRender(()=>this._syncMobileHelperTextareaToCursor())),this._cjkInput=null,typeof CjkInput<"u"&&(this._cjkInput=CjkInput.init({send:r=>{this._handleCjkInput(r)}}));const c=this.terminal.focus.bind(this.terminal);this.terminal.focus=()=>{const r=document.getElementById("cjkInput");r?.classList.contains("cjk-input-visible")?r.focus():c()},MobileDetection.getDeviceType()==="mobile"&&document.body.classList.contains("safari-browser")?requestAnimationFrame(()=>{this.fitAddon.fit(),requestAnimationFrame(()=>this.fitAddon.fit())}):this.fitAddon.fit(),this.registerFilePathLinkProvider(),s.addEventListener("wheel",r=>{r.preventDefault();const _=this._wheelScrollLines(r);if(this._shouldForwardWheelToApp(r)){this._sendSyntheticSgrWheel(r.clientX,r.clientY,_);return}this._noteTerminalUserScroll(_),this.terminal.scrollLines(_)},{passive:!1});{const r=()=>this.terminal._core?._renderService?.dimensions?.css?.cell?.height||13;let _=0,d=0,g=0,b=null,T=!1;const v=I=>{const k=g?(I-g)/16.67:1;if(g=I,!T&&Math.abs(d)>.3){const A=Math.round(d/r());A!==0&&this.terminal.scrollLines(A),d*=.92,b=requestAnimationFrame(v)}else T?b=requestAnimationFrame(v):(b=null,d=0)};let f=0,S=!1,C=0;const M=8;s.addEventListener("touchstart",I=>{I.touches.length===1&&(_=I.touches[0].clientY,C=_,d=0,f=0,T=!0,S=!1,g=0,b&&(cancelAnimationFrame(b),b=null))},{passive:!0}),s.addEventListener("touchmove",I=>{if(I.touches.length===1&&T){const k=I.touches[0].clientY;if(!S&&Math.abs(k-C)>=M&&(S=!0),!S)return;I.preventDefault();const A=_-k;f+=A,d=A*1.2,_=k;const p=r(),L=Math.trunc(f/p);L!==0&&(this._noteTerminalUserScroll(L),this.terminal.scrollLines(L),f-=L*p)}},{passive:!1}),s.addEventListener("touchend",I=>{if(T=!1,!b&&Math.abs(d)>.3&&(b=requestAnimationFrame(v)),!S&&this.terminal){const k=I.changedTouches&&I.changedTouches[0],A=this.terminal.modes?.mouseTrackingMode,p=!!A&&A!=="none";k&&this._suppressTrustedTapMouseEvents(),k&&p?this._dispatchSyntheticTerminalClick(k.clientX,k.clientY):k&&this._sessionUsesServerMouseStrip()&&this._sendSyntheticSgrTap(k.clientX,k.clientY),this._syncMobileHelperTextareaToCursor();const L=document.getElementById("cjkInput");L?.classList.contains("cjk-input-visible")?L.focus():this.terminal.focus()}},{passive:!0}),s.addEventListener("touchcancel",()=>{T=!1,d=0,f=0},{passive:!0})}s.addEventListener("click",r=>this._handleDesktopTerminalClick(r)),this.showWelcome(),this.initImageInput(),this._chunkedWriteGen=0,this._bufferLoadSeq=0,this._bufferLoadOwner=null,this._lastUserScrollUpAt=null,this._resizeTimeout=null,this._lastResizeDims=null;const y=40,E=10,x=()=>{this._resizeTimeout&&clearTimeout(this._resizeTimeout),this._resizeTimeout=setTimeout(()=>{this._resizeTimeout=null,this.fitAddon&&this.fitAddon.fit(),this.flickerFilterBuffer&&(this.flickerFilterTimeout&&(clearTimeout(this.flickerFilterTimeout),this.flickerFilterTimeout=null),this.flushFlickerBuffer());const r=typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible;if(this.activeSessionId&&!r){const _=this.fitAddon.proposeDimensions(),d=_?Math.max(_.cols,y):y,g=_?Math.max(_.rows,E):E;if(!this._lastResizeDims||d!==this._lastResizeDims.cols||g!==this._lastResizeDims.rows){const b=this.activeSessionId?this.sessions.get(this.activeSessionId):null;b&&b.mode!=="shell"&&this.terminal&&this.isTerminalAtBottom()&&this.terminal.write("\x1B[3J\x1B[H\x1B[2J"),this._lastResizeDims={cols:d,rows:g};const T=typeof MobileDetection<"u"&&MobileDetection.getDeviceType?MobileDetection.getDeviceType():"desktop";let v=!1;if(this._wsReady&&this._wsSessionId===this.activeSessionId)try{this._ws.send(JSON.stringify({t:"z",c:d,r:g,v:T})),v=!0}catch{}v||fetch(`/api/sessions/${this.activeSessionId}/resize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cols:d,rows:g,viewportType:T})}).catch(()=>{})}}this.updateConnectionLines(),this._localEchoOverlay?.hasPending&&this._localEchoOverlay.rerender()},300)};window.addEventListener("resize",x),this.terminalResizeObserver&&this.terminalResizeObserver.disconnect(),this.terminalResizeObserver=new ResizeObserver(x),this.terminalResizeObserver.observe(s),this._pendingInput="",this._inputFlushTimeout=null,this._lastKeystrokeTime=0;const w=()=>{if(this._inputFlushTimeout=null,this._pendingInput&&this.activeSessionId){const r=this._pendingInput,_=this.activeSessionId;this._pendingInput="",this._sendInputAsync(_,r)}};this.terminal.onData(r=>{if(!/^\x1b\[<\d+;\d+;\d+[Mm]$/.test(r)&&(window.cjkActive||document.activeElement?.id==="cjkInput")){const d=document.getElementById("cjkInput");d?.classList.contains("cjk-input-visible")&&document.activeElement===this.terminal.textarea&&!window.CodemanTerminalInput?.shouldSuppressTerminalQueryResponse(r)&&(_crashDiag.log("CJK regain-focus (onData swallowed input)"),d.focus());return}if(this.activeSessionId){if(window.CodemanTerminalInput?.shouldSuppressTerminalQueryResponse(r))return;if(this._lastTerminalData={data:r,time:performance.now()},this._localEchoEnabled){if(r==="\x7F"){if(this._localEchoOverlay?.removeChar()==="flushed"){const{count:b,text:T}=this._localEchoOverlay.getFlushed();this._flushedOffsets?.has(this.activeSessionId)&&(b===0?(this._flushedOffsets.delete(this.activeSessionId),this._flushedTexts?.delete(this.activeSessionId)):(this._flushedOffsets.set(this.activeSessionId,b),this._flushedTexts?.set(this.activeSessionId,T))),this._pendingInput+=r,w()}return}if(/^[\r\n]+$/.test(r)){const g=this._localEchoOverlay?.pendingText||"";this._localEchoOverlay?.clear(),this._localEchoOverlay?.suppressBufferDetection(),this._flushedOffsets?.delete(this.activeSessionId),this._flushedTexts?.delete(this.activeSessionId),this._inputFlushTimeout&&(clearTimeout(this._inputFlushTimeout),this._inputFlushTimeout=null),g&&(this._pendingInput+=g,w()),setTimeout(()=>{this._pendingInput+="\r",w()},80);return}if(r.length>1&&r.charCodeAt(0)>=32){this._localEchoOverlay?.appendText(r);return}if(r.charCodeAt(0)<32){if(r.length>1&&r.charCodeAt(0)===27){this._pendingInput+=r,w();return}if(this._restoringFlushedState){this._pendingInput+=r,w();return}if(r===" "){const b=this._localEchoOverlay?.pendingText||"";this._localEchoOverlay?.clear(),this._flushedOffsets?.delete(this.activeSessionId),this._flushedTexts?.delete(this.activeSessionId),b&&(this._pendingInput+=b),this._pendingInput+=r,this._inputFlushTimeout&&(clearTimeout(this._inputFlushTimeout),this._inputFlushTimeout=null);let T="";try{const f=this._localEchoOverlay?.findPrompt?.();if(f){const S=this.terminal.buffer.active,C=S.getLine(S.viewportY+f.row);C&&(T=C.translateToString(!0).slice(f.col+2).trimEnd())}}catch{}this._tabCompletionBaseText=T,w(),this._tabCompletionSessionId=this.activeSessionId,this._tabCompletionRetries=0,this._tabCompletionFallback&&clearTimeout(this._tabCompletionFallback);const v=this;this._tabCompletionFallback=setTimeout(()=>{if(v._tabCompletionFallback=null,!v._tabCompletionSessionId||v._tabCompletionSessionId!==v.activeSessionId)return;const f=v._localEchoOverlay;!f||f.pendingText||v.terminal.write("",()=>{if(!v._tabCompletionSessionId)return;f.resetBufferDetection();const S=f.detectBufferText();S&&S!==v._tabCompletionBaseText&&(v._tabCompletionSessionId=null,v._tabCompletionRetries=0,v._tabCompletionBaseText=null,f.rerender())})},300);return}const g=this._localEchoOverlay?.pendingText||"";this._localEchoOverlay?.clear(),this._localEchoOverlay?.suppressBufferDetection(),this._flushedOffsets?.delete(this.activeSessionId),this._flushedTexts?.delete(this.activeSessionId),g&&(this._pendingInput+=g),this._pendingInput+=r,this._inputFlushTimeout&&(clearTimeout(this._inputFlushTimeout),this._inputFlushTimeout=null),w();return}if(r.length===1&&r.charCodeAt(0)>=32){this._localEchoOverlay?.addChar(r);return}}if(this._pendingInput+=r,r.charCodeAt(0)<32||r.length>1){this._inputFlushTimeout&&(clearTimeout(this._inputFlushTimeout),this._inputFlushTimeout=null),w();return}const d=performance.now();d-this._lastKeystrokeTime>50?(this._inputFlushTimeout&&(clearTimeout(this._inputFlushTimeout),this._inputFlushTimeout=null),this._lastKeystrokeTime=d,w()):(this._lastKeystrokeTime=d,this._inputFlushTimeout||(this._inputFlushTimeout=setTimeout(w,0)))}})},registerFilePathLinkProvider(){const e=this;let t=-1;this.terminal.registerLinkProvider({provideLinks(s,i){s!==t&&(t=s,console.debug("[LinkProvider] Checking line:",s));const o=e.terminal.buffer.active;if(!o.getLine(s-1)){i(void 0);return}const n=e.terminal.cols,l=S=>o.getLine(S-1),h=S=>{if(S<=1)return!1;if(l(S)?.isWrapped)return!0;const C=l(S-1);return!!C&&C.translateToString(!0).length>=n},u=12;let c=s;for(;c>1&&s-c<u&&h(c);)c--;let m=s;for(;m<o.length&&m-c<u&&h(m+1);)m++;const y=[];for(let S=c;S<=m;S++){const C=l(S);if(!C)break;y.push(C.translateToString(S===m))}const E=y.join(""),x=S=>{let C=S;for(let M=0;M<y.length-1;M++){if(C<y[M].length)return{x:C+1,y:c+M};C-=y[M].length}return{x:C+1,y:c+y.length-1}};if(!E||!E.includes("/")){i(void 0);return}const w=[],r=/https?:\/\/(?:[^\s"'<>|;&)\]\x00-\x1f]|&(?!&))+/g,_=(S,C)=>{const M=S.replace(/[.,;:!?)&]+$/,""),I=E.indexOf(M,C);if(I===-1)return;const k=x(I),A=x(I+M.length);w.some(p=>p.range.start.x===k.x&&p.range.start.y===k.y)||w.push({text:M,range:{start:k,end:A},decorations:{pointerCursor:!0,underline:!0},activate(p,L){window.open(L,"_blank","noopener,noreferrer")},hover(){e._linkHovered=!0},leave(){e._linkHovered=!1}})},d=/\b(tail|cat|head|less|grep|watch|vim|nano)\s+(?:[^\s\/]+\s+){0,4}(\/[^\s"'<>|;&\n\x00-\x1f]+)/g,g=/(\/(?:home|tmp|var|etc|opt)[^\s"'<>|;&\n\x00-\x1f]*\.(?:log|txt|json|md|yaml|yml|csv|xml|sh|py|ts|js|png|jpe?g|gif|webp|bmp|svg|pdf))\b/g,b=/Bash\([^)]*?(\/(?:home|tmp|var|etc|opt)[^\s"'<>|;&\)\n\x00-\x1f]+)/g,T=new Set(["png","jpg","jpeg","gif","webp","bmp","svg","pdf"]),v=(S,C)=>{const M=E.indexOf(S,C);if(M===-1)return;const I=x(M),k=x(M+S.length);w.some(A=>A.range.start.x===I.x&&A.range.start.y===I.y)||w.push({text:S,range:{start:I,end:k},decorations:{pointerCursor:!0,underline:!0},activate(A,p){const L=(p.split(".").pop()||"").toLowerCase();if(T.has(L)){e.openFilePreview(p,e.activeSessionId);return}e.openLogViewerWindow(p,e.activeSessionId)},hover(){e._linkHovered=!0},leave(){e._linkHovered=!1}})};let f;for(r.lastIndex=0;(f=r.exec(E))!==null;)_(f[0],f.index);for(d.lastIndex=0;(f=d.exec(E))!==null;)v(f[2],f.index);for(g.lastIndex=0;(f=g.exec(E))!==null;)v(f[1],f.index);for(b.lastIndex=0;(f=b.exec(E))!==null;)v(f[1],f.index);w.length>0&&console.debug("[LinkProvider] Found links:",w.map(S=>S.text)),i(w.length>0?w:void 0)}}),console.log("[LinkProvider] File path link provider registered")},showWelcome(){if(this.shouldUseMobileOverview?.()){const t=document.getElementById("welcomeOverlay");t&&t.classList.remove("visible"),this.showMobileOverview(),this._updateCjkInputState?.();return}this.hideMobileOverview?.();const e=document.getElementById("welcomeOverlay");e&&(e.classList.add("visible"),this.loadTunnelStatus(),this.loadHistorySessions(),this.initSearchPanel()),this._updateCjkInputState?.()},hideWelcome(){this.hideMobileOverview?.();const e=document.getElementById("welcomeOverlay");e&&e.classList.remove("visible");const t=document.getElementById("welcomeQr");t&&(clearTimeout(this._welcomeQrShrinkTimer),t.classList.remove("expanded")),this._updateCjkInputState?.()},async _fetchHistorySessions(){const s=(await(await fetch("/api/history/sessions")).json()).data?.sessions||[];if(s.length===0)return[];const i=new Map;for(const a of s){const n=a.projectKey||a.workingDir;i.has(n)||i.set(n,[]),i.get(n).push(a)}const o=[];for(const[,a]of i)o.push(...a.slice(0,3));return o.sort((a,n)=>new Date(n.lastModified)-new Date(a.lastModified)),o},async _fetchUnifiedSessions(e=60){const t=await fetch("/api/sessions/unified?limit="+e),s=await t.json().catch(()=>null);if(!t.ok||!s||s.success===!1||!s.data)throw new Error(s?.error||`unified sessions request failed (HTTP ${t.status})`);return s.data.sessions||[]},_resolveCaseLabel(e,t){if(!e)return"";let s=null;for(const i of t||[])if(!(!i||!i.path)){if(e===i.path)return`#${i.name}`;if(e.startsWith(i.path+"/")){const o=i.path.length;(!s||o>s.len)&&(s={name:i.name,suffix:e.slice(o),len:o})}}return s?`#${s.name}${s.suffix}`:e.split("/").pop()||e},_shortenHomePath(e){return(e||"").replace(/^\/home\/[^/]+\//,"~/").replace(/^\/Users\/[^/]+\//,"~/")},_buildHistoryItem(e,t,s){const i=s?.showViewAll!==!1,o=typeof e.sizeBytes=="number",a=o?e.sizeBytes<1024?`${e.sizeBytes}B`:e.sizeBytes<1048576?`${(e.sizeBytes/1024).toFixed(0)}K`:`${(e.sizeBytes/1048576).toFixed(1)}M`:"",n=typeof e.lastActivityAt=="number"?e.lastActivityAt:e.lastModified?Date.parse(e.lastModified):NaN;let l="";if(!Number.isNaN(n)){const p=new Date(n);l=p.toLocaleDateString("en",{month:"short",day:"numeric"})+" "+p.toLocaleTimeString("en",{hour:"2-digit",minute:"2-digit",hour12:!1})}const h=this._shortenHomePath(e.workingDir),u=this._resolveCaseLabel(e.workingDir,t),c=Array.isArray(e.sources)&&e.sources.includes("live"),m=e.pinned===!0,y=document.createElement("div");y.className="history-item"+(m?" is-pinned":""),y.title=e.workingDir||"";const E=document.createElement("div");E.className="history-item-main",E.addEventListener("click",s?.onActivate||(()=>{c&&this.sessions.has(e.sessionId)?this.selectSession(e.sessionId):this.resumeHistorySession(e.claudeSessionId||e.sessionId,e.workingDir||"",e.name)}));const x=document.createElement("div");x.className="history-item-text";const w=document.createElement("span");if(w.className="history-item-title",m){const p=document.createElement("span");p.className="history-item-pin",p.textContent="\u{1F4CC}",p.setAttribute("aria-label","Pinned"),p.title="Pinned",w.appendChild(p)}w.appendChild(document.createTextNode(e.name||e.firstPrompt||h));const r=document.createElement("div");if(r.className="history-item-badges",e.mode){const p=document.createElement("span");p.className="history-item-badge history-item-badge-mode",p.textContent=e.mode,r.appendChild(p)}if(c){const p=document.createElement("span");p.className="history-item-badge history-item-badge-live",p.textContent="LIVE",r.appendChild(p)}const _=document.createElement("span");_.className="history-item-subtitle",u.startsWith("#")&&_.classList.add("is-case"),_.textContent=u,x.append(w),r.childElementCount>0&&x.append(r),x.append(_);const d=document.createElement("span");d.className="history-item-meta",d.textContent=l;const g=document.createElement("button");g.className="history-item-expand",g.type="button",g.setAttribute("aria-haspopup","menu"),g.setAttribute("aria-label","Session actions"),g.setAttribute("aria-expanded","false"),g.textContent="\u22EF",E.append(x,d,g);const b=document.createElement("div");b.className="history-item-detail",b.hidden=!0;const T=document.createElement("div");T.className="history-detail-row";const v=document.createElement("span");v.className="history-detail-label",v.textContent="Prompt";const f=document.createElement("span");f.className="history-detail-value history-detail-prompt",f.textContent=e.firstPrompt||"(no prompt captured)",T.append(v,f);let S=null;if(e.lastPrompt&&e.lastPrompt!==e.firstPrompt){S=document.createElement("div"),S.className="history-detail-row";const p=document.createElement("span");p.className="history-detail-label",p.textContent="Last prompt";const L=document.createElement("span");L.className="history-detail-value history-detail-prompt",L.textContent=e.lastPrompt,S.append(p,L)}const C=document.createElement("div");C.className="history-detail-row";const M=document.createElement("span");M.className="history-detail-label",M.textContent="Path";const I=document.createElement("span");I.className="history-detail-value history-detail-path",I.textContent=h,C.append(M,I);const k=document.createElement("div");k.className="history-detail-row history-detail-meta";const A=[];if(l&&A.push(l),o&&A.push(a),A.push(e.sessionId.slice(0,8)),k.textContent=A.join(" \xB7 "),b.append(T),S&&b.append(S),b.append(C,k),i&&e.projectKey){const p=document.createElement("div");p.className="history-detail-row history-detail-actions";const L=document.createElement("button");L.type="button",L.className="history-view-all-btn",L.textContent="View all in this folder",L.addEventListener("click",R=>{R.stopPropagation(),this.openFolderHistoryModal(e.projectKey,e.workingDir,t)}),p.appendChild(L),b.appendChild(p)}return g.addEventListener("click",p=>{p.stopPropagation(),p.preventDefault(),this._openSessionRowMenu(p.currentTarget,e,t,y,b)}),y.append(E,b),y},_openSessionRowMenu(e,t,s,i,o){if(this._openRowMenuClose)try{this._openRowMenuClose()}catch{}const a=Array.isArray(t.sources)&&t.sources.includes("live")&&this.sessions.has(t.sessionId),n=document.createElement("div");n.className="session-row-menu",n.setAttribute("role","menu");let l=null,h=null,u=null;const c=()=>{document.removeEventListener("mousedown",l,!0),document.removeEventListener("keydown",h,!0),window.removeEventListener("scroll",u,!0),window.removeEventListener("resize",u,!0);try{n.remove()}catch{}this._openRowMenuEl===n&&(this._openRowMenuEl=null,this._openRowMenuClose=null)},m=(d,g,b)=>{const T=document.createElement("button");T.type="button",T.className="session-row-menu-item",T.setAttribute("role","menuitem");const v=document.createElement("span");if(v.className="session-row-menu-label",v.textContent=d,T.appendChild(v),b&&b.sublabel){const f=document.createElement("span");f.className="session-row-menu-sublabel",f.textContent=b.sublabel,T.appendChild(f)}T.addEventListener("click",async f=>{f.stopPropagation(),f.preventDefault(),await g()}),n.appendChild(T)};m(a?"Switch to session":"Resume session",()=>{a?this.selectSession(t.sessionId):this.resumeHistorySession(t.claudeSessionId||t.sessionId,t.workingDir||"",t.name),this.closeSessionManager?.(),c()});const y=t.pinned===!0;m(y?"Unpin session":"Pin to top",async()=>{await this._setSessionPinned(t.sessionId,!y)?(t.pinned=!y,this.showToast(y?"Unpinned":"Pinned to top","success")):this.showToast("Pin failed","error"),c()}),a&&m("Open folder",()=>{this.selectSession(t.sessionId),this.loadFileBrowser?.(t.sessionId),this.closeSessionManager?.(),c()}),t.workingDir&&m("Copy path",async()=>{const d=await this._copyText(t.workingDir);this.showToast(d?"Path copied":"Copy failed",d?"success":"error"),c()}),m("Show details",()=>{const d=i.classList.toggle("expanded");o.hidden=!d,e.setAttribute("aria-expanded",d?"true":"false"),c()}),document.body.appendChild(n);const E=e.getBoundingClientRect(),x=n.getBoundingClientRect(),w=4;let r=E.bottom+w;r+x.height>window.innerHeight&&E.top-w-x.height>=0&&(r=E.top-w-x.height);let _=E.right-x.width;_<w&&(_=w),_+x.width>window.innerWidth-w&&(_=Math.max(w,window.innerWidth-w-x.width)),n.style.top=`${Math.max(w,r)}px`,n.style.left=`${_}px`,l=d=>{n.contains(d.target)||e.contains(d.target)||c()},h=d=>{d.key==="Escape"&&(d.stopPropagation(),c())},u=()=>c(),document.addEventListener("mousedown",l,!0),document.addEventListener("keydown",h,!0),window.addEventListener("scroll",u,!0),window.addEventListener("resize",u,!0),this._openRowMenuEl=n,this._openRowMenuClose=c},async _setSessionPinned(e,t){try{const s=await fetch(`/api/sessions/${encodeURIComponent(e)}/pin`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"same-origin",body:JSON.stringify({pinned:t})});return s.ok?(await s.json())?.success===!0:!1}catch(s){return console.error("[_setSessionPinned]",s),!1}},_HISTORY_INITIAL_COUNT:4,async loadHistorySessions(){const e=document.getElementById("historySessions"),t=document.getElementById("historyList");if(!(!e||!t))try{const s=Array.isArray(this.cases)&&this.cases.length>0?Promise.resolve(this.cases):fetch("/api/cases").then(n=>n.ok?n.json():null).then(n=>n?.data||[]).catch(()=>[]),[i,o]=await Promise.all([this._fetchUnifiedSessions(60),s]);if(i.length===0){e.style.display="none";return}t.replaceChildren();const a=this._HISTORY_INITIAL_COUNT;for(let n=0;n<Math.min(a,i.length);n++)t.appendChild(this._buildHistoryItem(i[n],o));if(i.length>a){const n=document.createElement("button");n.className="history-show-more",n.textContent=`Show ${i.length-a} more`,n.addEventListener("click",()=>{for(let l=a;l<i.length;l++)t.insertBefore(this._buildHistoryItem(i[l],o),n);n.remove()}),t.appendChild(n)}e.style.display=""}catch(s){console.error("[loadHistorySessions]",s),e.style.display="none"}},_FOLDER_HISTORY_PAGE_SIZE:20,openFolderHistoryModal(e,t,s){this._closeFolderHistoryModal();const i=document.createElement("div");i.className="modal active folder-history-modal",i.id="folderHistoryModal";const o=document.createElement("div");o.className="modal-backdrop",o.addEventListener("click",()=>this._closeFolderHistoryModal());const a=document.createElement("div");a.className="modal-content modal-lg";const n=document.createElement("div");n.className="modal-header";const l=document.createElement("h3");l.textContent="Folder History";const h=document.createElement("div");h.className="folder-history-subtitle",h.textContent=this._shortenHomePath(t);const u=document.createElement("button");u.className="modal-close",u.setAttribute("aria-label","Close"),u.innerHTML="&times;",u.addEventListener("click",()=>this._closeFolderHistoryModal()),n.append(l,u);const c=document.createElement("div");c.className="modal-body";const m=document.createElement("div");m.className="folder-history-list",m.setAttribute("data-loading","true"),m.textContent="Loading...",c.append(h,m),a.append(n,c),i.append(o,a),document.body.appendChild(i),this._folderHistoryState={projectKey:e,workingDir:t,cases:s||[],offset:0,total:null,list:m},this._folderHistoryEscHandler=y=>{y.key==="Escape"&&this._closeFolderHistoryModal()},document.addEventListener("keydown",this._folderHistoryEscHandler),this._loadFolderHistoryPage()},async _loadFolderHistoryPage(){const e=this._folderHistoryState;if(!e)return;const{projectKey:t,cases:s,list:i}=e,o=this._FOLDER_HISTORY_PAGE_SIZE,a=e.offset,n=i.querySelector(".folder-history-more");n&&n.remove(),a===0&&(i.replaceChildren(),i.removeAttribute("data-loading"));try{const l=`/api/history/sessions?projectKey=${encodeURIComponent(t)}&offset=${a}&limit=${o}`,u=await(await fetch(l)).json(),c=u.data?.sessions||[];if(e.total=typeof u.data?.total=="number"?u.data.total:c.length+a,a===0&&c.length===0){const m=document.createElement("div");m.className="folder-history-empty",m.textContent="No conversations found in this folder.",i.appendChild(m);return}for(const m of c)i.appendChild(this._buildHistoryItem(m,s,{showViewAll:!1}));if(e.offset=a+c.length,e.offset<e.total){const m=e.total-e.offset,y=document.createElement("button");y.className="history-show-more folder-history-more",y.textContent=`Show ${Math.min(o,m)} more (${m} remaining)`,y.addEventListener("click",()=>this._loadFolderHistoryPage()),i.appendChild(y)}}catch(l){console.error("[loadFolderHistoryPage]",l);const h=document.createElement("div");h.className="folder-history-empty",h.textContent="Failed to load folder history.",i.appendChild(h)}},_closeFolderHistoryModal(){const e=document.getElementById("folderHistoryModal");e&&e.remove(),this._folderHistoryEscHandler&&(document.removeEventListener("keydown",this._folderHistoryEscHandler),this._folderHistoryEscHandler=null),this._folderHistoryState=null},_resolveResumeName(e,t){if(typeof e=="string"&&e.trim())return e;const s=(t||"").split("/").pop()||"session";let i=1;for(const[,o]of this.sessions){const a=o.name&&o.name.match(/^w(\d+)-/);if(a){const n=parseInt(a[1]);n>=i&&(i=n+1)}}return`w${i}-${s}`},async resumeHistorySession(e,t,s){document.getElementById("runModeMenu")?.classList.remove("active"),this._closeFolderHistoryModal();try{this.terminal.clear(),this.terminal.writeln(`\x1B[1;32m Resuming conversation ${e.slice(0,8)}...\x1B[0m`);const i=this._resolveResumeName(s,t),a=(this.cases||[]).find(y=>y.path===t)?.name||t.split("/").pop()||"",n=this.loadAppSettingsFromStorage(),l=this.buildEnvOverrides(this.getCaseSettings(a),n),h=this.getEffortSetting(n),c=await(await fetch("/api/sessions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({workingDir:t,name:i,resumeSessionId:e,...Object.keys(l).length>0?{envOverrides:l}:{},...h?{effort:h}:{}})})).json();if(!c.success)throw new Error(c.error);const m=c.data.session.id;await fetch(`/api/sessions/${m}/interactive`,{method:"POST"}),this.terminal.writeln(`\x1B[90m Session ${i} ready\x1B[0m`),await this.selectSession(m),this.terminal.focus()}catch(i){this.terminal.writeln(`\x1B[1;31m Error: ${i.message}\x1B[0m`)}},isTerminalAtBottom(){if(!this.terminal)return!0;const e=this.terminal.buffer.active;return e.viewportY>=e.baseY-2},_noteTerminalUserScroll(e){e<0?this._lastUserScrollUpAt=performance.now():this.isTerminalAtBottom()&&(this._lastUserScrollUpAt=null)},_hasRecentUserScrollUp(){return typeof this._lastUserScrollUpAt!="number"?!1:performance.now()-this._lastUserScrollUpAt<window.CodemanTerminalInput.USER_SCROLL_STICKY_SUPPRESS_MS},batchTerminalWrite(e){if(this._isLoadingBuffer){this._loadBufferQueue&&this._loadBufferQueue.push(e);return}if(this.writeFrameScheduled||(this._wasAtBottomBeforeWrite=this.isTerminalAtBottom()),(this.activeSessionId?this.sessions.get(this.activeSessionId):null)?.flickerFilterEnabled??!1){if(e.includes("\x1B[2J")||e.includes("\x1B[H\x1B[J")||e.includes("\x1B[H")&&e.includes("\x1B[?25l")){this.flickerFilterActive=!0,this.flickerFilterBuffer+=e,this.flickerFilterTimeout&&clearTimeout(this.flickerFilterTimeout),this.flickerFilterTimeout=setTimeout(()=>{this.flickerFilterTimeout=null,this.flushFlickerBuffer()},SYNC_WAIT_TIMEOUT_MS);return}if(this.flickerFilterActive){this.flickerFilterBuffer+=e;return}}this.pendingWrites.push(e),this.writeFrameScheduled||(this.writeFrameScheduled=!0,this._safeYield(()=>{this.flushPendingWrites(),this.writeFrameScheduled=!1}))},flushFlickerBuffer(){this.flickerFilterBuffer&&(this.pendingWrites.push(this.flickerFilterBuffer),this.flickerFilterBuffer="",this.flickerFilterActive=!1,this.writeFrameScheduled||(this.writeFrameScheduled=!0,this._safeYield(()=>{this.flushPendingWrites(),this.writeFrameScheduled=!1})))},_updateLocalEchoState(){const e=this.loadAppSettingsFromStorage(),t=this.activeSessionId?this.sessions.get(this.activeSessionId):null,i=!!((e.localEchoEnabled??MobileDetection.isTouchDevice())&&t);this._localEchoEnabled&&!i&&this._localEchoOverlay?.clear(),this._localEchoEnabled=i,this._localEchoOverlay&&t&&(t.mode==="opencode"?this._localEchoOverlay.setPrompt({type:"custom",offset:3,find:o=>{try{const a=o.buffer.active,n=a.cursorY,l=a.getLine(a.viewportY+n);if(!l)return null;const u=l.translateToString(!0).indexOf("\u2503");return u>=0?{row:n,col:u}:null}catch{return null}}}):t.mode==="shell"?(this._localEchoOverlay.clear(),this._localEchoEnabled=!1):this._localEchoOverlay.setPrompt({type:"custom",offset:0,find:o=>{try{const a=o.buffer.active;for(let n=o.rows-1;n>=0;n--){const l=a.getLine(a.viewportY+n);if(!l)continue;const u=l.translateToString(!0).lastIndexOf("\u276F");if(u>=0)return{row:n,col:u+2}}return{row:Math.max(0,Math.min(o.rows-1,a.cursorY)),col:Math.max(0,Math.min(o.cols-1,a.cursorX))}}catch{return null}}}))},_handleCjkInput(e){if(!this.activeSessionId){_crashDiag.log(`CJK send DROP no-session len=${e.length}`);return}_crashDiag.log(`CJK send\u2192${this.activeSessionId.slice(0,8)} len=${e.length}`),this._sendInputAsync(this.activeSessionId,e)},flushPendingWrites(){if(this.pendingWrites.length===0||!this.terminal)return;const e=performance.now(),t=this.pendingWrites.join("");this.pendingWrites=[];const s=t.length;s>16384&&_crashDiag.log(`FLUSH: ${(s/1024).toFixed(0)}KB`);const o=(this.activeSessionId&&this.sessions?this.sessions.get(this.activeSessionId):null)?.mode==="codex"?32768:65536;let a=!1;const n=this._hasRecentUserScrollUp()&&this.terminal.buffer?.active?this.terminal.buffer.active.viewportY:null;s<=o?this.terminal.write(t):(this.terminal.write(t.slice(0,o)),this.pendingWrites.push(t.slice(o)),a=!0,this.writeFrameScheduled||(this.writeFrameScheduled=!0,this._safeYield(()=>{this.flushPendingWrites(),this.writeFrameScheduled=!1}))),n!==null&&this.terminal.buffer?.active?.viewportY!==n&&typeof this.terminal.scrollToLine=="function"&&this.terminal.scrollToLine(n);const l=a?o:s,h=performance.now()-e;if((h>100||a)&&console.warn(`[CRASH-DIAG] flushPendingWrites: ${h.toFixed(0)}ms, ${(l/1024).toFixed(0)}KB written${a?", rest deferred":""} (total ${(s/1024).toFixed(0)}KB)`),this._wasAtBottomBeforeWrite&&!this._hasRecentUserScrollUp()&&this.terminal.scrollToBottom(),this._localEchoOverlay?.hasPending&&this._localEchoOverlay.rerender(),this._tabCompletionSessionId&&this._tabCompletionSessionId===this.activeSessionId&&this._localEchoOverlay&&!this._localEchoOverlay.pendingText){const u=this._localEchoOverlay,c=this;this.terminal.write("",()=>{if(!c._tabCompletionSessionId)return;u.resetBufferDetection();const m=u.detectBufferText();m?m===c._tabCompletionBaseText?(u.undoDetection(),c._tabCompletionRetries=(c._tabCompletionRetries||0)+1,c._tabCompletionRetries>60&&(c._tabCompletionSessionId=null,c._tabCompletionRetries=0)):(c._tabCompletionSessionId=null,c._tabCompletionRetries=0,c._tabCompletionBaseText=null,c._tabCompletionFallback&&(clearTimeout(c._tabCompletionFallback),c._tabCompletionFallback=null),u.rerender()):(c._tabCompletionRetries=(c._tabCompletionRetries||0)+1,c._tabCompletionRetries>60&&(c._tabCompletionSessionId=null,c._tabCompletionRetries=0))})}},_safeYield(e){let t=!1;const s=()=>{t||(t=!0,e())};requestAnimationFrame(s),setTimeout(s,50),this._workerYield(s)},_workerYield(e){try{if(this._yieldWorker===void 0){const t="onmessage=()=>setTimeout(()=>postMessage(0),0);",s=new Blob([t],{type:"application/javascript"}),i=URL.createObjectURL(s);this._yieldWorker=new Worker(i),URL.revokeObjectURL(i),this._yieldQueue=[],this._yieldWorker.onmessage=()=>{const o=this._yieldQueue.shift();o&&o()}}if(!this._yieldWorker)return;this._yieldQueue.push(e),this._yieldWorker.postMessage(0)}catch{this._yieldWorker=null}},scrollToLastNonEmptyLine(){if(!this.terminal?.buffer?.active){this.terminal?.scrollToBottom?.();return}const e=this.terminal.buffer.active,t=e.baseY+e.length;let s=-1;for(let i=t-1;i>=0;i--)if(e.getLine(i)?.translateToString(!0).trim()){s=i;break}if(s>=0&&typeof this.terminal.scrollToLine=="function"){let i=Math.max(0,s-this.terminal.rows+2);const o=Math.max(0,s);for(;i<o&&!e.getLine(i)?.translateToString(!0).trim();)i++;this.terminal.scrollToLine(i)}else this.terminal.scrollToBottom()},chunkedTerminalWrite(e,t=TERMINAL_CHUNK_SIZE,s){const i=++this._chunkedWriteGen,o=this._beginBufferLoad(s);return new Promise(a=>{if(!e||e.length===0){this._finishBufferLoad(o),a();return}const n=e.replace(DEC_SYNC_STRIP_RE,""),l=()=>{this._chunkedWriteGen===i&&this._finishBufferLoad(o),a()};if(n.length<=t){this.terminal.write(n,l);return}let h=0;const u=performance.now();let c=0;const m=()=>{if(this._chunkedWriteGen!==i){a();return}if(h>=n.length){const w=performance.now()-u;console.log(`[CRASH-DIAG] chunkedTerminalWrite complete: ${n.length} bytes in ${c} chunks, ${w.toFixed(0)}ms total`),this._safeYield(l);return}const y=performance.now(),E=n.slice(h,h+t);this.terminal.write(E);const x=performance.now()-y;c++,x>50&&console.warn(`[CRASH-DIAG] chunk #${c} write took ${x.toFixed(0)}ms (${E.length} bytes at offset ${h})`),h+=t,this._safeYield(m)};this._safeYield(m)})},_beginBufferLoad(e){this._bufferLoadSeq===void 0&&(this._bufferLoadSeq=0);const t=e===void 0?`buffer-${++this._bufferLoadSeq}`:e;return this._bufferLoadOwner=t,this._isLoadingBuffer=!0,this._loadBufferQueue=[],t},_finishBufferLoad(e,t){if(e!==void 0&&this._bufferLoadOwner!==e)return!1;const s=this._loadBufferQueue;if(this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._bufferLoadOwner=null,t?.flushQueued&&s&&s.length)for(const i of s)this.batchTerminalWrite(i);return!0},clearTerminal(){this.terminal.clear()},insertTerminalText(e){!this.activeSessionId||!e||(this._localEchoEnabled&&this._localEchoOverlay?this._localEchoOverlay.appendText(e):this.sendInput(e).catch(()=>{}),this.terminal?.focus())},clearTerminalInput(){if(this.activeSessionId){if(typeof CjkInput<"u"&&CjkInput.clear(),this._inputFlushTimeout&&(clearTimeout(this._inputFlushTimeout),this._inputFlushTimeout=null),this._pendingInput="",this._localEchoEnabled&&this._localEchoOverlay){const e=this._localEchoOverlay.getFlushed?.()||{count:0,text:""};this._localEchoOverlay.clear(),this._localEchoOverlay.suppressBufferDetection(),this._flushedOffsets?.delete(this.activeSessionId),this._flushedTexts?.delete(this.activeSessionId),e.count>0&&this.sendInput("\x7F".repeat(e.count)).catch(()=>{})}else this.sendInput("").catch(()=>{});this.showToast?.("Input cleared","success"),this.terminal?.focus()}},async restoreTerminalSize(){if(!this.activeSessionId){this.showToast("No active session","warning");return}const e=this.getTerminalDimensions();if(!e){this.showToast("Could not determine terminal size","error");return}try{await this.sendResize(this.activeSessionId,{force:!0}),this.showToast(`Terminal restored to ${e.cols}x${e.rows}`,"success")}catch(t){console.error("Failed to restore terminal size:",t),this.showToast("Failed to restore terminal size","error")}},sendPendingCtrlL(e){},async copyTerminal(){try{const e=this.terminal.buffer.active;let t="";for(let s=0;s<e.length;s++){const i=e.getLine(s);i&&(t+=i.translateToString(!0)+`
2
+ `)}await navigator.clipboard.writeText(t.replace(/\n+$/,`
3
+ `)),this.showToast("Copied to clipboard","success")}catch{this.showToast("Failed to copy","error")}},_syncMobileHelperTextareaToCursor(){if(!(!MobileDetection.isTouchDevice()||!this.terminal?.element))try{const e=this.terminal.element,t=this.terminal.element.querySelector(".xterm-cursor"),s=this.terminal.element.querySelector(".xterm-screen");if(!(e instanceof HTMLElement)||!(t instanceof HTMLElement)||!(s instanceof HTMLElement))return;const i=t.getBoundingClientRect(),o=s.getBoundingClientRect();if(!i.width&&!i.height)return;const a=Math.max(0,Math.round(i.left-o.left)),n=Math.max(0,Math.round(i.top-o.top));e.style.setProperty("--xterm-helper-left",`${a}px`),e.style.setProperty("--xterm-helper-top",`${n}px`)}catch{}},_dispatchSyntheticTerminalClick(e,t){const s=this.terminal?.element;if(!s||!Number.isFinite(e)||!Number.isFinite(t))return;const i={bubbles:!0,cancelable:!0,view:window,clientX:e,clientY:t,screenX:e,screenY:t,button:0,detail:1};try{s.dispatchEvent(new MouseEvent("mousedown",{...i,buttons:1})),s.dispatchEvent(new MouseEvent("mouseup",{...i,buttons:0}))}catch{}},_sessionUsesServerMouseStrip(){const e=this.sessions?.get(this.activeSessionId)?.mode||"claude";return e==="claude"||e==="codex"||e==="gemini"},_terminalViewportAtBottom(){const e=this.terminal?.buffer?.active;return!e||e.viewportY>=e.baseY},_clientPointToCell(e,t){if(!this.terminal||!Number.isFinite(e)||!Number.isFinite(t))return null;const s=this.terminal.element?.querySelector(".xterm-screen"),i=this.terminal._core?._renderService?.dimensions?.css?.cell;if(!s||!i?.width||!i?.height)return null;const o=s.getBoundingClientRect(),a=Math.max(1,Math.min(this.terminal.cols,Math.floor((e-o.left)/i.width)+1)),n=Math.max(1,Math.min(this.terminal.rows,Math.floor((t-o.top)/i.height)+1));return{col:a,row:n}},_sendSyntheticSgrTap(e,t){if(!this.activeSessionId||!this._terminalViewportAtBottom())return;const s=this._clientPointToCell(e,t);s&&this._sendInputAsync(this.activeSessionId,`\x1B[<0;${s.col};${s.row}M\x1B[<0;${s.col};${s.row}m`)},_cliVersionAtLeast(e,t){if(typeof e!="string")return!1;const s=e.trim().replace(/^v/,"").split(".").map(Number);if(s.length!==3||s.some(o=>!Number.isFinite(o)))return!1;const i=t.split(".").map(Number);for(let o=0;o<3;o++)if(s[o]!==i[o])return s[o]>i[o];return!0},_wheelScrollLines(e){const t=e.shiftKey&&Math.abs(e.deltaX)>Math.abs(e.deltaY)?e.deltaX:e.deltaY;return Math.round(t/25)||(t>0?1:-1)},_shouldForwardWheelToApp(e){if(e.shiftKey||this.loadAppSettingsFromStorage?.()?.terminalWheelLocalScrollback)return!1;const t=this.terminal?.modes?.mouseTrackingMode;if(t&&t!=="none")return!1;const s=this.sessions?.get(this.activeSessionId),i=s?.mode||"claude";if(i==="claude"){if(!this._cliVersionAtLeast(s?.cliVersion,"2.1.187"))return!1}else if(i!=="codex")return!1;return this._terminalViewportAtBottom()},_sendSyntheticSgrWheel(e,t,s){if(!this.activeSessionId||!s)return;const i=this._clientPointToCell(e,t);if(!i)return;const o=s<0?64:65,a=Math.min(Math.abs(s),5),n=this._wheelSgrQueue||"";n.length>512||(this._wheelSgrQueue=n+`\x1B[<${o};${i.col};${i.row}M`.repeat(a),!this._wheelSgrFlushTimer&&(this._wheelSgrFlushTimer=setTimeout(()=>this._flushWheelSgrQueue(),40)))},_flushWheelSgrQueue(){this._wheelSgrFlushTimer=null;const e=this._wheelSgrQueue;this._wheelSgrQueue="",e&&this.activeSessionId&&this._sendInputEphemeral(this.activeSessionId,e)},_handleDesktopTerminalClick(e){if(!this.terminal||!e?.isTrusted||e.button!==0||e.detail!==1||e.shiftKey||e.altKey||e.ctrlKey||e.metaKey)return;const t=this.terminal.modes?.mouseTrackingMode;t&&t!=="none"||this._sessionUsesServerMouseStrip()&&(this.terminal.hasSelection?.()||this._linkHovered||performance.now()<=(this._trustedTapMouseSuppressUntil||0)||e.target?.closest?.(".xterm-screen")&&this._sendSyntheticSgrTap(e.clientX,e.clientY))},_installMobileTapMouseGuard(){const e=this.terminal?.element;if(!e||e._codemanTapMouseGuardInstalled||typeof MobileDetection<"u"&&MobileDetection.isTouchDevice&&!MobileDetection.isTouchDevice())return;e._codemanTapMouseGuardInstalled=!0;const t=s=>{const i=this._trustedTapMouseSuppressUntil||0;!s.isTrusted||performance.now()>i||(s.preventDefault(),s.stopImmediatePropagation())};e.addEventListener("mousedown",t,!0),e.addEventListener("mouseup",t,!0)},_suppressTrustedTapMouseEvents(){const e=window.CodemanTerminalInput?.TOUCH_COMPAT_MOUSE_SUPPRESS_MS||450;this._trustedTapMouseSuppressUntil=performance.now()+e},increaseFontSize(){const e=this.terminal.options.fontSize||14;this.setFontSize(Math.min(e+2,24))},decreaseFontSize(){const e=this.terminal.options.fontSize||14;this.setFontSize(Math.max(e-2,10))},setFontSize(e){this.terminal.options.fontSize=e,document.getElementById("fontSizeDisplay").textContent=e,this.fitAddon.fit(),localStorage.setItem("codeman-font-size",e),this._localEchoOverlay?.refreshFont()},loadFontSize(){const e=localStorage.getItem("codeman-font-size");if(e){const t=parseInt(e,10);t>=10&&t<=24&&(this.terminal.options.fontSize=t,document.getElementById("fontSizeDisplay").textContent=t)}},getTerminalDimensions(){const s=this.fitAddon?.proposeDimensions();return s?{cols:Math.max(s.cols,40),rows:Math.max(s.rows,10)}:null},async sendResize(e,t={}){this.fitAddon&&this.fitAddon.fit();const s=this.getTerminalDimensions();if(!s)return!1;const i=this._lastResizeDims,o=!i||i.cols!==s.cols||i.rows!==s.rows;this._lastResizeDims={cols:s.cols,rows:s.rows};const a=typeof MobileDetection<"u"&&MobileDetection.getDeviceType?MobileDetection.getDeviceType():window.innerWidth<430?"mobile":window.innerWidth<768?"tablet":"desktop";if(!t.forceHttp&&this._wsReady&&this._wsSessionId===e)try{const l={t:"z",c:s.cols,r:s.rows,v:a};return t.force&&(l.f=!0),this._ws.send(JSON.stringify(l)),o}catch{}const n={...s,viewportType:a};return t.force&&(n.force=!0),await fetch(`/api/sessions/${e}/resize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)}),o},async sendInput(e){!this.activeSessionId||!e||this._sendInputAsync(this.activeSessionId,e,{useMux:!0})},toggleDirInput(){const e=document.querySelector("#dirDisplay").parentElement,t=document.getElementById("dirInput");t.classList.contains("hidden")&&(t.classList.remove("hidden"),e.style.display="none",t.focus())},hideDirInput(){const e=document.querySelector("#dirDisplay").parentElement,t=document.getElementById("dirInput");setTimeout(()=>{t.classList.add("hidden"),e.style.display="";const s=t.value.trim();document.getElementById("dirDisplay").textContent=s||"No directory"},100)},applyTerminalSkin(e){const t={...window.CODEMAN_XTERM_THEMES[e]||window.CODEMAN_XTERM_THEMES["daylight-blue"]},s=window.codemanCurrentSkinIsLight(e)?4.5:1;if(this.terminal){this.terminal.options.minimumContrastRatio=s,this.terminal.options.theme=t,this._localEchoOverlay?.refreshFont();try{this.terminal.refresh(0,this.terminal.rows-1)}catch{}}if(this.teammateTerminals){for(const[,i]of this.teammateTerminals)if(i&&i.terminal){i.terminal.options.minimumContrastRatio=s,i.terminal.options.theme={...t};try{i.terminal.refresh(0,i.terminal.rows-1)}catch{}}}}}),(function(e){const i={session:"Sessions",event:"Events",file:"Files"};function o(a){if(!Number.isFinite(a))return"";const n=new Date(a);return n.toLocaleDateString("en",{month:"short",day:"numeric"})+" "+n.toLocaleTimeString("en",{hour:"2-digit",minute:"2-digit",hour12:!1})}e.CodemanSearch={SEARCH_DEBOUNCE_MS:250,SEARCH_LIMIT:60,SOURCE_LABELS:i,formatSearchTime:o}})(window),Object.assign(CodemanApp.prototype,{initSearchPanel(){const e=document.getElementById("searchInput");if(!e||this._searchPanelWired){this._searchPanelWired&&this._populateSearchCaseFilter();return}this._searchPanelWired=!0,this._searchTypes=new Set(["session","event","file"]),this._searchSecondary={caseLabel:"",status:"",days:""},this._searchDebounceTimer=null,this._searchSeq=0,this._searchLastData=null;const t=document.getElementById("searchClearBtn"),s=document.getElementById("searchResults");e.addEventListener("input",()=>{t&&(t.hidden=e.value.length===0),this._scheduleSearch()}),e.addEventListener("keydown",n=>{n.key==="Escape"&&e.value&&(n.stopPropagation(),this._clearSearch())}),t&&t.addEventListener("click",()=>this._clearSearch()),document.querySelectorAll("#searchFilters .search-filter-chip").forEach(n=>{n.addEventListener("click",()=>{const l=n.dataset.typeFilter;this._searchTypes.has(l)&&this._searchTypes.size===1||(this._searchTypes.has(l)?(this._searchTypes.delete(l),n.classList.remove("active")):(this._searchTypes.add(l),n.classList.add("active")),this._runSearch())})});const i=document.getElementById("searchCaseFilter"),o=document.getElementById("searchStatusFilter"),a=document.getElementById("searchDateFilter");i&&i.addEventListener("change",()=>{this._searchSecondary.caseLabel=i.value,this._renderSearch(this._searchLastData)}),o&&o.addEventListener("change",()=>{this._searchSecondary.status=o.value,this._renderSearch(this._searchLastData)}),a&&a.addEventListener("change",()=>{this._searchSecondary.days=a.value,this._renderSearch(this._searchLastData)}),this._populateSearchCaseFilter(),s&&(s.hidden=!0)},_populateSearchCaseFilter(){const e=document.getElementById("searchCaseFilter");if(!e)return;const t=Array.isArray(this.cases)?this.cases:[],s=Array.from(new Set(t.map(a=>a&&a.name).filter(Boolean))).sort(),i=e.value;e.innerHTML="";const o=document.createElement("option");o.value="",o.textContent="All cases",e.appendChild(o);for(const a of s){const n=document.createElement("option");n.value=a,n.textContent="#"+a,e.appendChild(n)}i&&s.includes(i)&&(e.value=i)},_scheduleSearch(){clearTimeout(this._searchDebounceTimer),this._searchDebounceTimer=setTimeout(()=>this._runSearch(),window.CodemanSearch.SEARCH_DEBOUNCE_MS)},_clearSearch(){const e=document.getElementById("searchInput"),t=document.getElementById("searchClearBtn");e&&(e.value=""),t&&(t.hidden=!0),this._searchLastData=null,this._renderSearch(null)},async _runSearch(){const e=document.getElementById("searchInput");if(!e)return;const t=e.value.trim();if(t.length===0){this._searchLastData=null,this._renderSearch(null);return}const s=Array.from(this._searchTypes),i=new URLSearchParams;i.set("q",t.slice(0,200)),s.length>0&&s.length<3&&i.set("types",s.join(",")),i.set("limit",String(window.CodemanSearch.SEARCH_LIMIT));const o=++this._searchSeq,a=await this._apiJson("/api/search?"+i.toString());o===this._searchSeq&&(a?this._searchLastData=a:this._searchLastData={query:t,groups:[],totalResults:0,truncated:!1,_error:!0},this._renderSearch(this._searchLastData))},_applySecondaryFilters(e){const{caseLabel:t,status:s,days:i}=this._searchSecondary;let o=e;if(t){const a="#"+t;o=o.filter(n=>(n.sessionName||"").includes(a)||n.sessionName===t)}if(s){const a=new Set((this.sessionOrder||[]).concat(Object.keys(this.sessions||{})));o=o.filter(n=>{const l=a.has(n.sessionId);return s==="active"?l:!l})}if(i){const a=Date.now()-Number(i)*24*60*60*1e3;o=o.filter(n=>Number.isFinite(n.timestamp)&&n.timestamp>=a)}return o},_renderSearch(e){const t=document.getElementById("searchResults"),s=document.getElementById("historyTitle"),i=document.getElementById("historyList");if(!t)return;const o=!!e;if(s&&(s.style.display=o?"none":""),i&&(i.style.display=o?"none":""),t.innerHTML="",!e){t.hidden=!0;return}if(t.hidden=!1,e._error){const l=document.createElement("div");l.className="search-empty",l.textContent="Search unavailable \u2014 check the query and try again.",t.appendChild(l);return}const a=(e.groups||[]).map(l=>({type:l.type,results:this._applySecondaryFilters(l.results||[])})).filter(l=>l.results.length>0);if(a.reduce((l,h)=>l+h.results.length,0)===0){const l=document.createElement("div");l.className="search-empty",l.textContent='No results for "'+(e.query||"")+'"',t.appendChild(l);return}for(const l of a){const h=document.createElement("div");h.className="search-group-header";const u=document.createElement("span");u.className="search-group-label",u.textContent=window.CodemanSearch.SOURCE_LABELS[l.type]||l.type;const c=document.createElement("span");c.className="search-group-count",c.textContent=String(l.results.length),h.append(u,c),t.appendChild(h);for(const m of l.results)t.appendChild(this._buildSearchResultCard(m))}if(e.truncated){const l=document.createElement("div");l.className="search-truncated",l.textContent="Showing the top matches \u2014 refine your search to narrow results.",t.appendChild(l)}},_buildSearchResultCard(e){const t=document.createElement("div");t.className="search-result-card",t.dataset.type=e.type,t.tabIndex=0,t.setAttribute("role","button");const s=document.createElement("div");s.className="search-result-top";const i=document.createElement("span");i.className="search-result-badge search-badge-"+e.type,i.textContent=(window.CodemanSearch.SOURCE_LABELS[e.type]||e.type).replace(/s$/,"");const o=document.createElement("span");o.className="search-result-name",o.textContent=e.sessionName||e.sessionId||"(session)";const a=document.createElement("span");a.className="search-result-time",a.textContent=window.CodemanSearch.formatSearchTime(e.timestamp),s.append(i,o,a);const n=document.createElement("div");n.className="search-result-snippet",n.textContent=e.snippet||"",t.append(s,n);const l=()=>this._jumpToSearchResult(e);return t.addEventListener("click",l),t.addEventListener("keydown",h=>{(h.key==="Enter"||h.key===" ")&&(h.preventDefault(),l())}),t},_jumpToSearchResult(e){const t=e&&e.jumpTo;if(t){typeof this.hideWelcome=="function"&&this.hideWelcome();try{t.kind==="run-summary"?this.openRunSummary(t.sessionId):t.kind==="file-preview"?this.openFilePreview(t.relativePath||"",t.sessionId,t.targetId||null):this.selectSession(t.sessionId)}catch(s){console.error("[search] jump failed",s)}}}});
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicodeman",
3
- "version": "1.9.7",
3
+ "version": "1.9.8",
4
4
  "description": "Mission control for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -22,6 +22,7 @@
22
22
  "test:coverage": "vitest run --config config/vitest.config.ts --coverage",
23
23
  "test:ci": "vitest run --config config/vitest.ci.config.ts",
24
24
  "check:frontend-syntax": "node scripts/check-frontend-syntax.mjs",
25
+ "fix:node-pty": "node scripts/fix-node-pty.mjs",
25
26
  "typecheck": "tsc --noEmit",
26
27
  "lint": "eslint --config config/eslint.config.js 'src/**/*.ts'",
27
28
  "lint:fix": "eslint --config config/eslint.config.js 'src/**/*.ts' --fix",
@@ -156,6 +157,7 @@
156
157
  "files": [
157
158
  "dist",
158
159
  "scripts/postinstall.js",
160
+ "scripts/fix-node-pty.mjs",
159
161
  "LICENSE",
160
162
  "README.md"
161
163
  ]
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview Repairs node-pty's macOS `spawn-helper` and verifies that a PTY
4
+ * can really be spawned. Called by `scripts/postinstall.js` on every install and
5
+ * exposed as `npm run fix:node-pty` for repairing an install after the fact.
6
+ *
7
+ * Why this exists (issues #6 and #204):
8
+ *
9
+ * node-pty@1.1.0 publishes its macOS prebuilt helper as
10
+ * `prebuilds/darwin-<arch>/spawn-helper` with mode 0644, i.e. no execute bit.
11
+ * On macOS node-pty launches every PTY through that helper with posix_spawnp,
12
+ * which then fails EACCES and surfaces as `Error: posix_spawnp failed.` on every
13
+ * session start.
14
+ *
15
+ * It is macOS-exclusive twice over: `spawn-helper` is an `OS=="mac"` gyp target,
16
+ * and pty.cc only spawns it under `#if defined(__APPLE__)`. node-pty ships
17
+ * prebuilds for darwin and win32 only, so Linux always compiles from source
18
+ * (which produces an executable helper) and never sees the bug.
19
+ *
20
+ * The repair is a chmod, NOT a rebuild: the prebuilt binary itself is fine, and
21
+ * requiring a from-source rebuild would make every macOS install depend on Xcode
22
+ * command line tools. A rebuild is attempted only when a chmod plus a real spawn
23
+ * probe still can't get a working PTY, and the prebuilds tree is backed up first
24
+ * so a failed rebuild can never leave the install worse than it started.
25
+ */
26
+
27
+ import { chmodSync, cpSync, existsSync, readdirSync, rmSync, statSync } from 'node:fs';
28
+ import { execSync } from 'node:child_process';
29
+ import { createRequire } from 'node:module';
30
+ import { tmpdir } from 'node:os';
31
+ import { dirname, join } from 'node:path';
32
+ import { fileURLToPath } from 'node:url';
33
+
34
+ const require = createRequire(import.meta.url);
35
+
36
+ /** Errors that mean "the native module or its helper is unusable", i.e. worth a rebuild. */
37
+ const NATIVE_FAILURE_PATTERN = /posix_spawnp|spawn-helper|Failed to load native module|Cannot find module/i;
38
+
39
+ /**
40
+ * Locates the installed node-pty package directory.
41
+ *
42
+ * @returns {string|null} Absolute path to the package root, or null if not installed.
43
+ */
44
+ export function findNodePtyDir() {
45
+ // package.json first: node-pty declares no "exports" map, so the subpath resolves,
46
+ // and it lands on the package root directly. require.resolve('node-pty') would give
47
+ // <pkg>/lib/index.js, which is one directory deeper than callers expect.
48
+ try {
49
+ return dirname(require.resolve('node-pty/package.json'));
50
+ } catch {
51
+ /* fall through */
52
+ }
53
+ try {
54
+ return join(dirname(require.resolve('node-pty')), '..');
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Lists every `spawn-helper` shipped in a node-pty install.
62
+ *
63
+ * node-pty's own loader (lib/utils.js) checks `build/Release`, `build/Debug` and
64
+ * then `prebuilds/<platform>-<arch>`, and takes the helper from whichever
65
+ * directory the native module loaded out of, so all of them must be executable,
66
+ * not just the one this machine happens to use today.
67
+ *
68
+ * @param {string} ptyDir Absolute path to the node-pty package root.
69
+ * @returns {string[]} Absolute paths of the helpers that exist on disk.
70
+ */
71
+ export function listSpawnHelpers(ptyDir) {
72
+ const dirs = [join(ptyDir, 'build', 'Release'), join(ptyDir, 'build', 'Debug')];
73
+
74
+ const prebuilds = join(ptyDir, 'prebuilds');
75
+ if (existsSync(prebuilds)) {
76
+ try {
77
+ for (const entry of readdirSync(prebuilds, { withFileTypes: true })) {
78
+ if (entry.isDirectory()) dirs.push(join(prebuilds, entry.name));
79
+ }
80
+ } catch {
81
+ /* unreadable prebuilds dir: nothing to repair there */
82
+ }
83
+ }
84
+
85
+ return dirs.map((d) => join(d, 'spawn-helper')).filter((p) => existsSync(p));
86
+ }
87
+
88
+ /**
89
+ * Adds the execute bit to every `spawn-helper` that is missing it.
90
+ *
91
+ * @param {string} ptyDir Absolute path to the node-pty package root.
92
+ * @returns {{ repaired: string[], failed: Array<{ path: string, error: string }> }}
93
+ */
94
+ export function repairSpawnHelpers(ptyDir) {
95
+ const repaired = [];
96
+ const failed = [];
97
+
98
+ for (const helper of listSpawnHelpers(ptyDir)) {
99
+ try {
100
+ const mode = statSync(helper).mode & 0o777;
101
+ if ((mode & 0o111) === 0o111) continue; // already executable by all
102
+ chmodSync(helper, mode | 0o755);
103
+ repaired.push(helper);
104
+ } catch (err) {
105
+ failed.push({ path: helper, error: err instanceof Error ? err.message : String(err) });
106
+ }
107
+ }
108
+
109
+ return { repaired, failed };
110
+ }
111
+
112
+ /**
113
+ * Proves node-pty works by actually opening a PTY, which is the only check that
114
+ * exercises the spawn-helper path that breaks. A `require` alone would pass on a
115
+ * broken install, because the helper is only touched at spawn time.
116
+ *
117
+ * @param {string} ptyDir Absolute path to the node-pty package root.
118
+ * @returns {{ ok: boolean, error?: string, nativeFailure?: boolean }}
119
+ */
120
+ export function verifyPtySpawn(ptyDir) {
121
+ let child;
122
+ try {
123
+ const pty = require(ptyDir); // directory require → node-pty's "main" (lib/index.js)
124
+ const file = process.platform === 'win32' ? process.env.COMSPEC || 'cmd.exe' : '/bin/echo';
125
+ const args = process.platform === 'win32' ? ['/c', 'exit'] : ['codeman-node-pty-check'];
126
+ child = pty.spawn(file, args, {
127
+ name: 'xterm-color',
128
+ cols: 80,
129
+ rows: 24,
130
+ cwd: tmpdir(),
131
+ env: process.env,
132
+ });
133
+ return { ok: true };
134
+ } catch (err) {
135
+ const message = err instanceof Error ? err.message : String(err);
136
+ return { ok: false, error: message, nativeFailure: NATIVE_FAILURE_PATTERN.test(message) };
137
+ } finally {
138
+ try {
139
+ child?.kill();
140
+ } catch {
141
+ /* the probe child exits on its own anyway */
142
+ }
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Rebuilds node-pty from source, preserving the prebuilds tree across a failure.
148
+ *
149
+ * node-pty's install script deletes `prebuilds/` as soon as
150
+ * `npm_config_build_from_source` is set and only then shells out to node-gyp, so
151
+ * a machine without a compiler toolchain would otherwise be left with neither a
152
+ * prebuilt nor a compiled binary.
153
+ *
154
+ * @param {string} ptyDir Absolute path to the node-pty package root.
155
+ * @param {string} cwd Directory to run npm from (the package root that owns node_modules).
156
+ * @returns {{ ok: boolean, error?: string }}
157
+ */
158
+ function rebuildFromSource(ptyDir, cwd) {
159
+ const prebuilds = join(ptyDir, 'prebuilds');
160
+ const backup = join(ptyDir, '.prebuilds-codeman-backup');
161
+
162
+ let backedUp = false;
163
+ if (existsSync(prebuilds)) {
164
+ try {
165
+ rmSync(backup, { recursive: true, force: true });
166
+ cpSync(prebuilds, backup, { recursive: true });
167
+ backedUp = true;
168
+ } catch {
169
+ /* best effort: proceed without a safety net rather than skip the repair */
170
+ }
171
+ }
172
+
173
+ try {
174
+ execSync('npm rebuild node-pty --build-from-source', { cwd, stdio: 'pipe', timeout: 300000 });
175
+ return { ok: true };
176
+ } catch (err) {
177
+ if (backedUp && !existsSync(prebuilds)) {
178
+ try {
179
+ cpSync(backup, prebuilds, { recursive: true });
180
+ } catch {
181
+ /* nothing further we can do */
182
+ }
183
+ }
184
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
185
+ } finally {
186
+ rmSync(backup, { recursive: true, force: true });
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Full repair flow: chmod, verify, and only rebuild if a working PTY still can't
192
+ * be opened.
193
+ *
194
+ * @param {object} [options]
195
+ * @param {(line: string) => void} [options.log] Progress sink (default: silent).
196
+ * @param {(line: string) => void} [options.warn] Warning sink (default: same as log).
197
+ * @param {boolean} [options.allowRebuild] Permit a from-source rebuild (default: true).
198
+ * @returns {Promise<{ ok: boolean, repaired: string[], rebuilt: boolean, reason?: string }>}
199
+ */
200
+ export async function fixNodePty(options = {}) {
201
+ const log = options.log ?? (() => {});
202
+ const warn = options.warn ?? log;
203
+ const allowRebuild = options.allowRebuild ?? true;
204
+
205
+ const ptyDir = findNodePtyDir();
206
+ if (!ptyDir) {
207
+ return { ok: false, repaired: [], rebuilt: false, reason: 'node-pty is not installed' };
208
+ }
209
+
210
+ const { repaired, failed } = repairSpawnHelpers(ptyDir);
211
+ for (const f of failed) warn(`could not chmod ${f.path}: ${f.error}`);
212
+ if (repaired.length > 0) {
213
+ log(`made node-pty spawn-helper executable (${repaired.length} file${repaired.length === 1 ? '' : 's'})`);
214
+ }
215
+
216
+ const first = verifyPtySpawn(ptyDir);
217
+ if (first.ok) return { ok: true, repaired, rebuilt: false };
218
+
219
+ if (!allowRebuild || !first.nativeFailure) {
220
+ return { ok: false, repaired, rebuilt: false, reason: first.error };
221
+ }
222
+
223
+ warn(`node-pty could not open a PTY (${first.error}), rebuilding from source...`);
224
+ const projectRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
225
+ const rebuild = rebuildFromSource(ptyDir, projectRoot);
226
+ if (!rebuild.ok) {
227
+ return { ok: false, repaired, rebuilt: false, reason: `rebuild failed: ${rebuild.error}` };
228
+ }
229
+
230
+ const after = repairSpawnHelpers(ptyDir);
231
+ repaired.push(...after.repaired);
232
+
233
+ const second = verifyPtySpawn(ptyDir);
234
+ return second.ok
235
+ ? { ok: true, repaired, rebuilt: true }
236
+ : { ok: false, repaired, rebuilt: true, reason: second.error };
237
+ }
238
+
239
+ // ---------------------------------------------------------------------------
240
+ // CLI: node scripts/fix-node-pty.mjs [--quiet]
241
+ // ---------------------------------------------------------------------------
242
+
243
+ const isDirectRun = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1];
244
+
245
+ if (isDirectRun) {
246
+ const quiet = process.argv.includes('--quiet');
247
+ const say = (line) => {
248
+ if (!quiet) console.log(line);
249
+ };
250
+
251
+ const result = await fixNodePty({ log: say, warn: (line) => console.warn(line) });
252
+
253
+ if (result.ok) {
254
+ say(result.repaired.length > 0 || result.rebuilt ? 'node-pty repaired, PTY spawning works' : 'node-pty is healthy');
255
+ process.exit(0);
256
+ }
257
+
258
+ console.error(`node-pty is not usable: ${result.reason}`);
259
+ console.error('Try: cd node_modules/node-pty && npx node-gyp rebuild');
260
+ process.exit(1);
261
+ }
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { execSync, spawn } from 'child_process';
9
- import { chmodSync, existsSync } from 'fs';
9
+ import { existsSync } from 'fs';
10
10
  import { homedir, platform } from 'os';
11
11
  import { join } from 'path';
12
12
  import { createRequire } from 'module';
@@ -148,35 +148,32 @@ if (majorVersion < MIN_NODE_VERSION) {
148
148
  }
149
149
 
150
150
  // ----------------------------------------------------------------------------
151
- // 1b. Fix node-pty spawn-helper permissions (macOS posix_spawnp fix)
151
+ // 1b. Repair + verify node-pty (macOS posix_spawnp fix, issues #6 and #204)
152
+ //
153
+ // node-pty ships its macOS spawn-helper without the execute bit, which breaks
154
+ // every session start on macOS. fixNodePty() chmods it, then proves a PTY can
155
+ // actually be opened, and only falls back to a from-source rebuild if that
156
+ // still fails. See scripts/fix-node-pty.mjs for the full story.
152
157
  // ----------------------------------------------------------------------------
153
158
 
154
159
  try {
155
- const require = createRequire(import.meta.url);
156
- const ptyPath = join(require.resolve('node-pty'), '..');
157
- const spawnHelper = join(ptyPath, 'build', 'Release', 'spawn-helper');
158
- if (existsSync(spawnHelper)) {
159
- chmodSync(spawnHelper, 0o755);
160
- console.log(colors.green('✓ node-pty spawn-helper permissions fixed'));
161
- }
162
- } catch {
163
- // Non-critical — only affects macOS with prebuilt binaries
164
- }
165
-
166
- // ----------------------------------------------------------------------------
167
- // 1c. Rebuild node-pty from source for Node.js 22+ compatibility
168
- // ----------------------------------------------------------------------------
160
+ const { fixNodePty } = await import('./fix-node-pty.mjs');
161
+ const result = await fixNodePty({
162
+ log: (line) => console.log(colors.dim(` ${line}`)),
163
+ warn: (line) => console.log(colors.yellow(`⚠ ${line}`)),
164
+ });
169
165
 
170
- if (majorVersion >= 22) {
171
- try {
172
- console.log(colors.dim(' Rebuilding node-pty from source for Node.js 22+...'));
173
- execSync('npm rebuild node-pty --build-from-source', { stdio: 'pipe', timeout: 120000 });
174
- console.log(colors.green('✓ node-pty rebuilt from source'));
175
- } catch {
166
+ if (result.ok) {
167
+ console.log(colors.green('✓ node-pty verified') + colors.dim(' (PTY spawn works)'));
168
+ } else {
176
169
  hasWarnings = true;
177
- console.log(colors.yellow('⚠ Failed to rebuild node-pty from source'));
178
- console.log(colors.dim(' You may need to run: npm rebuild node-pty --build-from-source'));
170
+ console.log(colors.yellow(`⚠ node-pty is not usable: ${result.reason}`));
171
+ console.log(colors.dim(' Sessions will fail to start. Try: ') + colors.cyan('npm run fix:node-pty'));
179
172
  }
173
+ } catch (err) {
174
+ hasWarnings = true;
175
+ console.log(colors.yellow(`⚠ Could not verify node-pty: ${err.message}`));
176
+ console.log(colors.dim(' If sessions fail to start, run: ') + colors.cyan('npm run fix:node-pty'));
180
177
  }
181
178
 
182
179
  // ----------------------------------------------------------------------------
Binary file