glenn-code 1.0.5 → 1.0.12

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.
@@ -1247,7 +1247,6 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1247
1247
  - **Dialog handling**: If an alert/confirm/prompt appears, use \`browser_handle_dialog\` immediately to dismiss it
1248
1248
  - **Browser lifecycle**: YOU control when to close the browser with \`browser_close\`. Keep it open if you need to do more interactions.
1249
1249
  - Use \`browser_console_messages\` to check for JavaScript errors when debugging
1250
- - **Popups auto-hidden**: Common feedback widgets (Wootric, Intercom, HubSpot, Drift, Zendesk, Hotjar) are automatically hidden
1251
1250
  - **iframes**: Use \`browser_evaluate\` or \`browser_run_code\` to interact with iframe content:
1252
1251
  \`\`\`javascript
1253
1252
  // Focus iframe and get content
@@ -1258,36 +1257,14 @@ You have access to **Playwright MCP** tools to verify the UI works correctly aft
1258
1257
  - **Multi-tab apps**: Use \`browser_tabs\` to manage multiple windows/tabs in legacy applications
1259
1258
  - **Large snapshots**: For pages with huge dropdowns (e.g., country lists), use \`browser_evaluate\` to collapse them first
1260
1259
  - ${e?"In local mode, the browser window will be visible to you":"In container mode, browser runs headless"}
1261
- </ui-verification>`}function pe(n){return n&&n.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var Ee=null;function We(n){Ee=pe(n),console.log(`[ProjectPrompt] Updated (${Ee?Ee.length:0} chars)`)}function jt(){return pe(Ee)}import*as Z from"path";import*as Qe from"fs";import{fileURLToPath as Sn}from"url";var wn=Be(),Ye=Z.dirname(Sn(import.meta.url)),Ot=[Z.resolve(Ye,"../mcps/stdio-server.js"),Z.resolve(Ye,"./adapters/mcps/stdio-server.js")],ue=Ot.find(n=>Qe.existsSync(n))||Ot[0];console.log("[MCP] Stdio server path resolved:",ue);console.log("[MCP] __dirname:",Ye);console.log("[MCP] File exists:",Qe.existsSync(ue));async function Ae(n,e={}){let{model:t,sessionId:o=null,projectId:s=null,apiUrl:r=null,callbacks:i={},debugLog:a,abortController:c,debugMode:l=!1,isLocal:d=process.env.LOCAL_MODE==="true",projectConfig:u=W(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:g=!0}=e,w=At(a),v=new Set,b=o||"",D,j,f,O=!1,q=!1,F=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[ue]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",s||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",F),console.log("[MCP] File exists check:",ue);try{for await(let p of bn({prompt:n,options:{abortController:c,cwd:F,resume:o??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...d?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_run_code","mcp__playwright__browser_tabs","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:t,mcpServers:{"agent-insights":wn,"agent-planning":{type:"stdio",command:"node",args:[ue],env:{WORKSPACE_DIR:F,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:s||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${Z.join(F,".playwright-data")}`,"--init-script",`(function() {
1262
- const popupSelectors = [
1263
- '[class*="wootric"]', '[id*="wootric"]',
1264
- '[class*="intercom"]', '[id*="intercom"]',
1265
- '[class*="hubspot"]', '[id*="hubspot"]',
1266
- '[class*="drift"]', '[id*="drift"]',
1267
- '[class*="zendesk"]', '[id*="zendesk"]',
1268
- '[class*="hotjar"]', '[id*="hotjar"]',
1269
- '[class*="feedback"]', '[class*="survey"]',
1270
- '[class*="nps-"]', '[id*="nps-"]',
1271
- '.grecaptcha-badge'
1272
- ];
1273
- const hidePopups = () => {
1274
- popupSelectors.forEach(sel => {
1275
- document.querySelectorAll(sel).forEach(el => {
1276
- if (el instanceof HTMLElement) el.style.display = 'none';
1277
- });
1278
- });
1279
- };
1280
- hidePopups();
1281
- const observer = new MutationObserver(hidePopups);
1282
- observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
1283
- })();`,...d?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:g?{scaffolding:Ke,debugger:be,planning:Se,backend:qe,frontend:Ge,"project-context":we}:{debugger:be,planning:Se,"project-context":we},systemPrompt:Dt({debugMode:l,projectPrompt:jt(),isLocal:d,projectConfig:u,useDefaultStack:g})}}))if(!(!p.uuid||v.has(p.uuid))){switch(v.add(p.uuid),w?.log(p),i.onRawMessage?.(p),p.type){case"assistant":if(p.message?.content)for(let C of p.message.content)C.type==="text"?i.onAssistantText?.(C.text):C.type==="tool_use"?i.onAssistantToolUse?.(C.name,C.input):C.type==="tool_result"&&i.onAssistantToolResult?.(C.tool_use_id,C.content);break;case"user":i.onUserMessage?.(p);break;case"result":if(p.subtype==="success")D=p.result,j=p.total_cost_usd,i.onResult?.(p.result,p.total_cost_usd);else{let C=`Agent error: ${p.subtype}`;f=C,i.onError?.(C)}q=!0;break;case"system":switch(p.subtype){case"init":b=p.session_id,w?.setSessionId(p.session_id),i.onSystemInit?.(p.session_id);break;case"status":i.onSystemStatus?.(JSON.stringify(p));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":i.onStreamEvent?.(p);break;case"tool_progress":i.onToolProgress?.(p);break;case"auth_status":i.onAuthStatus?.(p);break}if(q)break}}catch(p){p instanceof Error&&p.name==="AbortError"||p instanceof Error&&p.message.includes("aborted by user")?(O=!0,i.onAborted?.()):(f=p instanceof Error?p.message:String(p),i.onError?.(f))}finally{w?.stop()}return console.log("after try"),{sessionId:b,result:D,cost:j,error:f,aborted:O}}import{spawn as Mt,execSync as ee}from"child_process";import*as R from"fs";import*as Ut from"http";import*as V from"path";function xe(n){let{workspaceDir:e,apiPort:t,webPort:o,onBackendError:s,onFrontendError:r,projectConfig:i=W(e)}=n,a=t??i.services?.backend?.port??5338,c=o??i.services?.frontend?.port??5173,l=H(i),d=B(i),u=i.services?.backend?.startCommand??"dotnet run",g=i.services?.backend?.buildCommand??"dotnet build",w=i.services?.backend?.typecheckCommand??"dotnet build --no-restore",v=i.services?.frontend?.startCommand??"npm run dev",b=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",D=i.services?.backend?.logFile??"/tmp/api.log",j=i.services?.frontend?.logFile??"/tmp/web.log",f=null,O=null,q=y=>new Promise(m=>{let S=setTimeout(()=>m(!1),3e3);Ut.get(`http://localhost:${y}`,x=>{clearTimeout(S),m(x.statusCode!==void 0&&x.statusCode<500)}).on("error",()=>{clearTimeout(S),m(!1)})}),F=y=>{try{let m=ee(`lsof -ti:${y} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(m){let S=m.split(`
1284
- `).filter(Boolean);console.log(`[Services] Killing ${S.length} process(es) on port ${y}: ${S.join(", ")}`);for(let _ of S)try{process.kill(parseInt(_,10),"SIGKILL")}catch{}}}catch{try{ee(`fuser -k ${y}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},p=(y,m)=>new Promise(S=>{if(!y||!y.pid){S();return}console.log(`[Services] Stopping ${m} (PID: ${y.pid})...`);try{process.kill(-y.pid,"SIGTERM")}catch{try{y.kill("SIGTERM")}catch{}}setTimeout(S,500)}),C=async()=>{if(!l||!R.existsSync(l)){console.log("[Services] No backend found, skipping backend start");return}F(a),console.log(`[Services] Starting backend with: ${u}...`);let y=V.dirname(D);R.existsSync(y)||R.mkdirSync(y,{recursive:!0});let m=R.createWriteStream(D,{flags:"a"}),[S,..._]=u.split(" ");f=Mt(S,_,{cwd:l,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let x="",E=P=>{let Q=P.toString();m.write(Q);let L=(x+Q).split(`
1285
- `);x=L.pop()||"";for(let $ of L)$&&vn($)&&s&&s($)};f.stdout?.on("data",E),f.stderr?.on("data",E),f.on("exit",P=>{m.end(),P!==0&&P!==null&&s&&s(`Process exited with code ${P}`)}),f.unref()},de=async()=>{if(!d||!R.existsSync(V.join(d,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}F(c),console.log(`[Services] Starting frontend with: ${v}...`);let y=V.dirname(j);R.existsSync(y)||R.mkdirSync(y,{recursive:!0});let[m,...S]=v.split(" ");O=Mt(m,S,{cwd:d,stdio:["ignore",R.openSync(j,"w"),R.openSync(j,"w")],detached:!0,shell:!0}),O.unref()},ft=async()=>{await p(f,"backend"),f=null,F(a)},mt=async()=>{await p(O,"frontend"),O=null,F(c)},$e=async()=>{let[y,m]=await Promise.all([q(a),q(c)]);return{api:y,web:m}},ht=async(y=15e3)=>{let m=Date.now(),S=1e3,_=0,x=null,E=null;for(console.log(`[Services] Waiting for health (timeout: ${y}ms)...`);Date.now()-m<y;){_++;let L=await $e(),$=Date.now()-m;if(L.web&&E===null&&(E=$,console.log(`[Services] \u2713 Frontend (Vite) ready after ${$}ms`)),L.api&&x===null&&(x=$,console.log(`[Services] \u2713 Backend (dotnet) ready after ${$}ms`)),console.log(`[Services] Health poll #${_} (${$}ms): api=${L.api}, web=${L.web}`),L.api&&L.web)return console.log(`[Services] \u2713 Both services healthy after ${$}ms (${_} polls)`),L;await new Promise(Vt=>setTimeout(Vt,S))}let P=await $e(),Q=Date.now()-m;return P.web&&E===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${Q}ms`),P.api&&x===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${Q}ms`),console.log(`[Services] \u26A0 Health timeout after ${Q}ms: api=${P.api}, web=${P.web}`),P},zt=async y=>{console.log(`[Services] Restarting ${y}...`);let m={success:!0},S=y==="backend"||y==="both",_=y==="frontend"||y==="both";if(S&&await ft(),_&&await mt(),S&&l&&R.existsSync(l)){console.log(`[Services] Building backend with: ${g}...`);try{ee(g,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(E){let P=E;m.success=!1,m.backendError=P.stderr||P.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(_&&d&&R.existsSync(V.join(d,"package.json"))){console.log(`[Services] Type-checking frontend with: ${b}...`);try{ee(b,{cwd:d,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(E){let P=E;m.success=!1,m.frontendError=P.stderr||P.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${y}...`),S&&await C(),_&&await de();let x=await ht(1e4);if(S&&!x.api){m.success=!1;let E=await St("backend",20),P=E.length>0?`
1260
+ </ui-verification>`}function pe(n){return n&&n.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}var Ee=null;function We(n){Ee=pe(n),console.log(`[ProjectPrompt] Updated (${Ee?Ee.length:0} chars)`)}function jt(){return pe(Ee)}import*as Z from"path";import*as Qe from"fs";import{fileURLToPath as Sn}from"url";var wn=Be(),Ye=Z.dirname(Sn(import.meta.url)),Ot=[Z.resolve(Ye,"../mcps/stdio-server.js"),Z.resolve(Ye,"./adapters/mcps/stdio-server.js")],ue=Ot.find(n=>Qe.existsSync(n))||Ot[0];console.log("[MCP] Stdio server path resolved:",ue);console.log("[MCP] __dirname:",Ye);console.log("[MCP] File exists:",Qe.existsSync(ue));async function Ae(n,e={}){let{model:t,sessionId:o=null,projectId:s=null,apiUrl:r=null,callbacks:i={},debugLog:a,abortController:c,debugMode:l=!1,isLocal:d=process.env.LOCAL_MODE==="true",projectConfig:u=W(process.env.WORKSPACE_DIR||process.cwd()),useDefaultStack:g=!0}=e,w=At(a),v=new Set,b=o||"",D,j,f,O=!1,q=!1,F=process.env.WORKSPACE_DIR||process.cwd();console.log("[MCP] Configuring agent-planning stdio server:"),console.log("[MCP] Command: node"),console.log("[MCP] Args:",[ue]),console.log("[MCP] Env: API_URL=",process.env.API_URL||"http://localhost:5338",", PROJECT_ID=",s||process.env.PROJECT_ID||"",", PROJECT_KEY=",process.env.PROJECT_KEY?"***set***":"(not set)",", WORKSPACE_DIR=",F),console.log("[MCP] File exists check:",ue);try{for await(let p of bn({prompt:n,options:{abortController:c,cwd:F,resume:o??void 0,allowedTools:["Bash","Read","Write","Edit","MultiEdit","Glob","Grep","LS","WebFetch","NotebookRead","NotebookEdit","Skill","mcp__agent-insights__check_backend_build","mcp__agent-insights__check_frontend_types","mcp__agent-insights__check_service_health",...d?[]:["mcp__agent-insights__stop_services","mcp__agent-insights__start_services","mcp__agent-insights__restart_services"],"mcp__agent-planning__start_question_session","mcp__agent-planning__ask_question","mcp__agent-planning__end_question_session","mcp__agent-planning__save_specification","mcp__agent-planning__list_specifications","mcp__agent-planning__read_specification","mcp__agent-planning__delete_specification","mcp__agent-planning__report_learning","mcp__agent-planning__report_bug","mcp__agent-planning__report_debug_insight","mcp__agent-planning__get_kanban_board","mcp__agent-planning__get_column_cards","mcp__agent-planning__get_card_details","mcp__agent-planning__create_kanban_card","mcp__agent-planning__update_kanban_card","mcp__agent-planning__move_kanban_card","mcp__agent-planning__delete_kanban_card","mcp__agent-planning__create_subtask","mcp__agent-planning__toggle_subtask","mcp__agent-planning__delete_subtask","mcp__agent-planning__get_project_prompt","mcp__agent-planning__update_project_prompt","mcp__agent-planning__create_command","mcp__agent-planning__get_test_suites","mcp__agent-planning__get_test_suite_details","mcp__agent-planning__get_test_details","mcp__agent-planning__run_test","mcp__agent-planning__run_test_suite","mcp__agent-planning__report_test_status","mcp__agent-planning__create_test_suite","mcp__agent-planning__create_test","mcp__agent-planning__update_test","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_scroll_down","mcp__playwright__browser_scroll_up","mcp__playwright__browser_go_back","mcp__playwright__browser_go_forward","mcp__playwright__browser_wait","mcp__playwright__browser_fill_form","mcp__playwright__browser_select_option","mcp__playwright__browser_press_key","mcp__playwright__browser_hover","mcp__playwright__browser_drag","mcp__playwright__browser_file_upload","mcp__playwright__browser_handle_dialog","mcp__playwright__browser_close","mcp__playwright__browser_resize","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_screen_capture","mcp__playwright__browser_screen_move_mouse","mcp__playwright__browser_screen_click","mcp__playwright__browser_screen_drag","mcp__playwright__browser_screen_type","mcp__playwright__browser_evaluate","mcp__playwright__browser_run_code","mcp__playwright__browser_tabs","mcp__playwright__browser_tab_list","mcp__playwright__browser_tab_new","mcp__playwright__browser_tab_select","mcp__playwright__browser_tab_close"],model:t,mcpServers:{"agent-insights":wn,"agent-planning":{type:"stdio",command:"node",args:[ue],env:{WORKSPACE_DIR:F,API_URL:r||process.env.API_URL||process.env.MASTER_URL||"http://localhost:5338",PROJECT_ID:s||process.env.PROJECT_ID||"",PROJECT_KEY:process.env.PROJECT_KEY||""}},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--caps=vision",`--user-data-dir=${Z.join(F,".playwright-data")}`,...d?[]:["--headless"]]}},settingSources:["project"],disallowedTools:["AskUserQuestion","TodoWrite","EnterPlanMode"],agents:g?{scaffolding:Ke,debugger:be,planning:Se,backend:qe,frontend:Ge,"project-context":we}:{debugger:be,planning:Se,"project-context":we},systemPrompt:Dt({debugMode:l,projectPrompt:jt(),isLocal:d,projectConfig:u,useDefaultStack:g})}}))if(!(!p.uuid||v.has(p.uuid))){switch(v.add(p.uuid),w?.log(p),i.onRawMessage?.(p),p.type){case"assistant":if(p.message?.content)for(let C of p.message.content)C.type==="text"?i.onAssistantText?.(C.text):C.type==="tool_use"?i.onAssistantToolUse?.(C.name,C.input):C.type==="tool_result"&&i.onAssistantToolResult?.(C.tool_use_id,C.content);break;case"user":i.onUserMessage?.(p);break;case"result":if(p.subtype==="success")D=p.result,j=p.total_cost_usd,i.onResult?.(p.result,p.total_cost_usd);else{let C=`Agent error: ${p.subtype}`;f=C,i.onError?.(C)}q=!0;break;case"system":switch(p.subtype){case"init":b=p.session_id,w?.setSessionId(p.session_id),i.onSystemInit?.(p.session_id);break;case"status":i.onSystemStatus?.(JSON.stringify(p));break;case"compact_boundary":break;case"hook_response":break}break;case"stream_event":i.onStreamEvent?.(p);break;case"tool_progress":i.onToolProgress?.(p);break;case"auth_status":i.onAuthStatus?.(p);break}if(q)break}}catch(p){p instanceof Error&&p.name==="AbortError"||p instanceof Error&&p.message.includes("aborted by user")?(O=!0,i.onAborted?.()):(f=p instanceof Error?p.message:String(p),i.onError?.(f))}finally{w?.stop()}return console.log("after try"),{sessionId:b,result:D,cost:j,error:f,aborted:O}}import{spawn as Ut,execSync as ee}from"child_process";import*as R from"fs";import*as Mt from"http";import*as V from"path";function xe(n){let{workspaceDir:e,apiPort:t,webPort:o,onBackendError:s,onFrontendError:r,projectConfig:i=W(e)}=n,a=t??i.services?.backend?.port??5338,c=o??i.services?.frontend?.port??5173,l=H(i),d=B(i),u=i.services?.backend?.startCommand??"dotnet run",g=i.services?.backend?.buildCommand??"dotnet build",w=i.services?.backend?.typecheckCommand??"dotnet build --no-restore",v=i.services?.frontend?.startCommand??"npm run dev",b=i.services?.frontend?.typecheckCommand??"npx tsc -p tsconfig.app.json --noEmit",D=i.services?.backend?.logFile??"/tmp/api.log",j=i.services?.frontend?.logFile??"/tmp/web.log",f=null,O=null,q=y=>new Promise(m=>{let S=setTimeout(()=>m(!1),3e3);Mt.get(`http://localhost:${y}`,x=>{clearTimeout(S),m(x.statusCode!==void 0&&x.statusCode<500)}).on("error",()=>{clearTimeout(S),m(!1)})}),F=y=>{try{let m=ee(`lsof -ti:${y} 2>/dev/null || true`,{encoding:"utf-8"}).trim();if(m){let S=m.split(`
1261
+ `).filter(Boolean);console.log(`[Services] Killing ${S.length} process(es) on port ${y}: ${S.join(", ")}`);for(let _ of S)try{process.kill(parseInt(_,10),"SIGKILL")}catch{}}}catch{try{ee(`fuser -k ${y}/tcp 2>/dev/null || true`,{encoding:"utf-8"})}catch{}}},p=(y,m)=>new Promise(S=>{if(!y||!y.pid){S();return}console.log(`[Services] Stopping ${m} (PID: ${y.pid})...`);try{process.kill(-y.pid,"SIGTERM")}catch{try{y.kill("SIGTERM")}catch{}}setTimeout(S,500)}),C=async()=>{if(!l||!R.existsSync(l)){console.log("[Services] No backend found, skipping backend start");return}F(a),console.log(`[Services] Starting backend with: ${u}...`);let y=V.dirname(D);R.existsSync(y)||R.mkdirSync(y,{recursive:!0});let m=R.createWriteStream(D,{flags:"a"}),[S,..._]=u.split(" ");f=Ut(S,_,{cwd:l,stdio:["ignore","pipe","pipe"],detached:!0,shell:!0});let x="",E=P=>{let Q=P.toString();m.write(Q);let L=(x+Q).split(`
1262
+ `);x=L.pop()||"";for(let $ of L)$&&vn($)&&s&&s($)};f.stdout?.on("data",E),f.stderr?.on("data",E),f.on("exit",P=>{m.end(),P!==0&&P!==null&&s&&s(`Process exited with code ${P}`)}),f.unref()},de=async()=>{if(!d||!R.existsSync(V.join(d,"package.json"))){console.log("[Services] No frontend found, skipping frontend start");return}F(c),console.log(`[Services] Starting frontend with: ${v}...`);let y=V.dirname(j);R.existsSync(y)||R.mkdirSync(y,{recursive:!0});let[m,...S]=v.split(" ");O=Ut(m,S,{cwd:d,stdio:["ignore",R.openSync(j,"w"),R.openSync(j,"w")],detached:!0,shell:!0}),O.unref()},ft=async()=>{await p(f,"backend"),f=null,F(a)},mt=async()=>{await p(O,"frontend"),O=null,F(c)},$e=async()=>{let[y,m]=await Promise.all([q(a),q(c)]);return{api:y,web:m}},ht=async(y=15e3)=>{let m=Date.now(),S=1e3,_=0,x=null,E=null;for(console.log(`[Services] Waiting for health (timeout: ${y}ms)...`);Date.now()-m<y;){_++;let L=await $e(),$=Date.now()-m;if(L.web&&E===null&&(E=$,console.log(`[Services] \u2713 Frontend (Vite) ready after ${$}ms`)),L.api&&x===null&&(x=$,console.log(`[Services] \u2713 Backend (dotnet) ready after ${$}ms`)),console.log(`[Services] Health poll #${_} (${$}ms): api=${L.api}, web=${L.web}`),L.api&&L.web)return console.log(`[Services] \u2713 Both services healthy after ${$}ms (${_} polls)`),L;await new Promise(Vt=>setTimeout(Vt,S))}let P=await $e(),Q=Date.now()-m;return P.web&&E===null&&console.log(`[Services] \u2713 Frontend (Vite) ready after ${Q}ms`),P.api&&x===null&&console.log(`[Services] \u2713 Backend (dotnet) ready after ${Q}ms`),console.log(`[Services] \u26A0 Health timeout after ${Q}ms: api=${P.api}, web=${P.web}`),P},zt=async y=>{console.log(`[Services] Restarting ${y}...`);let m={success:!0},S=y==="backend"||y==="both",_=y==="frontend"||y==="both";if(S&&await ft(),_&&await mt(),S&&l&&R.existsSync(l)){console.log(`[Services] Building backend with: ${g}...`);try{ee(g,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),console.log("[Services] \u2713 Backend build succeeded")}catch(E){let P=E;m.success=!1,m.backendError=P.stderr||P.stdout||"Build failed",console.error("[Services] \u2717 Backend build failed")}}if(_&&d&&R.existsSync(V.join(d,"package.json"))){console.log(`[Services] Type-checking frontend with: ${b}...`);try{ee(b,{cwd:d,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),console.log("[Services] \u2713 Frontend types OK")}catch(E){let P=E;m.success=!1,m.frontendError=P.stderr||P.stdout||"Type check failed",console.error("[Services] \u2717 Frontend type check failed")}}console.log(`[Services] Starting ${y}...`),S&&await C(),_&&await de();let x=await ht(1e4);if(S&&!x.api){m.success=!1;let E=await St("backend",20),P=E.length>0?`
1286
1263
  Recent logs:
1287
1264
  ${E.join(`
1288
1265
  `)}`:"";m.backendError||(m.backendError=`Backend failed to start.${P}`)}return _&&!x.web&&(m.success=!1,m.frontendError||(m.frontendError="Frontend failed to start")),m},yt=async()=>{if(!l||!R.existsSync(l))return{success:!0};try{return ee(w,{cwd:l,stdio:"pipe",timeout:12e4,encoding:"utf-8"}),{success:!0}}catch(y){let m=y;return{success:!1,errors:m.stdout||m.stderr||"Build failed"}}},bt=async()=>{if(!d||!R.existsSync(V.join(d,"package.json")))return{success:!0};try{return ee(b,{cwd:d,stdio:"pipe",timeout:6e4,encoding:"utf-8"}),{success:!0}}catch(y){let m=y;return{success:!1,errors:m.stdout||m.stderr||"Type check failed"}}},Jt=async()=>{let[y,m]=await Promise.all([yt(),bt()]);return{backend:y,frontend:m}},St=async(y,m)=>{let S=y==="backend"?D:j;if(!R.existsSync(S))return[`Log file not found: ${S}`];try{return ee(`tail -n ${m} ${S}`,{encoding:"utf-8"}).split(`
1289
- `).filter(Boolean)}catch(_){return[`Error reading logs: ${_ instanceof Error?_.message:String(_)}`]}};return{startBackend:C,startFrontend:de,stopBackend:ft,stopFrontend:mt,restartServices:zt,checkHealth:$e,waitForHealth:ht,getProcesses:()=>({backend:f,frontend:O}),checkBackendBuild:yt,checkFrontendTypes:bt,runTypeChecks:Jt,tailLogs:St,checkSwaggerEndpoints:async y=>{let m=V.join(e,"swagger.json");if(!R.existsSync(m))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let S=JSON.parse(R.readFileSync(m,"utf-8")),_=Object.keys(S.paths||{}),x=[];for(let E of _)if(E.toLowerCase().includes(y.toLowerCase())){let P=Object.keys(S.paths[E]);for(let Q of P)x.push(`${Q.toUpperCase()} ${E}`)}return{foundEndpoints:x,totalEndpoints:_.length}}catch(S){return{foundEndpoints:[`Error parsing swagger.json: ${S instanceof Error?S.message:String(S)}`],totalEndpoints:0}}}}}function vn(n){let e=n.toLowerCase();return e.includes("exception")||e.includes("fail:")||e.includes("[err]")||e.includes("[error]")}import{execSync as te}from"child_process";function De(n){let e=async()=>{try{return te("git status --porcelain",{cwd:n,encoding:"utf-8"}).trim().length>0}catch{return!1}},t=async()=>{try{return te("git branch --show-current",{cwd:n,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:e,commitAndPush:async(s,r)=>{try{if(!await e())return{success:!0,noChanges:!0};let i=s.length>60?s.substring(0,60)+"...":s,c=`${r?"[agent] (partial)":"[agent]"} ${i}`;te("git add -A",{cwd:n}),te(`git commit -m "${c.replace(/"/g,'\\"')}"`,{cwd:n,encoding:"utf-8"});let l=te("git rev-parse --short HEAD",{cwd:n,encoding:"utf-8"}).trim();try{te("git push",{cwd:n,stdio:"pipe"})}catch(d){let u=d instanceof Error?d.message:String(d);if(u.includes("no upstream branch")){let g=await t();te(`git push --set-upstream origin ${g}`,{cwd:n,stdio:"pipe"})}else return console.error("[Git] Push failed:",u),{success:!0,commitHash:l,error:`Committed but push failed: ${u}`}}return{success:!0,commitHash:l}}catch(i){let a=i instanceof Error?i.message:String(i);return console.error("[Git] Error:",a),{success:!1,error:a}}},getCurrentBranch:t}}import*as se from"@microsoft/signalr";var X=class{constructor(e,t){this.connection=e;this.receiverMethod=t}dispose=()=>{for(let e of this.receiverMethod)this.connection.off(e.methodName,e.method)}},Nt=n=>{if(n==="IDeploymentHub")return ze.Instance;if(n==="IKanbanHub")return Ve.Instance;if(n==="IAgentHub")return Ze.Instance;if(n==="ISlaveHub")return tt.Instance;if(n==="ITestHub")return ot.Instance},Ft=n=>{if(n==="IDeploymentHubClient")return rt.Instance;if(n==="IKanbanBoardClient")return it.Instance;if(n==="IAgentReceiver")return at.Instance;if(n==="IBackofficeReceiver")return ct.Instance;if(n==="ISlaveHubClient")return lt.Instance;if(n==="ITestHubClient")return pt.Instance},ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Je(e)},Je=class{constructor(e){this.connection=e}registerMachine=async(e,t)=>await this.connection.invoke("RegisterMachine",e,t);reportDeploymentStatus=async e=>await this.connection.invoke("ReportDeploymentStatus",e);reportHealth=async e=>await this.connection.invoke("ReportHealth",e);reportMachineStatus=async e=>await this.connection.invoke("ReportMachineStatus",e);sendDeploymentLog=async e=>await this.connection.invoke("SendDeploymentLog",e)},Ve=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Xe(e)},Xe=class{constructor(e){this.connection=e}joinBoard=async e=>await this.connection.invoke("JoinBoard",e);leaveBoard=async e=>await this.connection.invoke("LeaveBoard",e)},Ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new et(e)},et=class{constructor(e){this.connection=e}registerAgent=async e=>await this.connection.invoke("RegisterAgent",e);getSecrets=async()=>await this.connection.invoke("GetSecrets");reportStatus=async(e,t)=>await this.connection.invoke("ReportStatus",e,t);reportSessionOutput=async(e,t,o)=>await this.connection.invoke("ReportSessionOutput",e,t,o);reportSessionCost=async(e,t,o)=>await this.connection.invoke("ReportSessionCost",e,t,o);reportInsight=async(e,t)=>await this.connection.invoke("ReportInsight",e,t);setClaudeSessionId=async(e,t)=>await this.connection.invoke("SetClaudeSessionId",e,t);sessionCompleted=async(e,t,o)=>await this.connection.invoke("SessionCompleted",e,t,o);initSessionCompleted=async(e,t,o,s)=>await this.connection.invoke("InitSessionCompleted",e,t,o,s);reportPreviewBuild=async(e,t,o)=>await this.connection.invoke("ReportPreviewBuild",e,t,o);saveSpecification=async(e,t,o,s)=>await this.connection.invoke("SaveSpecification",e,t,o,s);getSpecification=async(e,t)=>await this.connection.invoke("GetSpecification",e,t);listSpecifications=async e=>await this.connection.invoke("ListSpecifications",e);deleteSpecification=async(e,t)=>await this.connection.invoke("DeleteSpecification",e,t);watchContainer=async e=>await this.connection.invoke("WatchContainer",e);watchSession=async e=>await this.connection.invoke("WatchSession",e);watchSlaves=async()=>await this.connection.invoke("WatchSlaves");unwatchSlaves=async()=>await this.connection.invoke("UnwatchSlaves");sendPrompt=async(e,t,o,s)=>await this.connection.invoke("SendPrompt",e,t,o,s);stopSession=async e=>await this.connection.invoke("StopSession",e);switchSession=async(e,t)=>await this.connection.invoke("SwitchSession",e,t);createSession=async e=>await this.connection.invoke("CreateSession",e)},tt=class n{static Instance=new n;constructor(){}createHubProxy=e=>new nt(e)},nt=class{constructor(e){this.connection=e}register=async(e,t)=>await this.connection.invoke("Register",e,t);getSecrets=async()=>await this.connection.invoke("GetSecrets");containerCreated=async e=>await this.connection.invoke("ContainerCreated",e);containerReady=async e=>await this.connection.invoke("ContainerReady",e);reportCapacity=async e=>await this.connection.invoke("ReportCapacity",e)},ot=class n{static Instance=new n;constructor(){}createHubProxy=e=>new st(e)},st=class{constructor(e){this.connection=e}joinProject=async e=>await this.connection.invoke("JoinProject",e);leaveProject=async e=>await this.connection.invoke("LeaveProject",e)},rt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...i)=>t.deploy(...i),s=(...i)=>t.stop(...i);e.on("Deploy",o),e.on("Stop",s);let r=[{methodName:"Deploy",method:o},{methodName:"Stop",method:s}];return new X(e,r)}},it=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...f)=>t.boardUpdated(...f),s=(...f)=>t.boardDeleted(...f),r=(...f)=>t.columnCreated(...f),i=(...f)=>t.columnUpdated(...f),a=(...f)=>t.columnDeleted(...f),c=(...f)=>t.cardCreated(...f),l=(...f)=>t.cardUpdated(...f),d=(...f)=>t.cardDeleted(...f),u=(...f)=>t.subtaskCreated(...f),g=(...f)=>t.subtaskUpdated(...f),w=(...f)=>t.subtaskDeleted(...f),v=(...f)=>t.noteCreated(...f),b=(...f)=>t.noteUpdated(...f),D=(...f)=>t.noteDeleted(...f);e.on("BoardUpdated",o),e.on("BoardDeleted",s),e.on("ColumnCreated",r),e.on("ColumnUpdated",i),e.on("ColumnDeleted",a),e.on("CardCreated",c),e.on("CardUpdated",l),e.on("CardDeleted",d),e.on("SubtaskCreated",u),e.on("SubtaskUpdated",g),e.on("SubtaskDeleted",w),e.on("NoteCreated",v),e.on("NoteUpdated",b),e.on("NoteDeleted",D);let j=[{methodName:"BoardUpdated",method:o},{methodName:"BoardDeleted",method:s},{methodName:"ColumnCreated",method:r},{methodName:"ColumnUpdated",method:i},{methodName:"ColumnDeleted",method:a},{methodName:"CardCreated",method:c},{methodName:"CardUpdated",method:l},{methodName:"CardDeleted",method:d},{methodName:"SubtaskCreated",method:u},{methodName:"SubtaskUpdated",method:g},{methodName:"SubtaskDeleted",method:w},{methodName:"NoteCreated",method:v},{methodName:"NoteUpdated",method:b},{methodName:"NoteDeleted",method:D}];return new X(e,j)}},at=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...u)=>t.initSession(...u),s=(...u)=>t.runPrompt(...u),r=(...u)=>t.setModel(...u),i=()=>t.stopAgent(),a=()=>t.ping(),c=(...u)=>t.switchSession(...u),l=(...u)=>t.projectPromptUpdated(...u);e.on("InitSession",o),e.on("RunPrompt",s),e.on("SetModel",r),e.on("StopAgent",i),e.on("Ping",a),e.on("SwitchSession",c),e.on("ProjectPromptUpdated",l);let d=[{methodName:"InitSession",method:o},{methodName:"RunPrompt",method:s},{methodName:"SetModel",method:r},{methodName:"StopAgent",method:i},{methodName:"Ping",method:a},{methodName:"SwitchSession",method:c},{methodName:"ProjectPromptUpdated",method:l}];return new X(e,d)}},ct=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...b)=>t.sessionOutput(...b),s=(...b)=>t.sessionCost(...b),r=(...b)=>t.sessionCompleted(...b),i=(...b)=>t.containerStatus(...b),a=(...b)=>t.slaveCapacityUpdate(...b),c=(...b)=>t.previewBuildStatus(...b),l=(...b)=>t.previewReload(...b),d=(...b)=>t.questionsReceived(...b),u=(...b)=>t.specificationUpdated(...b),g=(...b)=>t.singleQuestionReceived(...b),w=(...b)=>t.questionSessionEnded(...b);e.on("SessionOutput",o),e.on("SessionCost",s),e.on("SessionCompleted",r),e.on("ContainerStatus",i),e.on("SlaveCapacityUpdate",a),e.on("PreviewBuildStatus",c),e.on("PreviewReload",l),e.on("QuestionsReceived",d),e.on("SpecificationUpdated",u),e.on("SingleQuestionReceived",g),e.on("QuestionSessionEnded",w);let v=[{methodName:"SessionOutput",method:o},{methodName:"SessionCost",method:s},{methodName:"SessionCompleted",method:r},{methodName:"ContainerStatus",method:i},{methodName:"SlaveCapacityUpdate",method:a},{methodName:"PreviewBuildStatus",method:c},{methodName:"PreviewReload",method:l},{methodName:"QuestionsReceived",method:d},{methodName:"SpecificationUpdated",method:u},{methodName:"SingleQuestionReceived",method:g},{methodName:"QuestionSessionEnded",method:w}];return new X(e,v)}},lt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...i)=>t.startContainer(...i),s=(...i)=>t.terminateContainer(...i);e.on("StartContainer",o),e.on("TerminateContainer",s);let r=[{methodName:"StartContainer",method:o},{methodName:"TerminateContainer",method:s}];return new X(e,r)}},pt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...l)=>t.testRunCreated(...l),s=(...l)=>t.testRunUpdated(...l),r=(...l)=>t.testSuiteCreated(...l),i=(...l)=>t.testCreated(...l),a=(...l)=>t.testUpdated(...l);e.on("TestRunCreated",o),e.on("TestRunUpdated",s),e.on("TestSuiteCreated",r),e.on("TestCreated",i),e.on("TestUpdated",a);let c=[{methodName:"TestRunCreated",method:o},{methodName:"TestRunUpdated",method:s},{methodName:"TestSuiteCreated",method:r},{methodName:"TestCreated",method:i},{methodName:"TestUpdated",method:a}];return new X(e,c)}};var je=class{constructor(e){this.config=e;this.connection=new se.HubConnectionBuilder().withUrl(`${e.masterUrl}/api/hubs/agent`,{headers:{"X-Container-Id":e.containerId,"X-Project-Key":e.projectKey}}).withAutomaticReconnect({nextRetryDelayInMilliseconds:t=>{let o=t.previousRetryCount+1,s=Math.min(1e3*Math.pow(2,t.previousRetryCount),6e4);return console.log(`[SignalR] Reconnect attempt ${o} (will retry in ${s}ms)`),s}}).withServerTimeout(3e5).withKeepAliveInterval(1e4).configureLogging(se.LogLevel.Information).build(),this.hubProxy=Nt("IAgentHub").createHubProxy(this.connection),this.connection.onreconnected(async()=>{console.log("[SignalR] Reconnected to gateway");try{await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Re-registered as agent for container: ${this.config.containerId}`),await this.reportStatus(this.currentStatus,void 0,this.currentSessionId??void 0,this.currentProjectId??void 0)}catch(t){console.error("[SignalR] Failed to re-register agent after reconnect:",t instanceof Error?t.message:t)}}),this.connection.onclose(t=>{console.error("[SignalR] Connection closed:",t?.message),process.exit(1)})}connection;hubProxy;receiverSubscription=null;currentStatus="WarmingUp";currentSessionId=null;currentProjectId=null;tunnelUrl=null;async connect(){console.log(`[SignalR] Connecting to gateway: ${this.config.masterUrl}/api/hubs/agent`),await this.connection.start(),console.log("[SignalR] Connected to gateway!"),await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Registered as agent for container: ${this.config.containerId}`)}registerReceiver(e){this.receiverSubscription&&this.receiverSubscription.dispose(),this.receiverSubscription=Ft("IAgentReceiver").register(this.connection,e)}getHubProxy(){return this.hubProxy}getMasterUrl(){return this.config.masterUrl}getProjectKey(){return this.config.projectKey}getConnection(){return this.connection}setTunnelUrl(e){this.tunnelUrl=e}async reportStatus(e,t,o,s){if(this.currentStatus=e,o&&(this.currentSessionId=o),s&&(this.currentProjectId=s),this.connection.state===se.HubConnectionState.Connected)try{let r={status:e,error:t,sessionId:o,projectId:s,tunnelUrl:this.tunnelUrl??void 0};await this.hubProxy.reportStatus(this.config.containerId,r),console.log(`[Status] ${e}${t?` (${t})`:""}${this.tunnelUrl?` [${this.tunnelUrl}]`:""}${o?` [session: ${o}]`:""}`)}catch(r){console.error("[SignalR] Failed to report status:",r)}}async getSecrets(){let e=await this.hubProxy.getSecrets();if(!e.success)throw new Error(`Failed to get secrets: ${e.error}`);return e.secrets||{}}};function Cn(n){let e=n.match(/name:\s*"([^"]+)"/);if(e)return e[1];let t=n.match(/^#\s+(.+)$/m);return t?t[1].trim():"Untitled Specification"}var Oe=class{constructor(e,t){this.hubProxy=e;this.projectId=t}async saveSpecification(e,t){let o=Cn(t),s=await this.hubProxy.saveSpecification(this.projectId,e,o,t);if(!s.success)throw new Error(s.error||"Failed to save specification")}async getSpecification(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content||null}async listSpecifications(){let e=await this.hubProxy.listSpecifications(this.projectId);return!e.success||!e.specifications?[]:e.specifications.map(t=>({slug:t.slug,name:t.name,status:t.status,version:"1.0.0"}))}async deleteSpecification(e){return(await this.hubProxy.deleteSpecification(this.projectId,e)).success}async specificationExists(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content!=null}};import{exec as _n,execSync as Pn,spawn as Tn}from"child_process";import{promisify as Rn}from"util";import{createHash as Lt}from"crypto";import*as k from"fs";import*as U from"path";var Me=class{domain;accountId;tunnelId;apiToken;zoneId;tunnelApiBase;constructor(){this.domain=process.env.CLOUDFLARE_DEPLOY_DOMAIN||process.env.DEPLOY_DOMAIN||"vibecodementor.net",this.accountId=process.env.CLOUDFLARE_ACCOUNT_ID||"",this.tunnelId=process.env.CLOUDFLARE_TUNNEL_ID||"",this.apiToken=process.env.CLOUDFLARE_API_TOKEN||"",this.zoneId=process.env.CLOUDFLARE_ZONE_ID||"",this.tunnelApiBase=`https://api.cloudflare.com/client/v4/accounts/${this.accountId}/cfd_tunnel/${this.tunnelId}/configurations`}isConfigured(){return!!(this.accountId&&this.tunnelId&&this.apiToken&&this.zoneId)}buildHostname(e){let t=this.domain.split("."),o=t.length>2?t.slice(-2).join("."):this.domain;return`${e}.${o}`}async addRoute(e,t){let o=this.buildHostname(e);if(console.log(`[TunnelManager] Adding route: ${o} -> localhost:${t}`),!this.isConfigured())return console.log("[TunnelManager] Not configured, skipping route addition"),`https://${o}`;try{let s=await this.getConfig(),r=s.ingress.findIndex(a=>a.hostname===o);if(r!==-1)s.ingress[r].service=`http://localhost:${t}`;else{let a=s.ingress.findIndex(l=>!l.hostname),c={hostname:o,service:`http://localhost:${t}`};a!==-1?s.ingress.splice(a,0,c):(s.ingress.push(c),s.ingress.push({service:"http_status:404"}))}await this.putConfig(s),await this.ensureDnsPointsToTunnel(o);let i=`https://${o}`;return console.log(`[TunnelManager] \u2713 Route added: ${i}`),i}catch(s){throw console.error("[TunnelManager] Failed to add route:",s),s}}async removeRoute(e){let t=this.buildHostname(e);if(console.log(`[TunnelManager] Removing route: ${t}`),!this.isConfigured()){console.log("[TunnelManager] Not configured, skipping route removal");return}try{let o=await this.getConfig();o.ingress=o.ingress.filter(s=>s.hostname!==t),o.ingress.some(s=>!s.hostname)||o.ingress.push({service:"http_status:404"}),await this.putConfig(o),console.log(`[TunnelManager] \u2713 Route removed: ${t}`)}catch(o){throw console.error("[TunnelManager] Failed to remove route:",o),o}}async getConfig(){let t=await(await fetch(this.tunnelApiBase,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!t.success)throw new Error(`Cloudflare API error: ${t.errors.map(o=>o.message).join(", ")}`);return t.result?.config||{ingress:[{service:"http_status:404"}]}}async putConfig(e){let o=await(await fetch(this.tunnelApiBase,{method:"PUT",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({config:e})})).json();if(!o.success)throw new Error(`Cloudflare API error: ${o.errors.map(s=>s.message).join(", ")}`);console.log("[TunnelManager] Configuration updated via Cloudflare API")}async ensureDnsPointsToTunnel(e){let t=`${this.tunnelId}.cfargotunnel.com`,o=`https://api.cloudflare.com/client/v4/zones/${this.zoneId}/dns_records`;try{let r=await(await fetch(`${o}?name=${e}&type=CNAME`,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!r.success||!r.result?.length){console.log(`[TunnelManager] No DNS record found for ${e}, skipping DNS update`);return}let i=r.result[0];if(i.content===t){console.log(`[TunnelManager] DNS already points to correct tunnel: ${e}`);return}console.log(`[TunnelManager] Updating DNS: ${e} from ${i.content} to ${t}`);let c=await(await fetch(`${o}/${i.id}`,{method:"PATCH",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({type:"CNAME",name:e,content:t,proxied:!0})})).json();c.success?console.log(`[TunnelManager] \u2713 DNS updated: ${e} -> ${t}`):console.error(`[TunnelManager] Failed to update DNS: ${c.errors.map(l=>l.message).join(", ")}`)}catch(s){console.error(`[TunnelManager] Error updating DNS for ${e}:`,s)}}};var $t=Rn(_n),I=class{start;label;constructor(e){this.label=e,this.start=Date.now(),console.log(`[TIMING] \u23F1 START: ${e}`)}stop(){let e=Date.now()-this.start,t=(e/1e3).toFixed(2);return console.log(`[TIMING] \u2713 DONE: ${this.label} (${t}s)`),e}};function M(n,e={}){return new Promise((t,o)=>{let s=Tn(n,[],{shell:!0,stdio:"inherit",cwd:e.cwd}),r=null;e.timeout&&(r=setTimeout(()=>{s.kill(),o(new Error(`Command timed out: ${n}`))},e.timeout)),s.on("close",i=>{r&&clearTimeout(r),i===0?t():o(new Error(`Command failed with code ${i}: ${n}`))}),s.on("error",i=>{r&&clearTimeout(r),o(i)})})}var Ht="main",Ue=class{constructor(e,t,o,s){this.connection=e;this.serviceManager=t;this.workspaceDir=o;this.gitHubPat=s}currentBranchName=Ht;currentRepoUrl="";lastLockfileHash=null;tunnelManager=null;currentPreviewSubdomain=null;getTunnelManager(){return this.tunnelManager||(this.tunnelManager=new Me),this.tunnelManager}async prewarmWorkspace(){let e=new I("TOTAL INIT"),t=process.env.REPO_URL,o=process.env.BRANCH_NAME||Ht,s=process.env.PREVIEW_SUBDOMAIN,r=process.env.PREVIEW_HOSTNAME,i=process.env.PROJECT_GITHUB_PAT;if(console.log("[Init] Starting single-phase init..."),console.log(`[Init] Repo: ${t?this.extractRepoPath(t):"NOT SET"}`),console.log(`[Init] Branch: ${o}`),console.log(`[Init] Preview: ${s||"none"}`),!t)throw console.error("[Init] \u274C REPO_URL not set - cannot initialize workspace"),new Error("REPO_URL environment variable is required");i&&(this.gitHubPat=i);let a=new I("Clean workspace");Pn("rm -rf /workspace/* /workspace/.* 2>/dev/null || true",{stdio:"inherit"}),a.stop();let c=new I("Clone repository");await this.cloneRepository(t,o),this.currentRepoUrl=t,this.currentBranchName=o,c.stop(),await this.installDependencies(),this.lastLockfileHash=this.getLockfileHash(),console.log("[Init] Starting services...");let l=new I("Start backend (dotnet)");await this.serviceManager.startBackend(),l.stop();let d=new I("Start frontend (vite)");await this.serviceManager.startFrontend(),d.stop();let u=new I("Wait for health check");await this.serviceManager.waitForHealth(3e4),u.stop(),s&&r&&await this.setupPreviewSubdomainFromEnv(s,r),e.stop(),console.log("[Init] \u2713 Workspace ready")}async handleInitSession(e){console.log(`[InitSession] Session ${e.sessionId} starting...`);let t=this.extractRepoPath(e.repoUrl),o=this.extractRepoPath(this.currentRepoUrl),s=t!==o,r=e.branchName!==this.currentBranchName;s||r?(console.log("[InitSession] Repo/branch mismatch - updating workspace..."),console.log(`[InitSession] Current: ${o}@${this.currentBranchName}`),console.log(`[InitSession] Requested: ${t}@${e.branchName}`),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,""),this.handleRepoChange(e).catch(async a=>{let c=a instanceof Error?a.message:"Unknown error";console.error("[InitSession] Background update failed:",c),await this.connection.reportStatus("Error",c,e.sessionId,e.projectId)})):(console.log("[InitSession] Workspace already matches - ready immediately"),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,"")),console.log(`[InitSession] \u2713 Ready for session ${e.sessionId}`)}async setupPreviewSubdomainFromEnv(e,t){let o=new I("Setup preview subdomain");console.log(`[Preview] Setting up preview subdomain: ${e} (${t})`);let s=this.getTunnelManager();if(!s){console.log("[Preview] TunnelManager not available, skipping subdomain setup"),o.stop();return}if(!s.isConfigured()){console.log("[Preview] Cloudflare not configured, skipping subdomain setup"),o.stop();return}try{let r=parseInt(process.env.ALLOCATED_HOST_PORT||"5173"),i=await s.addRoute(e,r);this.currentPreviewSubdomain=e,this.connection.setTunnelUrl(i),o.stop(),console.log(`[Preview] \u2713 Preview subdomain configured: ${i}`)}catch(r){o.stop(),console.error("[Preview] Failed to set up subdomain route:",r)}}async handleRepoChange(e){let t=new I("TOTAL REPO CHANGE");e.gitHubPat&&e.gitHubPat!==this.gitHubPat&&(console.log("[RepoChange] Updating git credentials"),this.gitHubPat=e.gitHubPat,await M(`echo "https://${e.gitHubPat}@github.com" > ~/.git-credentials`));let o=this.extractRepoPath(e.repoUrl),s=this.extractRepoPath(this.currentRepoUrl);if(o!==s){let i=new I("Clone new repository");await this.cloneRepository(e.repoUrl,e.branchName),this.currentRepoUrl=e.repoUrl,this.currentBranchName=e.branchName,i.stop(),await this.installDependencies();let a=new I("Restart services");await this.serviceManager.restartServices("both"),a.stop()}else{let i=new I(`Switch branch to ${e.branchName}`);await this.switchBranch(e.branchName),i.stop(),await this.checkNeedsReinstall()&&await this.installDependencies();let c=new I("Restart services");await this.serviceManager.restartServices("both"),c.stop()}e.previewSubdomain&&e.previewHostname&&await this.setupPreviewSubdomainFromEnv(e.previewSubdomain,e.previewHostname),t.stop(),console.log("[RepoChange] \u2713 Workspace updated")}extractRepoPath(e){let t=e.match(/github\.com[:/]([^/]+\/[^/]+?)(?:\.git)?$/);return t?t[1]:e}async cloneRepository(e,t){console.log(`[Clone] Clearing workspace and cloning ${this.extractRepoPath(e)} @ ${t}`),await M("rm -rf /workspace/* /workspace/.* 2>/dev/null || true"),await M(`git clone -b ${t} "${e}" /workspace`,{timeout:12e4}),await M('cd /workspace && git config user.email "agent@dotnetmentor.se" && git config user.name "Agent"'),console.log("[Clone] \u2713 Repository cloned")}async switchBranch(e){if(console.log(`[Branch] Switching from ${this.currentBranchName} to ${e}`),e===this.currentBranchName)console.log(`[Branch] Already on ${e}, pulling latest...`),await M(`git pull origin ${e}`,{cwd:this.workspaceDir,timeout:6e4});else{await M("git fetch origin",{cwd:this.workspaceDir,timeout:6e4});let{stdout:t}=await $t(`git ls-remote --heads origin ${e}`,{cwd:this.workspaceDir,timeout:3e4});t.trim().length>0?await M(`git checkout -B ${e} origin/${e}`,{cwd:this.workspaceDir,timeout:3e4}):(console.log(`[Branch] Branch ${e} doesn't exist on remote, creating from main...`),await M(`git checkout -b ${e}`,{cwd:this.workspaceDir,timeout:3e4})),this.currentBranchName=e}console.log(`[Branch] \u2713 Now on ${e}`)}getLockfileHash(){let e=U.join(this.workspaceDir,"packages/backoffice-web/package-lock.json");return k.existsSync(e)?Lt("sha256").update(k.readFileSync(e)).digest("hex"):null}async checkNeedsReinstall(){let e=this.getLockfileHash();return!e||!this.lastLockfileHash?!0:e!==this.lastLockfileHash}async checkCodeDiff(e,t){if(e===t)return!1;try{let{stdout:o}=await $t(`git diff ${e}..HEAD --name-only`,{cwd:this.workspaceDir,timeout:3e4}),s=o.trim();if(!s)return console.log(`[CodeDiff] No file differences between ${e} and ${t}`),!1;let r=s.split(`
1290
- `).filter(c=>c.length>0);console.log(`[CodeDiff] ${r.length} files changed between ${e} and ${t}`);let i=[".cs",".tsx",".ts",".json",".csproj"],a=r.some(c=>i.some(l=>c.endsWith(l)));return a&&console.log("[CodeDiff] Found code changes that require service restart"),a}catch(o){return console.error("[CodeDiff] Failed to check diff:",o instanceof Error?o.message:o),!0}}async installDependencies(){let e=new I("TOTAL INSTALL DEPENDENCIES");if(k.existsSync(U.join(this.workspaceDir,"packages/dotnet-api"))){let t=new I("dotnet restore");await M("dotnet restore",{cwd:U.join(this.workspaceDir,"packages/dotnet-api"),timeout:12e4}),t.stop()}if(k.existsSync(U.join(this.workspaceDir,"packages/backoffice-web/package.json"))){let t=U.join(this.workspaceDir,"packages/backoffice-web"),o=U.join(t,"node_modules"),s=U.join(t,"package-lock.json"),r=U.join(o,".lockhash"),i="/opt/cache";k.mkdirSync(i,{recursive:!0});let a=k.existsSync(s)?Lt("sha256").update(k.readFileSync(s)).digest("hex"):null,c=k.existsSync(r)?k.readFileSync(r,"utf-8").trim():null,l=U.join(i,"node_modules","current_hash"),d=k.existsSync(l)?k.readFileSync(l,"utf-8").trim():null,u=d?U.join(i,"node_modules",d):null,g=a?U.join(i,"node_modules",a):null;if(console.log(`[Install] node_modules exists: ${k.existsSync(o)}`),console.log(`[Install] lockHash (current branch): ${a?.substring(0,8)||"null"}`),console.log(`[Install] installedHash: ${c?.substring(0,8)||"null"}`),console.log(`[Install] dockerCacheHash (from image): ${d?.substring(0,8)||"null"}`),console.log(`[Install] exactMatchCache exists: ${g?k.existsSync(g):!1}`),console.log(`[Install] dockerCachedNodeModules exists: ${u?k.existsSync(u):!1}`),!k.existsSync(o)){if(g&&k.existsSync(g)){let v=new I("Copy exact-match cached node_modules");console.log(`[Install] Copying exact-match cached node_modules (hash ${a?.substring(0,8)})...`),await M(`cp -r "${g}" "${o}"`),a&&k.writeFileSync(r,a),c=a,v.stop()}else if(u&&k.existsSync(u)){let v=new I("Copy Docker-cached node_modules (as base)");console.log(`[Install] Copying Docker-cached node_modules as base (hash ${d?.substring(0,8)}, need ${a?.substring(0,8)})...`),await M(`cp -r "${u}" "${o}"`),v.stop()}}let w=!k.existsSync(o)||a&&a!==c;if(console.log(`[Install] needsInstall: ${w}`),!w)console.log("[Install] npm install skipped (cache OK)");else{let v=new I("npm install --prefer-offline");await M("npm install --prefer-offline --no-progress --fund=false",{cwd:t,timeout:18e4}),v.stop(),a&&(k.mkdirSync(o,{recursive:!0}),k.writeFileSync(r,a))}this.lastLockfileHash=a}e.stop()}};function Bt(n,e,t){let o=n.getHubProxy(),s=process.env.CONTAINER_ID||"unknown";return{onAssistantText:async r=>{let i={type:"Text",content:r,isError:!1};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report assistant text:",a)})},onAssistantToolUse:async(r,i)=>{if(["Write","Edit","MultiEdit"].includes(r)){let c=i,l=c.file_path||c.target_file;l&&t.onFileModified(l)}let a={type:"ToolUse",toolName:r,toolInput:JSON.stringify(i),isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report tool use:",c)})},onResult:async(r,i)=>{let a={type:"System",event:"completed",content:r,cost:i,isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report result:",c)})},onError:async r=>{let i={type:"System",event:"error",content:r,isError:!0};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report error:",a)})},onAborted:async()=>{let r={type:"System",event:"aborted",content:"Agent stopped by user",isError:!1};await o.reportSessionOutput(s,e,r).catch(i=>{console.error("Failed to report abort:",i)})},onSystemInit:async r=>{console.log(`[Callbacks] Claude session initialized: ${r}`),t.onClaudeSessionId(r),await o.setClaudeSessionId(e,r).catch(i=>{console.error("Failed to set Claude session ID:",i)})},onRawMessage:r=>{if(r.type==="assistant"&&r.message?.usage){let i=r.message.usage,a={totalCostUsd:In(i),inputTokens:i.input_tokens||0,outputTokens:i.output_tokens||0,cacheCreationTokens:i.cache_creation_input_tokens||0,cacheReadTokens:i.cache_read_input_tokens||0};o.reportSessionCost(s,e,a).catch(c=>{console.error("Failed to report cost:",c)})}}}}var Ne={inputPerMillion:3,outputPerMillion:15,cacheReadPerMillion:.3,cacheCreationPerMillion:3.75};function In(n){return(n.input_tokens||0)/1e6*Ne.inputPerMillion+(n.output_tokens||0)/1e6*Ne.outputPerMillion+(n.cache_read_input_tokens||0)/1e6*Ne.cacheReadPerMillion+(n.cache_creation_input_tokens||0)/1e6*Ne.cacheCreationPerMillion}import*as re from"fs";import*as Kt from"path";function qt(n,e){let t=Kt.join(n,`.agent-questions-${e}.json`);if(!re.existsSync(t))return null;try{let o=re.readFileSync(t,"utf-8");return re.unlinkSync(t),JSON.parse(o)}catch(o){return console.error("Failed to read questions file:",o),null}}async function Gt(n,e,t,o){if(e.size===0)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let s=o?H(o):`${t}/packages/dotnet-api`,r=o?B(o):`${t}/packages/backoffice-web`,i=o?.services?.backend?.extensions??[".cs",".csproj"],a=o?.services?.frontend?.extensions??[".ts",".tsx"],c=s&&[...e].some(g=>g.includes(s)||i.some(w=>g.endsWith(w))),l=r&&[...e].some(g=>g.includes(r)||a.some(w=>g.endsWith(w)));if(!c&&!l)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let d=[];c&&d.push("backend"),l&&d.push("frontend"),console.log(`[TypeCheck] Checking ${d.join(" & ")}...`);let u=[];if(c){let g=await n.checkBackendBuild();!g.success&&g.errors&&u.push(`## Backend Build Errors (in ${s})
1266
+ `).filter(Boolean)}catch(_){return[`Error reading logs: ${_ instanceof Error?_.message:String(_)}`]}};return{startBackend:C,startFrontend:de,stopBackend:ft,stopFrontend:mt,restartServices:zt,checkHealth:$e,waitForHealth:ht,getProcesses:()=>({backend:f,frontend:O}),checkBackendBuild:yt,checkFrontendTypes:bt,runTypeChecks:Jt,tailLogs:St,checkSwaggerEndpoints:async y=>{let m=V.join(e,"swagger.json");if(!R.existsSync(m))return{foundEndpoints:["Error: swagger.json not found"],totalEndpoints:0};try{let S=JSON.parse(R.readFileSync(m,"utf-8")),_=Object.keys(S.paths||{}),x=[];for(let E of _)if(E.toLowerCase().includes(y.toLowerCase())){let P=Object.keys(S.paths[E]);for(let Q of P)x.push(`${Q.toUpperCase()} ${E}`)}return{foundEndpoints:x,totalEndpoints:_.length}}catch(S){return{foundEndpoints:[`Error parsing swagger.json: ${S instanceof Error?S.message:String(S)}`],totalEndpoints:0}}}}}function vn(n){let e=n.toLowerCase();return e.includes("exception")||e.includes("fail:")||e.includes("[err]")||e.includes("[error]")}import{execSync as te}from"child_process";function De(n){let e=async()=>{try{return te("git status --porcelain",{cwd:n,encoding:"utf-8"}).trim().length>0}catch{return!1}},t=async()=>{try{return te("git branch --show-current",{cwd:n,encoding:"utf-8"}).trim()}catch{return"unknown"}};return{hasChanges:e,commitAndPush:async(s,r)=>{try{if(!await e())return{success:!0,noChanges:!0};let i=s.length>60?s.substring(0,60)+"...":s,c=`${r?"[agent] (partial)":"[agent]"} ${i}`;te("git add -A",{cwd:n}),te(`git commit -m "${c.replace(/"/g,'\\"')}"`,{cwd:n,encoding:"utf-8"});let l=te("git rev-parse --short HEAD",{cwd:n,encoding:"utf-8"}).trim();try{te("git push",{cwd:n,stdio:"pipe"})}catch(d){let u=d instanceof Error?d.message:String(d);if(u.includes("no upstream branch")){let g=await t();te(`git push --set-upstream origin ${g}`,{cwd:n,stdio:"pipe"})}else return console.error("[Git] Push failed:",u),{success:!0,commitHash:l,error:`Committed but push failed: ${u}`}}return{success:!0,commitHash:l}}catch(i){let a=i instanceof Error?i.message:String(i);return console.error("[Git] Error:",a),{success:!1,error:a}}},getCurrentBranch:t}}import*as se from"@microsoft/signalr";var X=class{constructor(e,t){this.connection=e;this.receiverMethod=t}dispose=()=>{for(let e of this.receiverMethod)this.connection.off(e.methodName,e.method)}},Nt=n=>{if(n==="IDeploymentHub")return ze.Instance;if(n==="IKanbanHub")return Ve.Instance;if(n==="IAgentHub")return Ze.Instance;if(n==="ISlaveHub")return tt.Instance;if(n==="ITestHub")return ot.Instance},Ft=n=>{if(n==="IDeploymentHubClient")return rt.Instance;if(n==="IKanbanBoardClient")return it.Instance;if(n==="IAgentReceiver")return at.Instance;if(n==="IBackofficeReceiver")return ct.Instance;if(n==="ISlaveHubClient")return lt.Instance;if(n==="ITestHubClient")return pt.Instance},ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Je(e)},Je=class{constructor(e){this.connection=e}registerMachine=async(e,t)=>await this.connection.invoke("RegisterMachine",e,t);reportDeploymentStatus=async e=>await this.connection.invoke("ReportDeploymentStatus",e);reportHealth=async e=>await this.connection.invoke("ReportHealth",e);reportMachineStatus=async e=>await this.connection.invoke("ReportMachineStatus",e);sendDeploymentLog=async e=>await this.connection.invoke("SendDeploymentLog",e)},Ve=class n{static Instance=new n;constructor(){}createHubProxy=e=>new Xe(e)},Xe=class{constructor(e){this.connection=e}joinBoard=async e=>await this.connection.invoke("JoinBoard",e);leaveBoard=async e=>await this.connection.invoke("LeaveBoard",e)},Ze=class n{static Instance=new n;constructor(){}createHubProxy=e=>new et(e)},et=class{constructor(e){this.connection=e}registerAgent=async e=>await this.connection.invoke("RegisterAgent",e);getSecrets=async()=>await this.connection.invoke("GetSecrets");reportStatus=async(e,t)=>await this.connection.invoke("ReportStatus",e,t);reportSessionOutput=async(e,t,o)=>await this.connection.invoke("ReportSessionOutput",e,t,o);reportSessionCost=async(e,t,o)=>await this.connection.invoke("ReportSessionCost",e,t,o);reportInsight=async(e,t)=>await this.connection.invoke("ReportInsight",e,t);setClaudeSessionId=async(e,t)=>await this.connection.invoke("SetClaudeSessionId",e,t);sessionCompleted=async(e,t,o)=>await this.connection.invoke("SessionCompleted",e,t,o);initSessionCompleted=async(e,t,o,s)=>await this.connection.invoke("InitSessionCompleted",e,t,o,s);reportPreviewBuild=async(e,t,o)=>await this.connection.invoke("ReportPreviewBuild",e,t,o);saveSpecification=async(e,t,o,s)=>await this.connection.invoke("SaveSpecification",e,t,o,s);getSpecification=async(e,t)=>await this.connection.invoke("GetSpecification",e,t);listSpecifications=async e=>await this.connection.invoke("ListSpecifications",e);deleteSpecification=async(e,t)=>await this.connection.invoke("DeleteSpecification",e,t);watchContainer=async e=>await this.connection.invoke("WatchContainer",e);watchSession=async e=>await this.connection.invoke("WatchSession",e);watchSlaves=async()=>await this.connection.invoke("WatchSlaves");unwatchSlaves=async()=>await this.connection.invoke("UnwatchSlaves");sendPrompt=async(e,t,o,s)=>await this.connection.invoke("SendPrompt",e,t,o,s);stopSession=async e=>await this.connection.invoke("StopSession",e);switchSession=async(e,t)=>await this.connection.invoke("SwitchSession",e,t);createSession=async e=>await this.connection.invoke("CreateSession",e)},tt=class n{static Instance=new n;constructor(){}createHubProxy=e=>new nt(e)},nt=class{constructor(e){this.connection=e}register=async(e,t)=>await this.connection.invoke("Register",e,t);getSecrets=async()=>await this.connection.invoke("GetSecrets");containerCreated=async e=>await this.connection.invoke("ContainerCreated",e);containerReady=async e=>await this.connection.invoke("ContainerReady",e);reportCapacity=async e=>await this.connection.invoke("ReportCapacity",e)},ot=class n{static Instance=new n;constructor(){}createHubProxy=e=>new st(e)},st=class{constructor(e){this.connection=e}joinProject=async e=>await this.connection.invoke("JoinProject",e);leaveProject=async e=>await this.connection.invoke("LeaveProject",e)},rt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...i)=>t.deploy(...i),s=(...i)=>t.stop(...i);e.on("Deploy",o),e.on("Stop",s);let r=[{methodName:"Deploy",method:o},{methodName:"Stop",method:s}];return new X(e,r)}},it=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...f)=>t.boardUpdated(...f),s=(...f)=>t.boardDeleted(...f),r=(...f)=>t.columnCreated(...f),i=(...f)=>t.columnUpdated(...f),a=(...f)=>t.columnDeleted(...f),c=(...f)=>t.cardCreated(...f),l=(...f)=>t.cardUpdated(...f),d=(...f)=>t.cardDeleted(...f),u=(...f)=>t.subtaskCreated(...f),g=(...f)=>t.subtaskUpdated(...f),w=(...f)=>t.subtaskDeleted(...f),v=(...f)=>t.noteCreated(...f),b=(...f)=>t.noteUpdated(...f),D=(...f)=>t.noteDeleted(...f);e.on("BoardUpdated",o),e.on("BoardDeleted",s),e.on("ColumnCreated",r),e.on("ColumnUpdated",i),e.on("ColumnDeleted",a),e.on("CardCreated",c),e.on("CardUpdated",l),e.on("CardDeleted",d),e.on("SubtaskCreated",u),e.on("SubtaskUpdated",g),e.on("SubtaskDeleted",w),e.on("NoteCreated",v),e.on("NoteUpdated",b),e.on("NoteDeleted",D);let j=[{methodName:"BoardUpdated",method:o},{methodName:"BoardDeleted",method:s},{methodName:"ColumnCreated",method:r},{methodName:"ColumnUpdated",method:i},{methodName:"ColumnDeleted",method:a},{methodName:"CardCreated",method:c},{methodName:"CardUpdated",method:l},{methodName:"CardDeleted",method:d},{methodName:"SubtaskCreated",method:u},{methodName:"SubtaskUpdated",method:g},{methodName:"SubtaskDeleted",method:w},{methodName:"NoteCreated",method:v},{methodName:"NoteUpdated",method:b},{methodName:"NoteDeleted",method:D}];return new X(e,j)}},at=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...u)=>t.initSession(...u),s=(...u)=>t.runPrompt(...u),r=(...u)=>t.setModel(...u),i=()=>t.stopAgent(),a=()=>t.ping(),c=(...u)=>t.switchSession(...u),l=(...u)=>t.projectPromptUpdated(...u);e.on("InitSession",o),e.on("RunPrompt",s),e.on("SetModel",r),e.on("StopAgent",i),e.on("Ping",a),e.on("SwitchSession",c),e.on("ProjectPromptUpdated",l);let d=[{methodName:"InitSession",method:o},{methodName:"RunPrompt",method:s},{methodName:"SetModel",method:r},{methodName:"StopAgent",method:i},{methodName:"Ping",method:a},{methodName:"SwitchSession",method:c},{methodName:"ProjectPromptUpdated",method:l}];return new X(e,d)}},ct=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...b)=>t.sessionOutput(...b),s=(...b)=>t.sessionCost(...b),r=(...b)=>t.sessionCompleted(...b),i=(...b)=>t.containerStatus(...b),a=(...b)=>t.slaveCapacityUpdate(...b),c=(...b)=>t.previewBuildStatus(...b),l=(...b)=>t.previewReload(...b),d=(...b)=>t.questionsReceived(...b),u=(...b)=>t.specificationUpdated(...b),g=(...b)=>t.singleQuestionReceived(...b),w=(...b)=>t.questionSessionEnded(...b);e.on("SessionOutput",o),e.on("SessionCost",s),e.on("SessionCompleted",r),e.on("ContainerStatus",i),e.on("SlaveCapacityUpdate",a),e.on("PreviewBuildStatus",c),e.on("PreviewReload",l),e.on("QuestionsReceived",d),e.on("SpecificationUpdated",u),e.on("SingleQuestionReceived",g),e.on("QuestionSessionEnded",w);let v=[{methodName:"SessionOutput",method:o},{methodName:"SessionCost",method:s},{methodName:"SessionCompleted",method:r},{methodName:"ContainerStatus",method:i},{methodName:"SlaveCapacityUpdate",method:a},{methodName:"PreviewBuildStatus",method:c},{methodName:"PreviewReload",method:l},{methodName:"QuestionsReceived",method:d},{methodName:"SpecificationUpdated",method:u},{methodName:"SingleQuestionReceived",method:g},{methodName:"QuestionSessionEnded",method:w}];return new X(e,v)}},lt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...a)=>t.startContainer(...a),s=(...a)=>t.terminateContainer(...a),r=()=>t.reconnect();e.on("StartContainer",o),e.on("TerminateContainer",s),e.on("Reconnect",r);let i=[{methodName:"StartContainer",method:o},{methodName:"TerminateContainer",method:s},{methodName:"Reconnect",method:r}];return new X(e,i)}},pt=class n{static Instance=new n;constructor(){}register=(e,t)=>{let o=(...l)=>t.testRunCreated(...l),s=(...l)=>t.testRunUpdated(...l),r=(...l)=>t.testSuiteCreated(...l),i=(...l)=>t.testCreated(...l),a=(...l)=>t.testUpdated(...l);e.on("TestRunCreated",o),e.on("TestRunUpdated",s),e.on("TestSuiteCreated",r),e.on("TestCreated",i),e.on("TestUpdated",a);let c=[{methodName:"TestRunCreated",method:o},{methodName:"TestRunUpdated",method:s},{methodName:"TestSuiteCreated",method:r},{methodName:"TestCreated",method:i},{methodName:"TestUpdated",method:a}];return new X(e,c)}};var je=class{constructor(e){this.config=e;this.connection=new se.HubConnectionBuilder().withUrl(`${e.masterUrl}/api/hubs/agent`,{headers:{"X-Container-Id":e.containerId,"X-Project-Key":e.projectKey}}).withAutomaticReconnect({nextRetryDelayInMilliseconds:t=>{let o=t.previousRetryCount+1,s=Math.min(1e3*Math.pow(2,t.previousRetryCount),6e4);return console.log(`[SignalR] Reconnect attempt ${o} (will retry in ${s}ms)`),s}}).withServerTimeout(3e5).withKeepAliveInterval(1e4).configureLogging(se.LogLevel.Information).build(),this.hubProxy=Nt("IAgentHub").createHubProxy(this.connection),this.connection.onreconnected(async()=>{console.log("[SignalR] Reconnected to gateway");try{await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Re-registered as agent for container: ${this.config.containerId}`),await this.reportStatus(this.currentStatus,void 0,this.currentSessionId??void 0,this.currentProjectId??void 0)}catch(t){console.error("[SignalR] Failed to re-register agent after reconnect:",t instanceof Error?t.message:t)}}),this.connection.onclose(t=>{console.error("[SignalR] Connection closed:",t?.message),process.exit(1)})}connection;hubProxy;receiverSubscription=null;currentStatus="WarmingUp";currentSessionId=null;currentProjectId=null;tunnelUrl=null;async connect(){console.log(`[SignalR] Connecting to gateway: ${this.config.masterUrl}/api/hubs/agent`),await this.connection.start(),console.log("[SignalR] Connected to gateway!"),await this.hubProxy.registerAgent(this.config.containerId),console.log(`[SignalR] Registered as agent for container: ${this.config.containerId}`)}registerReceiver(e){this.receiverSubscription&&this.receiverSubscription.dispose(),this.receiverSubscription=Ft("IAgentReceiver").register(this.connection,e)}getHubProxy(){return this.hubProxy}getMasterUrl(){return this.config.masterUrl}getProjectKey(){return this.config.projectKey}getConnection(){return this.connection}setTunnelUrl(e){this.tunnelUrl=e}async reportStatus(e,t,o,s){if(this.currentStatus=e,o&&(this.currentSessionId=o),s&&(this.currentProjectId=s),this.connection.state===se.HubConnectionState.Connected)try{let r={status:e,error:t,sessionId:o,projectId:s,tunnelUrl:this.tunnelUrl??void 0};await this.hubProxy.reportStatus(this.config.containerId,r),console.log(`[Status] ${e}${t?` (${t})`:""}${this.tunnelUrl?` [${this.tunnelUrl}]`:""}${o?` [session: ${o}]`:""}`)}catch(r){console.error("[SignalR] Failed to report status:",r)}}async getSecrets(){let e=await this.hubProxy.getSecrets();if(!e.success)throw new Error(`Failed to get secrets: ${e.error}`);return e.secrets||{}}};function Cn(n){let e=n.match(/name:\s*"([^"]+)"/);if(e)return e[1];let t=n.match(/^#\s+(.+)$/m);return t?t[1].trim():"Untitled Specification"}var Oe=class{constructor(e,t){this.hubProxy=e;this.projectId=t}async saveSpecification(e,t){let o=Cn(t),s=await this.hubProxy.saveSpecification(this.projectId,e,o,t);if(!s.success)throw new Error(s.error||"Failed to save specification")}async getSpecification(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content||null}async listSpecifications(){let e=await this.hubProxy.listSpecifications(this.projectId);return!e.success||!e.specifications?[]:e.specifications.map(t=>({slug:t.slug,name:t.name,status:t.status,version:"1.0.0"}))}async deleteSpecification(e){return(await this.hubProxy.deleteSpecification(this.projectId,e)).success}async specificationExists(e){let t=await this.hubProxy.getSpecification(this.projectId,e);return t.success&&t.content!=null}};import{exec as _n,execSync as Pn,spawn as Tn}from"child_process";import{promisify as Rn}from"util";import{createHash as Lt}from"crypto";import*as k from"fs";import*as M from"path";var Ue=class{domain;accountId;tunnelId;apiToken;zoneId;tunnelApiBase;constructor(){this.domain=process.env.CLOUDFLARE_DEPLOY_DOMAIN||process.env.DEPLOY_DOMAIN||"vibecodementor.net",this.accountId=process.env.CLOUDFLARE_ACCOUNT_ID||"",this.tunnelId=process.env.CLOUDFLARE_TUNNEL_ID||"",this.apiToken=process.env.CLOUDFLARE_API_TOKEN||"",this.zoneId=process.env.CLOUDFLARE_ZONE_ID||"",this.tunnelApiBase=`https://api.cloudflare.com/client/v4/accounts/${this.accountId}/cfd_tunnel/${this.tunnelId}/configurations`}isConfigured(){return!!(this.accountId&&this.tunnelId&&this.apiToken&&this.zoneId)}buildHostname(e){let t=this.domain.split("."),o=t.length>2?t.slice(-2).join("."):this.domain;return`${e}.${o}`}async addRoute(e,t){let o=this.buildHostname(e);if(console.log(`[TunnelManager] Adding route: ${o} -> localhost:${t}`),!this.isConfigured())return console.log("[TunnelManager] Not configured, skipping route addition"),`https://${o}`;try{let s=await this.getConfig(),r=s.ingress.findIndex(a=>a.hostname===o);if(r!==-1)s.ingress[r].service=`http://localhost:${t}`;else{let a=s.ingress.findIndex(l=>!l.hostname),c={hostname:o,service:`http://localhost:${t}`};a!==-1?s.ingress.splice(a,0,c):(s.ingress.push(c),s.ingress.push({service:"http_status:404"}))}await this.putConfig(s),await this.ensureDnsPointsToTunnel(o);let i=`https://${o}`;return console.log(`[TunnelManager] \u2713 Route added: ${i}`),i}catch(s){throw console.error("[TunnelManager] Failed to add route:",s),s}}async removeRoute(e){let t=this.buildHostname(e);if(console.log(`[TunnelManager] Removing route: ${t}`),!this.isConfigured()){console.log("[TunnelManager] Not configured, skipping route removal");return}try{let o=await this.getConfig();o.ingress=o.ingress.filter(s=>s.hostname!==t),o.ingress.some(s=>!s.hostname)||o.ingress.push({service:"http_status:404"}),await this.putConfig(o),console.log(`[TunnelManager] \u2713 Route removed: ${t}`)}catch(o){throw console.error("[TunnelManager] Failed to remove route:",o),o}}async getConfig(){let t=await(await fetch(this.tunnelApiBase,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!t.success)throw new Error(`Cloudflare API error: ${t.errors.map(o=>o.message).join(", ")}`);return t.result?.config||{ingress:[{service:"http_status:404"}]}}async putConfig(e){let o=await(await fetch(this.tunnelApiBase,{method:"PUT",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({config:e})})).json();if(!o.success)throw new Error(`Cloudflare API error: ${o.errors.map(s=>s.message).join(", ")}`);console.log("[TunnelManager] Configuration updated via Cloudflare API")}async ensureDnsPointsToTunnel(e){let t=`${this.tunnelId}.cfargotunnel.com`,o=`https://api.cloudflare.com/client/v4/zones/${this.zoneId}/dns_records`;try{let r=await(await fetch(`${o}?name=${e}&type=CNAME`,{method:"GET",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"}})).json();if(!r.success||!r.result?.length){console.log(`[TunnelManager] No DNS record found for ${e}, skipping DNS update`);return}let i=r.result[0];if(i.content===t){console.log(`[TunnelManager] DNS already points to correct tunnel: ${e}`);return}console.log(`[TunnelManager] Updating DNS: ${e} from ${i.content} to ${t}`);let c=await(await fetch(`${o}/${i.id}`,{method:"PATCH",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({type:"CNAME",name:e,content:t,proxied:!0})})).json();c.success?console.log(`[TunnelManager] \u2713 DNS updated: ${e} -> ${t}`):console.error(`[TunnelManager] Failed to update DNS: ${c.errors.map(l=>l.message).join(", ")}`)}catch(s){console.error(`[TunnelManager] Error updating DNS for ${e}:`,s)}}};var $t=Rn(_n),I=class{start;label;constructor(e){this.label=e,this.start=Date.now(),console.log(`[TIMING] \u23F1 START: ${e}`)}stop(){let e=Date.now()-this.start,t=(e/1e3).toFixed(2);return console.log(`[TIMING] \u2713 DONE: ${this.label} (${t}s)`),e}};function U(n,e={}){return new Promise((t,o)=>{let s=Tn(n,[],{shell:!0,stdio:"inherit",cwd:e.cwd}),r=null;e.timeout&&(r=setTimeout(()=>{s.kill(),o(new Error(`Command timed out: ${n}`))},e.timeout)),s.on("close",i=>{r&&clearTimeout(r),i===0?t():o(new Error(`Command failed with code ${i}: ${n}`))}),s.on("error",i=>{r&&clearTimeout(r),o(i)})})}var Ht="main",Me=class{constructor(e,t,o,s){this.connection=e;this.serviceManager=t;this.workspaceDir=o;this.gitHubPat=s}currentBranchName=Ht;currentRepoUrl="";lastLockfileHash=null;tunnelManager=null;currentPreviewSubdomain=null;getTunnelManager(){return this.tunnelManager||(this.tunnelManager=new Ue),this.tunnelManager}async prewarmWorkspace(){let e=new I("TOTAL INIT"),t=process.env.REPO_URL,o=process.env.BRANCH_NAME||Ht,s=process.env.PREVIEW_SUBDOMAIN,r=process.env.PREVIEW_HOSTNAME,i=process.env.PROJECT_GITHUB_PAT;if(console.log("[Init] Starting single-phase init..."),console.log(`[Init] Repo: ${t?this.extractRepoPath(t):"NOT SET"}`),console.log(`[Init] Branch: ${o}`),console.log(`[Init] Preview: ${s||"none"}`),!t)throw console.error("[Init] \u274C REPO_URL not set - cannot initialize workspace"),new Error("REPO_URL environment variable is required");i&&(this.gitHubPat=i);let a=new I("Clean workspace");Pn("rm -rf /workspace/* /workspace/.* 2>/dev/null || true",{stdio:"inherit"}),a.stop();let c=new I("Clone repository");await this.cloneRepository(t,o),this.currentRepoUrl=t,this.currentBranchName=o,c.stop(),await this.installDependencies(),this.lastLockfileHash=this.getLockfileHash(),console.log("[Init] Starting services...");let l=new I("Start backend (dotnet)");await this.serviceManager.startBackend(),l.stop();let d=new I("Start frontend (vite)");await this.serviceManager.startFrontend(),d.stop();let u=new I("Wait for health check");await this.serviceManager.waitForHealth(3e4),u.stop(),s&&r&&await this.setupPreviewSubdomainFromEnv(s,r),e.stop(),console.log("[Init] \u2713 Workspace ready")}async handleInitSession(e){console.log(`[InitSession] Session ${e.sessionId} starting...`);let t=this.extractRepoPath(e.repoUrl),o=this.extractRepoPath(this.currentRepoUrl),s=t!==o,r=e.branchName!==this.currentBranchName;s||r?(console.log("[InitSession] Repo/branch mismatch - updating workspace..."),console.log(`[InitSession] Current: ${o}@${this.currentBranchName}`),console.log(`[InitSession] Requested: ${t}@${e.branchName}`),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,""),this.handleRepoChange(e).catch(async a=>{let c=a instanceof Error?a.message:"Unknown error";console.error("[InitSession] Background update failed:",c),await this.connection.reportStatus("Error",c,e.sessionId,e.projectId)})):(console.log("[InitSession] Workspace already matches - ready immediately"),await this.connection.reportStatus("Ready",void 0,e.sessionId,e.projectId),await this.connection.getHubProxy().initSessionCompleted(process.env.CONTAINER_ID||"unknown",e.sessionId,!0,"")),console.log(`[InitSession] \u2713 Ready for session ${e.sessionId}`)}async setupPreviewSubdomainFromEnv(e,t){let o=new I("Setup preview subdomain");console.log(`[Preview] Setting up preview subdomain: ${e} (${t})`);let s=this.getTunnelManager();if(!s){console.log("[Preview] TunnelManager not available, skipping subdomain setup"),o.stop();return}if(!s.isConfigured()){console.log("[Preview] Cloudflare not configured, skipping subdomain setup"),o.stop();return}try{let r=parseInt(process.env.ALLOCATED_HOST_PORT||"5173"),i=await s.addRoute(e,r);this.currentPreviewSubdomain=e,this.connection.setTunnelUrl(i),o.stop(),console.log(`[Preview] \u2713 Preview subdomain configured: ${i}`)}catch(r){o.stop(),console.error("[Preview] Failed to set up subdomain route:",r)}}async handleRepoChange(e){let t=new I("TOTAL REPO CHANGE");e.gitHubPat&&e.gitHubPat!==this.gitHubPat&&(console.log("[RepoChange] Updating git credentials"),this.gitHubPat=e.gitHubPat,await U(`echo "https://${e.gitHubPat}@github.com" > ~/.git-credentials`));let o=this.extractRepoPath(e.repoUrl),s=this.extractRepoPath(this.currentRepoUrl);if(o!==s){let i=new I("Clone new repository");await this.cloneRepository(e.repoUrl,e.branchName),this.currentRepoUrl=e.repoUrl,this.currentBranchName=e.branchName,i.stop(),await this.installDependencies();let a=new I("Restart services");await this.serviceManager.restartServices("both"),a.stop()}else{let i=new I(`Switch branch to ${e.branchName}`);await this.switchBranch(e.branchName),i.stop(),await this.checkNeedsReinstall()&&await this.installDependencies();let c=new I("Restart services");await this.serviceManager.restartServices("both"),c.stop()}e.previewSubdomain&&e.previewHostname&&await this.setupPreviewSubdomainFromEnv(e.previewSubdomain,e.previewHostname),t.stop(),console.log("[RepoChange] \u2713 Workspace updated")}extractRepoPath(e){let t=e.match(/github\.com[:/]([^/]+\/[^/]+?)(?:\.git)?$/);return t?t[1]:e}async cloneRepository(e,t){console.log(`[Clone] Clearing workspace and cloning ${this.extractRepoPath(e)} @ ${t}`),await U("rm -rf /workspace/* /workspace/.* 2>/dev/null || true"),await U(`git clone -b ${t} "${e}" /workspace`,{timeout:12e4}),await U('cd /workspace && git config user.email "agent@dotnetmentor.se" && git config user.name "Agent"'),console.log("[Clone] \u2713 Repository cloned")}async switchBranch(e){if(console.log(`[Branch] Switching from ${this.currentBranchName} to ${e}`),e===this.currentBranchName)console.log(`[Branch] Already on ${e}, pulling latest...`),await U(`git pull origin ${e}`,{cwd:this.workspaceDir,timeout:6e4});else{await U("git fetch origin",{cwd:this.workspaceDir,timeout:6e4});let{stdout:t}=await $t(`git ls-remote --heads origin ${e}`,{cwd:this.workspaceDir,timeout:3e4});t.trim().length>0?await U(`git checkout -B ${e} origin/${e}`,{cwd:this.workspaceDir,timeout:3e4}):(console.log(`[Branch] Branch ${e} doesn't exist on remote, creating from main...`),await U(`git checkout -b ${e}`,{cwd:this.workspaceDir,timeout:3e4})),this.currentBranchName=e}console.log(`[Branch] \u2713 Now on ${e}`)}getLockfileHash(){let e=M.join(this.workspaceDir,"packages/backoffice-web/package-lock.json");return k.existsSync(e)?Lt("sha256").update(k.readFileSync(e)).digest("hex"):null}async checkNeedsReinstall(){let e=this.getLockfileHash();return!e||!this.lastLockfileHash?!0:e!==this.lastLockfileHash}async checkCodeDiff(e,t){if(e===t)return!1;try{let{stdout:o}=await $t(`git diff ${e}..HEAD --name-only`,{cwd:this.workspaceDir,timeout:3e4}),s=o.trim();if(!s)return console.log(`[CodeDiff] No file differences between ${e} and ${t}`),!1;let r=s.split(`
1267
+ `).filter(c=>c.length>0);console.log(`[CodeDiff] ${r.length} files changed between ${e} and ${t}`);let i=[".cs",".tsx",".ts",".json",".csproj"],a=r.some(c=>i.some(l=>c.endsWith(l)));return a&&console.log("[CodeDiff] Found code changes that require service restart"),a}catch(o){return console.error("[CodeDiff] Failed to check diff:",o instanceof Error?o.message:o),!0}}async installDependencies(){let e=new I("TOTAL INSTALL DEPENDENCIES");if(k.existsSync(M.join(this.workspaceDir,"packages/dotnet-api"))){let t=new I("dotnet restore");await U("dotnet restore",{cwd:M.join(this.workspaceDir,"packages/dotnet-api"),timeout:12e4}),t.stop()}if(k.existsSync(M.join(this.workspaceDir,"packages/backoffice-web/package.json"))){let t=M.join(this.workspaceDir,"packages/backoffice-web"),o=M.join(t,"node_modules"),s=M.join(t,"package-lock.json"),r=M.join(o,".lockhash"),i="/opt/cache";k.mkdirSync(i,{recursive:!0});let a=k.existsSync(s)?Lt("sha256").update(k.readFileSync(s)).digest("hex"):null,c=k.existsSync(r)?k.readFileSync(r,"utf-8").trim():null,l=M.join(i,"node_modules","current_hash"),d=k.existsSync(l)?k.readFileSync(l,"utf-8").trim():null,u=d?M.join(i,"node_modules",d):null,g=a?M.join(i,"node_modules",a):null;if(console.log(`[Install] node_modules exists: ${k.existsSync(o)}`),console.log(`[Install] lockHash (current branch): ${a?.substring(0,8)||"null"}`),console.log(`[Install] installedHash: ${c?.substring(0,8)||"null"}`),console.log(`[Install] dockerCacheHash (from image): ${d?.substring(0,8)||"null"}`),console.log(`[Install] exactMatchCache exists: ${g?k.existsSync(g):!1}`),console.log(`[Install] dockerCachedNodeModules exists: ${u?k.existsSync(u):!1}`),!k.existsSync(o)){if(g&&k.existsSync(g)){let v=new I("Copy exact-match cached node_modules");console.log(`[Install] Copying exact-match cached node_modules (hash ${a?.substring(0,8)})...`),await U(`cp -r "${g}" "${o}"`),a&&k.writeFileSync(r,a),c=a,v.stop()}else if(u&&k.existsSync(u)){let v=new I("Copy Docker-cached node_modules (as base)");console.log(`[Install] Copying Docker-cached node_modules as base (hash ${d?.substring(0,8)}, need ${a?.substring(0,8)})...`),await U(`cp -r "${u}" "${o}"`),v.stop()}}let w=!k.existsSync(o)||a&&a!==c;if(console.log(`[Install] needsInstall: ${w}`),!w)console.log("[Install] npm install skipped (cache OK)");else{let v=new I("npm install --prefer-offline");await U("npm install --prefer-offline --no-progress --fund=false",{cwd:t,timeout:18e4}),v.stop(),a&&(k.mkdirSync(o,{recursive:!0}),k.writeFileSync(r,a))}this.lastLockfileHash=a}e.stop()}};function Bt(n,e,t){let o=n.getHubProxy(),s=process.env.CONTAINER_ID||"unknown";return{onAssistantText:async r=>{let i={type:"Text",content:r,isError:!1};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report assistant text:",a)})},onAssistantToolUse:async(r,i)=>{if(["Write","Edit","MultiEdit"].includes(r)){let c=i,l=c.file_path||c.target_file;l&&t.onFileModified(l)}let a={type:"ToolUse",toolName:r,toolInput:JSON.stringify(i),isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report tool use:",c)})},onResult:async(r,i)=>{let a={type:"System",event:"completed",content:r,cost:i,isError:!1};await o.reportSessionOutput(s,e,a).catch(c=>{console.error("Failed to report result:",c)})},onError:async r=>{let i={type:"System",event:"error",content:r,isError:!0};await o.reportSessionOutput(s,e,i).catch(a=>{console.error("Failed to report error:",a)})},onAborted:async()=>{let r={type:"System",event:"aborted",content:"Agent stopped by user",isError:!1};await o.reportSessionOutput(s,e,r).catch(i=>{console.error("Failed to report abort:",i)})},onSystemInit:async r=>{console.log(`[Callbacks] Claude session initialized: ${r}`),t.onClaudeSessionId(r),await o.setClaudeSessionId(e,r).catch(i=>{console.error("Failed to set Claude session ID:",i)})},onRawMessage:r=>{if(r.type==="assistant"&&r.message?.usage){let i=r.message.usage,a={totalCostUsd:In(i),inputTokens:i.input_tokens||0,outputTokens:i.output_tokens||0,cacheCreationTokens:i.cache_creation_input_tokens||0,cacheReadTokens:i.cache_read_input_tokens||0};o.reportSessionCost(s,e,a).catch(c=>{console.error("Failed to report cost:",c)})}}}}var Ne={inputPerMillion:3,outputPerMillion:15,cacheReadPerMillion:.3,cacheCreationPerMillion:3.75};function In(n){return(n.input_tokens||0)/1e6*Ne.inputPerMillion+(n.output_tokens||0)/1e6*Ne.outputPerMillion+(n.cache_read_input_tokens||0)/1e6*Ne.cacheReadPerMillion+(n.cache_creation_input_tokens||0)/1e6*Ne.cacheCreationPerMillion}import*as re from"fs";import*as Kt from"path";function qt(n,e){let t=Kt.join(n,`.agent-questions-${e}.json`);if(!re.existsSync(t))return null;try{let o=re.readFileSync(t,"utf-8");return re.unlinkSync(t),JSON.parse(o)}catch(o){return console.error("Failed to read questions file:",o),null}}async function Gt(n,e,t,o){if(e.size===0)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let s=o?H(o):`${t}/packages/dotnet-api`,r=o?B(o):`${t}/packages/backoffice-web`,i=o?.services?.backend?.extensions??[".cs",".csproj"],a=o?.services?.frontend?.extensions??[".ts",".tsx"],c=s&&[...e].some(g=>g.includes(s)||i.some(w=>g.endsWith(w))),l=r&&[...e].some(g=>g.includes(r)||a.some(w=>g.endsWith(w)));if(!c&&!l)return console.log("[TypeCheck] No code changes, skipping type checks"),{passed:!0};let d=[];c&&d.push("backend"),l&&d.push("frontend"),console.log(`[TypeCheck] Checking ${d.join(" & ")}...`);let u=[];if(c){let g=await n.checkBackendBuild();!g.success&&g.errors&&u.push(`## Backend Build Errors (in ${s})
1291
1268
  \`\`\`
1292
1269
  ${g.errors}
1293
1270
  \`\`\``)}if(l){let g=await n.checkFrontendTypes();!g.success&&g.errors&&u.push(`## Frontend Type Errors (in ${r})
@@ -1303,7 +1280,7 @@ ${u.join(`
1303
1280
 
1304
1281
  File paths like "src/..." are relative to the package directory shown in parentheses.`}):(console.log("[TypeCheck] \u2713 All type checks passed"),{passed:!0})}async function ut(n,e,t,o,s){if(!await n.hasChanges()){console.log("[Git] No changes to commit");return}console.log("[Git] Committing changes...");let i=await n.commitAndPush(o,s);if(i.noChanges)console.log("[Git] No changes to commit");else if(i.success){let a=await n.getCurrentBranch();console.log(`[Git] \u2713 Committed ${i.commitHash||""}`),console.log(`[Git] \u2192 Pushed to ${a}`),await e.getHubProxy().reportSessionOutput(process.env.CONTAINER_ID||"unknown",t,{type:"System",event:"committed",content:`Changes committed to ${a}`,isError:!1}).catch(l=>{console.error("[Git] Failed to report commit:",l)})}else console.error(`[Git] \u274C Git error: ${i.error}`)}var Fe=class{constructor(e,t,o,s,r=!0,i=!0,a,c,l=!0){this.connection=e;this.lifecycle=t;this.serviceManager=o;this.workspaceDir=s;this.enableAutoTypecheck=r;this.enableAutoCommit=i;this.setupPlanningTransport=a;this.projectConfig=c;this.useDefaultStack=l;this.gitManager=De(s)}currentSession=null;claudeSessionIdMap=new Map;currentAbortController=null;gitManager;async initSession(e){console.log(`[Receiver] InitSession for session ${e.sessionId}`),this.setupPlanningTransport&&e.projectId&&this.setupPlanningTransport(e.projectId),e.projectId&&await this.fetchProjectPrompt(e.projectId),this.currentSession={sessionId:e.sessionId,projectId:e.projectId,branchName:e.branchName,model:e.model==="auto"?void 0:e.model||"claude-haiku-4-5-20251001",debugMode:e.debugMode},await this.lifecycle.handleInitSession(e),e.prompt&&await this.runPrompt(e.sessionId,e.prompt,e.claudeSessionId)}async runPrompt(e,t,o,s=!1){let r=pe(t);if(console.log(`[Receiver] RunPrompt for session ${e}${o?` (ClaudeSessionId: ${o})`:""}${s?" [DEBUG MODE]":""}: ${r.substring(0,50)}...`),(!this.currentSession||this.currentSession.sessionId!==e)&&(console.log(`[Receiver] Session mismatch: expected ${this.currentSession?.sessionId}, got ${e}, switching...`),await this.switchSession(e),!this.currentSession)){console.error("[Receiver] Failed to create session context");return}let i=this.claudeSessionIdMap.get(e);if(o&&i!==o){console.warn(`[Receiver] Claude session not found in container: ${o}`);let l=this.connection.getHubProxy();await l.reportSessionOutput(process.env.CONTAINER_ID||"unknown",e,{type:"System",event:"sessionNotFound",content:o,isError:!0}),await this.connection.reportStatus("Ready","Session lost - summary needed",e,this.currentSession.projectId),await l.sessionCompleted(process.env.CONTAINER_ID||"unknown",e,0);return}await this.connection.reportStatus("Active",void 0,e,this.currentSession.projectId),this.currentSession.debugMode=s,this.currentAbortController=new AbortController;let a=i||null,c=new Set;try{let l=await Ae(r,{model:this.currentSession.model,sessionId:a||null,projectId:this.currentSession.projectId||null,apiUrl:this.connection.getMasterUrl(),debugLog:!0,debugMode:s,abortController:this.currentAbortController,projectConfig:this.projectConfig,useDefaultStack:this.useDefaultStack,callbacks:Bt(this.connection,e,{onClaudeSessionId:g=>{this.claudeSessionIdMap.set(e,g)},onFileModified:g=>{c.add(g)}})}),d=qt(this.workspaceDir,e);if(d&&d.length>0){console.log(`[Receiver] ${d.length} pending questions found, waiting for user answers`),await this.connection.reportStatus("Ready",void 0,e,this.currentSession.projectId),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,0);return}if(this.enableAutoTypecheck&&!l.error&&!l.aborted){let g=await Gt(this.serviceManager,c,this.workspaceDir,this.projectConfig);if(!g.passed&&g.errorPrompt){console.log("[Receiver] Type errors detected, auto-correcting..."),await this.runPrompt(e,g.errorPrompt,o);return}}this.enableAutoCommit&&!l.error&&!l.aborted?await ut(this.gitManager,this.connection,e,t,!1):(l.error||l.aborted)&&this.enableAutoCommit&&await ut(this.gitManager,this.connection,e,t,!0),await this.connection.reportStatus("Ready",void 0,e,this.currentSession.projectId),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,l.error?1:0)}catch(l){let d=l instanceof Error&&l.name==="AbortError",u=l instanceof Error?l.message:"Unknown error";d?console.log(`[Receiver] Agent aborted for session ${e}`):(console.error("[Receiver] Agent error:",u),await this.connection.reportStatus("Error",u,e,this.currentSession.projectId)),await this.connection.getHubProxy().sessionCompleted(process.env.CONTAINER_ID||"unknown",e,d?0:1)}finally{this.currentAbortController=null}}async setModel(e){console.log(`[Receiver] Model changed to: ${e}`),this.currentSession&&(this.currentSession.model=e==="auto"?void 0:e)}async stopAgent(){console.log("[Receiver] Stop command received"),this.currentAbortController&&this.currentAbortController.abort()}async ping(){await this.connection.getConnection().invoke("Pong",process.env.CONTAINER_ID||"unknown")}async switchSession(e){console.log(`[Receiver] SwitchSession to ${e}`),this.currentSession?this.currentSession.sessionId=e:(console.log(`[Receiver] Creating new session context for ${e}`),this.currentSession={sessionId:e,projectId:process.env.PROJECT_ID||"unknown",branchName:"main",model:void 0,debugMode:!1})}async projectPromptUpdated(e){console.log(`[Receiver] ProjectPromptUpdated received (${e.length} chars)`),We(e)}async fetchProjectPrompt(e){try{let t=this.connection.getMasterUrl(),o=this.connection.getProjectKey(),s=await fetch(`${t}/api/projects/${e}/prompt`,{method:"GET",headers:{"Content-Type":"application/json","X-Project-Key":o}});if(!s.ok){console.log(`[Receiver] Failed to fetch project prompt: ${s.status}`);return}let r=await s.json();r.prompt?(We(r.prompt),console.log(`[Receiver] Initial project prompt loaded (${r.prompt.length} chars)`)):console.log(`[Receiver] No project prompt set for project ${e}`)}catch(t){console.error("[Receiver] Error fetching project prompt:",t)}}};process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT=process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT||"60000";var Wt=Y.dirname(En(import.meta.url));function An(){let n=[Y.resolve(Wt,"../package.json"),Y.resolve(Wt,"../../../package.json")];for(let e of n)try{if(N.existsSync(e)){let t=JSON.parse(N.readFileSync(e,"utf-8"));if(t.name==="glenn-code")return t.version||"unknown"}}catch{}return"unknown"}function xn(n){let{useDefaultStack:e,projectConfig:t,configSource:o,tunnelPort:s,enableAutoTypecheck:r,enableAutoCommit:i}=n;if(console.log(`
1305
1282
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`),console.log("\u2551 AGENT CONFIGURATION \u2551"),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),e?(console.log("\u2551 Mode: DEFAULT STACK (.NET/React) \u2551"),console.log("\u2551 Agents: scaffolding, backend, frontend, debugger, \u2551"),console.log("\u2551 planning, project-context \u2551")):(console.log("\u2551 Mode: CUSTOM STACK (generic agents only) \u2551"),console.log("\u2551 Agents: debugger, planning, project-context \u2551")),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log(`\u2551 Config source: ${o.padEnd(42)}\u2551`),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log("\u2551 SERVICES: \u2551"),t.services?.backend){let a=t.services.backend;console.log(`\u2551 Backend: port ${a.port}, extensions: ${a.extensions?.join(", ")||"N/A"}`.padEnd(62)+"\u2551")}else console.log("\u2551 Backend: not configured \u2551");if(t.services?.frontend){let a=t.services.frontend;console.log(`\u2551 Frontend: port ${a.port}, extensions: ${a.extensions?.join(", ")||"N/A"}`.padEnd(62)+"\u2551")}else console.log("\u2551 Frontend: not configured \u2551");t.techStack&&(console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log("\u2551 TECH STACK: \u2551"),t.techStack.backend?.length&&console.log(`\u2551 Backend: ${t.techStack.backend.join(", ")}`.padEnd(62)+"\u2551"),t.techStack.frontend?.length&&console.log(`\u2551 Frontend: ${t.techStack.frontend.join(", ")}`.padEnd(62)+"\u2551"),t.techStack.patterns?.length&&console.log(`\u2551 Patterns: ${t.techStack.patterns.join(", ")}`.padEnd(62)+"\u2551")),console.log("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"),console.log("\u2551 SETTINGS: \u2551"),console.log(`\u2551 Tunnel port: ${s}`.padEnd(62)+"\u2551"),console.log(`\u2551 Auto-typecheck: ${r?"enabled":"disabled"}`.padEnd(62)+"\u2551"),console.log(`\u2551 Auto-commit: ${i?"enabled":"disabled"}`.padEnd(62)+"\u2551"),console.log(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1306
- `)}var Dn=".sdd/local-signalr-config.json";function gt(n){return Y.join(n,Dn)}function jn(n){try{let e=gt(n);if(!N.existsSync(e))return null;let t=N.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function dt(n,e){let t=Y.dirname(gt(n));N.existsSync(t)||N.mkdirSync(t,{recursive:!0}),N.writeFileSync(gt(n),JSON.stringify(e,null,2))}function Le(){return`local-${Qt.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${Date.now().toString(36)}`}function On(n){return!!(n.version||n.paths||n.services||n.techStack)}function Mn(n,e){return On(n)?{version:n.version||"1.0",paths:n.paths||{workspace:e},services:n.services,techStack:n.techStack,tunnel:n.tunnel,scaffold:n.scaffold,git:n.git}:null}async function Un(n){console.log(`
1283
+ `)}var Dn=".sdd/local-signalr-config.json";function gt(n){return Y.join(n,Dn)}function jn(n){try{let e=gt(n);if(!N.existsSync(e))return null;let t=N.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return null}}function dt(n,e){let t=Y.dirname(gt(n));N.existsSync(t)||N.mkdirSync(t,{recursive:!0}),N.writeFileSync(gt(n),JSON.stringify(e,null,2))}function Le(){return`local-${Qt.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${Date.now().toString(36)}`}function On(n){return!!(n.version||n.paths||n.services||n.techStack)}function Un(n,e){return On(n)?{version:n.version||"1.0",paths:n.paths||{workspace:e},services:n.services,techStack:n.techStack,tunnel:n.tunnel,scaffold:n.scaffold,git:n.git}:null}async function Mn(n){console.log(`
1307
1284
  \u{1F527} Interactive Project Config Builder
1308
1285
  `),console.log(" Answer the following questions to configure your project."),console.log(` Press Enter to skip optional questions.
1309
1286
  `);let{projectType:e}=await K.prompt([{type:"select",name:"projectType",message:"What type of project is this?",choices:[{name:"Frontend only (React, Next.js, Vue, etc.)",value:"frontend"},{name:"Backend only (Node, Python, Go, etc.)",value:"backend"},{name:"Fullstack (Frontend + Backend)",value:"fullstack"},{name:"Skip - use default .NET/React stack",value:"skip"}]}]);if(e==="skip")return null;let t={version:"1.0",paths:{workspace:"."}};if(e==="frontend"||e==="fullstack"){console.log(`
@@ -1311,12 +1288,12 @@ File paths like "src/..." are relative to the package directory shown in parenth
1311
1288
  \u2699\uFE0F Backend Configuration:`);let s=await K.prompt([{type:"input",name:"path",message:"Backend path (relative to workspace):",default:e==="fullstack"?"./backend":"."},{type:"number",name:"port",message:"API server port:",default:8080},{type:"input",name:"startCommand",message:"Start command (or Enter to skip):",default:""},{type:"input",name:"buildCommand",message:"Build command (or Enter to skip):",default:""},{type:"input",name:"extensions",message:"File extensions (comma-separated):",default:".ts,.js"}]);t.paths={workspace:t.paths?.workspace||".",...t.paths,backend:s.path||"."},t.services={...t.services,backend:{port:s.port||8080,startCommand:s.startCommand||void 0,buildCommand:s.buildCommand||void 0,extensions:s.extensions?s.extensions.split(",").map(r=>r.trim()):[".ts",".js"]}},e==="backend"&&(t.tunnel={enabled:!0,port:s.port||8080})}console.log(`
1312
1289
  \u{1F4DA} Tech Stack (optional - helps AI understand your project):`);let{addTechStack:o}=await K.prompt([{type:"confirm",name:"addTechStack",message:"Add tech stack info?",default:!1}]);if(o){let s=await K.prompt([{type:"input",name:"frontend",message:"Frontend technologies (comma-separated, or Enter to skip):",default:""},{type:"input",name:"backend",message:"Backend technologies (comma-separated, or Enter to skip):",default:""},{type:"input",name:"patterns",message:"Patterns/practices (comma-separated, or Enter to skip):",default:""}]);t.techStack={},s.frontend&&(t.techStack.frontend=s.frontend.split(",").map(r=>r.trim())),s.backend&&(t.techStack.backend=s.backend.split(",").map(r=>r.trim())),s.patterns&&(t.techStack.patterns=s.patterns.split(",").map(r=>r.trim()))}return t}function Yt(n){try{let e={},t=n.match(/MASTER_URL=(\S+)/);t&&(e.masterUrl=t[1].trim());let o=n.match(/PROJECT_ID=(\S+)/);o&&(e.projectId=o[1].trim());let s=n.match(/PROJECT_KEY=(\S+)/);return s&&(e.projectKey=s[1].trim()),e.masterUrl&&e.projectId&&e.projectKey?e:null}catch{return null}}async function Nn(n){let e=jn(n);console.log(`
1313
1290
  \u{1F3E0} Local SignalR Mode - Jack into the real system
1314
- `);let t=e?.masterUrl&&e?.projectId&&e?.projectKey,{configMethod:o}=await K.prompt([{type:"select",name:"configMethod",message:"How would you like to configure?",choices:[...t?[{name:`Use saved config (${e.projectId})`,value:"saved"}]:[],{name:"Paste config from frontend",value:"paste"},{name:"Enter manually",value:"manual"},{name:"Build project config interactively",value:"interactive"}]}]);if(o==="saved"&&t){let a=Mn(e,n),c=a?.tunnel?.port||e.tunnelPort||5173;return console.log(`
1291
+ `);let t=e?.masterUrl&&e?.projectId&&e?.projectKey,{configMethod:o}=await K.prompt([{type:"select",name:"configMethod",message:"How would you like to configure?",choices:[...t?[{name:`Use saved config (${e.projectId})`,value:"saved"}]:[],{name:"Paste config from frontend",value:"paste"},{name:"Enter manually",value:"manual"},{name:"Build project config interactively",value:"interactive"}]}]);if(o==="saved"&&t){let a=Un(e,n),c=a?.tunnel?.port||e.tunnelPort||5173;return console.log(`
1315
1292
  \u{1F4CB} Using saved configuration:`),console.log(` Master URL: ${e.masterUrl}`),console.log(` Project ID: ${e.projectId}`),console.log(` Project Key: ${e.projectKey.substring(0,16)}...`),console.log(` Container ID: ${e.containerId}`),console.log(` Tunnel: ${e.enableTunnel!==!1?`enabled (port ${c})`:"disabled"}`),a&&console.log(" Project config: embedded in local-signalr-config.json"),{masterUrl:e.masterUrl,projectId:e.projectId,projectKey:e.projectKey,containerId:e.containerId||Le(),enableTunnel:e.enableTunnel!==!1,tunnelPort:c,embeddedProjectConfig:a}}if(o==="paste"){let{pastedConfig:a}=await K.prompt([{type:"input",name:"pastedConfig",message:"Paste the config (MASTER_URL=... PROJECT_ID=... PROJECT_KEY=...):",validate:g=>Yt(g)?!0:"Could not parse config. Make sure it contains MASTER_URL=..., PROJECT_ID=..., and PROJECT_KEY=..."}]),c=Yt(a),l=e?.containerId||Le(),d=e?.tunnelPort||5173,u={masterUrl:c.masterUrl,projectId:c.projectId,projectKey:c.projectKey,containerId:l,enableTunnel:!0,tunnelPort:d};return console.log(`
1316
1293
  \u{1F4CB} Parsed configuration:`),console.log(` Master URL: ${u.masterUrl}`),console.log(` Project ID: ${u.projectId}`),console.log(` Project Key: ${u.projectKey.substring(0,16)}...`),console.log(` Container ID: ${u.containerId}`),console.log(` Tunnel: enabled (port ${d})`),dt(n,u),{...u,embeddedProjectConfig:null}}if(o==="interactive"){console.log(`
1317
- \u{1F50C} Connection Configuration:`);let a=await K.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:g=>{if(!g.trim())return"Master URL is required";try{return new URL(g),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:g=>g.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:g=>g.trim()?g.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"}]),c=await Un(n),l=e?.containerId||Le(),d=c?.tunnel?.port||e?.tunnelPort||5173,u={masterUrl:a.masterUrl,projectId:a.projectId,projectKey:a.projectKey,containerId:l,enableTunnel:!0,tunnelPort:d,...c&&{version:c.version,paths:c.paths,services:c.services,techStack:c.techStack,tunnel:c.tunnel}};return console.log(`
1294
+ \u{1F50C} Connection Configuration:`);let a=await K.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:g=>{if(!g.trim())return"Master URL is required";try{return new URL(g),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:g=>g.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:g=>g.trim()?g.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"}]),c=await Mn(n),l=e?.containerId||Le(),d=c?.tunnel?.port||e?.tunnelPort||5173,u={masterUrl:a.masterUrl,projectId:a.projectId,projectKey:a.projectKey,containerId:l,enableTunnel:!0,tunnelPort:d,...c&&{version:c.version,paths:c.paths,services:c.services,techStack:c.techStack,tunnel:c.tunnel}};return console.log(`
1318
1295
  \u{1F4CB} Configuration built:`),console.log(` Master URL: ${u.masterUrl}`),console.log(` Project ID: ${u.projectId}`),console.log(` Project Key: ${u.projectKey.substring(0,16)}...`),console.log(` Container ID: ${u.containerId}`),console.log(` Tunnel: enabled (port ${d})`),console.log(c?" Project config: custom stack":" Project config: default .NET/React stack"),dt(n,u),{masterUrl:u.masterUrl,projectId:u.projectId,projectKey:u.projectKey,containerId:u.containerId,enableTunnel:u.enableTunnel,tunnelPort:u.tunnelPort,embeddedProjectConfig:c}}let s=await K.prompt([{type:"input",name:"masterUrl",message:"Master URL (backend):",default:e?.masterUrl||process.env.MASTER_URL||"http://localhost:5338",validate:a=>{if(!a.trim())return"Master URL is required";try{return new URL(a),!0}catch{return"Please enter a valid URL"}}},{type:"input",name:"projectId",message:"Project ID:",default:e?.projectId||process.env.PROJECT_ID,validate:a=>a.trim()?!0:"Project ID is required"},{type:"password",name:"projectKey",message:"Project Key (pk_proj_...):",mask:"*",default:e?.projectKey||process.env.PROJECT_KEY,validate:a=>a.trim()?a.startsWith("pk_proj_")?!0:"Project Key must start with pk_proj_":"Project Key is required"},{type:"input",name:"containerId",message:"Container ID (unique identifier for this agent):",default:e?.containerId||Le()},{type:"confirm",name:"enableTunnel",message:"Enable preview URL reporting (report local dev server URL to backend)?",default:e?.enableTunnel!==!1}]),r=e?.tunnelPort||5173;s.enableTunnel&&(r=(await K.prompt([{type:"number",name:"tunnelPort",message:"Preview port (local dev server port to report):",default:r}])).tunnelPort||r);let i={...s,tunnelPort:r};return dt(n,i),{...i,embeddedProjectConfig:null}}async function Fn(){let n=process.env.LOCAL_MODE==="true",e=process.env.WORKSPACE_DIR||process.cwd(),t=An();console.log(n?`=== Glenn Code v${t} ===`:`=== SignalR Agent v${t} ===`),console.log(`Workspace: ${e}`),n&&!N.existsSync(Y.join(e,".git"))&&console.warn("\u26A0\uFE0F Warning: Not a git repository. Some features may not work.");let o,s,r=!1,i=5173,a=null;if(n){let p=await Nn(e);o={masterUrl:p.masterUrl,containerId:p.containerId,projectKey:p.projectKey,workspaceDir:e},s=p.projectId,r=p.enableTunnel,i=p.tunnelPort,a=p.embeddedProjectConfig,process.env.PROJECT_ID=p.projectId,process.env.CONTAINER_ID=p.containerId,process.env.PROJECT_KEY=p.projectKey}else{let p=process.env.PROJECT_KEY;p||(console.error("\u274C PROJECT_KEY environment variable is required"),process.exit(1)),o={masterUrl:process.env.MASTER_URL||"http://host.docker.internal:5338",containerId:process.env.CONTAINER_ID||"unknown",projectKey:p,workspaceDir:e}}console.log(`
1319
- \u{1F50C} Connecting to backend...`),console.log(` Container ID: ${o.containerId}`),console.log(` Project Key: ${o.projectKey.substring(0,16)}...`),console.log(` Gateway URL: ${o.masterUrl}`);let c=new je(o),l=0,d=10;for(;l<d;)try{await c.connect();break}catch(p){if(l++,console.error(`Connection attempt ${l} failed:`,p instanceof Error?p.message:p),l<d){let C=Math.min(1e3*Math.pow(2,l),3e4);await new Promise(de=>setTimeout(de,C))}}l>=d&&(console.error("\u274C Failed to connect to gateway after max retries"),process.exit(1)),n&&console.log("\u2705 Connected to backend");let u=await c.getSecrets();n?(console.log("\u2705 Handshake complete"),u.gitHubPat&&console.log(" GitHub PAT received from backend")):console.log("\u2705 Secrets received");let g,w=!0,v="default";if(n){if(a)g=a,v="embedded in local-signalr-config.json",w=!1,console.log("[Config] Using embedded project config from local-signalr-config.json");else{let p=await _e({workspaceDir:o.workspaceDir});g=p.config,v=p.source==="file"?p.configPath||".sdd/project-config.json":"default (no config file)",p.source==="file"&&(w=!1)}w||g.tunnel?.port&&(i=g.tunnel.port)}let b=xe({workspaceDir:o.workspaceDir,projectConfig:g});he(b);let D=ce(o.workspaceDir);ie(D);let j=p=>{console.log(`[Planning] Setting up SignalR transport for project ${p}`),D=new Oe(c.getHubProxy(),p),ie(D)};n&&s&&j(s);let f;n?f={prewarmWorkspace:async()=>{console.log("[LocalMode] Skipping prewarm - using existing workspace")},handleInitSession:async p=>{console.log(`[LocalMode] Session init for ${p.sessionId}`),console.log("[LocalMode] Skipping branch switch - using current branch"),await c.reportStatus("Ready",void 0,p.sessionId,p.projectId),await c.getHubProxy().initSessionCompleted(o.containerId,p.sessionId,!0,"")}}:f=new Ue(c,b,o.workspaceDir,u.gitHubPat||null);let O=process.env.DISABLE_AUTO_TYPECHECK!=="true",q=process.env.DISABLE_AUTO_COMMIT!=="true"&&!n;n&&g?xn({useDefaultStack:w,projectConfig:g,configSource:v,tunnelPort:i,enableAutoTypecheck:O,enableAutoCommit:q}):(console.log(`[Config] Auto-typecheck: ${O?"enabled":"disabled"}`),console.log(`[Config] Auto-commit: ${q?"enabled":"disabled"}`));let F=new Fe(c,f,b,o.workspaceDir,O,q,j,g,w);if(c.registerReceiver(F),n){if(r){let p=`http://localhost:${i}`;c.setTunnelUrl(p),console.log(`
1296
+ \u{1F50C} Connecting to backend...`),console.log(` Container ID: ${o.containerId}`),console.log(` Project Key: ${o.projectKey.substring(0,16)}...`),console.log(` Gateway URL: ${o.masterUrl}`);let c=new je(o),l=0,d=10;for(;l<d;)try{await c.connect();break}catch(p){if(l++,console.error(`Connection attempt ${l} failed:`,p instanceof Error?p.message:p),l<d){let C=Math.min(1e3*Math.pow(2,l),3e4);await new Promise(de=>setTimeout(de,C))}}l>=d&&(console.error("\u274C Failed to connect to gateway after max retries"),process.exit(1)),n&&console.log("\u2705 Connected to backend");let u=await c.getSecrets();n?(console.log("\u2705 Handshake complete"),u.gitHubPat&&console.log(" GitHub PAT received from backend")):console.log("\u2705 Secrets received");let g,w=!0,v="default";if(n){if(a)g=a,v="embedded in local-signalr-config.json",w=!1,console.log("[Config] Using embedded project config from local-signalr-config.json");else{let p=await _e({workspaceDir:o.workspaceDir});g=p.config,v=p.source==="file"?p.configPath||".sdd/project-config.json":"default (no config file)",p.source==="file"&&(w=!1)}w||g.tunnel?.port&&(i=g.tunnel.port)}let b=xe({workspaceDir:o.workspaceDir,projectConfig:g});he(b);let D=ce(o.workspaceDir);ie(D);let j=p=>{console.log(`[Planning] Setting up SignalR transport for project ${p}`),D=new Oe(c.getHubProxy(),p),ie(D)};n&&s&&j(s);let f;n?f={prewarmWorkspace:async()=>{console.log("[LocalMode] Skipping prewarm - using existing workspace")},handleInitSession:async p=>{console.log(`[LocalMode] Session init for ${p.sessionId}`),console.log("[LocalMode] Skipping branch switch - using current branch"),await c.reportStatus("Ready",void 0,p.sessionId,p.projectId),await c.getHubProxy().initSessionCompleted(o.containerId,p.sessionId,!0,"")}}:f=new Me(c,b,o.workspaceDir,u.gitHubPat||null);let O=process.env.DISABLE_AUTO_TYPECHECK!=="true",q=process.env.DISABLE_AUTO_COMMIT!=="true"&&!n;n&&g?xn({useDefaultStack:w,projectConfig:g,configSource:v,tunnelPort:i,enableAutoTypecheck:O,enableAutoCommit:q}):(console.log(`[Config] Auto-typecheck: ${O?"enabled":"disabled"}`),console.log(`[Config] Auto-commit: ${q?"enabled":"disabled"}`));let F=new Fe(c,f,b,o.workspaceDir,O,q,j,g,w);if(c.registerReceiver(F),n){if(r){let p=`http://localhost:${i}`;c.setTunnelUrl(p),console.log(`
1320
1297
  \u{1F310} Preview available at: ${p}`)}await c.reportStatus("Ready",void 0,void 0,s),console.log(`
1321
1298
  \u2705 Agent ready and waiting for commands`),console.log(` Open the frontend and select this agent to start working
1322
1299
  `),console.log(` Press Ctrl+C to disconnect