alchemylab 1.4.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/414.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict"
2
+ exports.id=414,exports.ids=[414],exports.modules={9414(t,exports,e){exports.getActionServerPort=function(t){const e=Date.now(),l=!t
3
+ if(l&&null!==i&&u>e-p)return i
4
+ const a=(0,o.join)(t||process.env.ALCHEMY_PROJECT_PATH||process.cwd(),s,c)
5
+ if((0,r.existsSync)(a))try{const t=(0,r.readFileSync)(a,"utf-8"),o=JSON.parse(t)
6
+ if("number"==typeof o.port&&o.port>0&&65536>o.port)return l&&(i=o.port,p=e),o.port}catch{}return l&&(i=n,p=e),n}
7
+ const r=e(9896),o=e(6928),n=3456,s=".alchemy_lab",c="action-server.json"
8
+ let i=null,p=0
9
+ const u=5e3}}
package/dist/cli.js CHANGED
@@ -890,13 +890,13 @@ a.logger.debug("inject_prompt: interpolating",{variables:Object.keys(u),chainInf
890
890
  const h=d(r,u)
891
891
  await Promise.allSettled(c.map((e=>(a.logger.debug("inject_prompt: sending to ancestor",{targetSessionId:e.externalSessionId,agentType:e.agentType}),s(h,e.externalSessionId,a,(0,n.partMeta)("inserted-prompt"))))))},exports.executeNotification=async function(e,t){const s=r(t)
892
892
  if(s)try{const t=s+"/tui/toast"
893
- await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:"Background Work Complete",message:e,variant:"success",duration:8e3})})}catch(e){t.logger.warn("Failed to send toast notification",{error:e+""})}},exports.executeDirectReport=async function(e,t,r){try{const o="http://localhost:3456/api/direct-report",n=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sessionId:e,requestId:t||void 0,status:"success"})})
894
- if(!n.ok)return void r.logger.warn("Direct report failed",{sessionId:e,status:n.status})
895
- const a=await n.json()
896
- if(!a.success||!a.data)return void r.logger.warn("Direct report returned failure",{sessionId:e})
897
- const{notifiedSessions:c,parentExternalSessionId:l}=a.data,d=c||(l?[{externalSessionId:l}]:[])
898
- await Promise.allSettled(d.map((t=>{const o=t.content?.reportStatus||"success",n=t.content?.summary||"Task completed",a=t.content?.details,c=t.messageCode,l=(0,i.formatReportNotification)({messageCode:c,status:o,summary:n,details:a,fromSessionId:e}),d={alchemy:{kind:"worker-report",payload:{messageCode:c,status:o,summary:n,details:a,fromSessionId:e}}}
899
- return s(l,t.externalSessionId,r,d)})))}catch(t){r.logger.warn("Error in direct report",{sessionId:e,error:t+""})}}
893
+ await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:"Background Work Complete",message:e,variant:"success",duration:8e3})})}catch(e){t.logger.warn("Failed to send toast notification",{error:e+""})}},exports.executeDirectReport=async function(e,r,o){try{const{getActionServerPort:n}=await t.e(414).then(t.bind(t,9414)),a=`http://localhost:${n(o.projectPath)}/api/direct-report`,c=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sessionId:e,requestId:r||void 0,status:"success"})})
894
+ if(!c.ok)return void o.logger.warn("Direct report failed",{sessionId:e,status:c.status})
895
+ const l=await c.json()
896
+ if(!l.success||!l.data)return void o.logger.warn("Direct report returned failure",{sessionId:e})
897
+ const{notifiedSessions:d,parentExternalSessionId:u}=l.data,h=d||(u?[{externalSessionId:u}]:[])
898
+ await Promise.allSettled(h.map((t=>{const r=t.content?.reportStatus||"success",n=t.content?.summary||"Task completed",a=t.content?.details,c=t.messageCode,l=(0,i.formatReportNotification)({messageCode:c,status:r,summary:n,details:a,fromSessionId:e}),d={alchemy:{kind:"worker-report",payload:{messageCode:c,status:r,summary:n,details:a,fromSessionId:e}}}
899
+ return s(l,t.externalSessionId,o,d)})))}catch(t){o.logger.warn("Error in direct report",{sessionId:e,error:t+""})}}
900
900
  const o=t(8148),n=t(2272),i=t(7904)},5086(module){"use strict"
901
901
  module.exports=require("ws")},5124(module){"use strict"
902
902
  module.exports=require("winston")},5317(module){"use strict"
@@ -1502,7 +1502,7 @@ for(const e of T){if(!e.enabled)continue
1502
1502
  const t=this.generateAgentMarkdown(e,d,S,k),s=c.join(C,e.name+".md")
1503
1503
  await r.writeFile(s,t,"utf8"),this.logger.info(`Created ${e.name} agent file from remote config`,{agentPath:s,mode:e.mode})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
1504
1504
  else this.logger.warn("No projectId provided - agents must be configured via database")
1505
- const j={$schema:"https://opencode.ai/config.json",model:E,mcp:{[d]:{type:"local",command:[n,u,s],enabled:!0}}}
1505
+ const j={$schema:"https://opencode.ai/config.json",model:E,mcp:{[d]:{type:"local",command:[n,u,s],enabled:!0,timeout:6e5}}}
1506
1506
  if(l&&process.env.REMOTE_SERVER_URL&&(j.mcp[d].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL}),T&&T.length>0){j.agent={}
1507
1507
  for(const e of T){if(!e.enabled)continue
1508
1508
  const t={}
@@ -1342,7 +1342,7 @@ if("setup"===e.commandName){const e=process.config.setup||[]
1342
1342
  if(0===e.length)return a({success:!0}),{content:[{type:"text",text:"No setup commands configured for this test process."}]}
1343
1343
  l=e.map(((e,t)=>({name:"setup-"+(t+1),command:e,timeout:120})))}else{const t=process.config.commands||[],r=t.find((t=>t.name===e.commandName))
1344
1344
  if(!r){const r=t.map((e=>e.name)).join(", ")
1345
- return a({success:!1,error:"command_not_found"}),{content:[{type:"text",text:`Command "${e.commandName}" not found in test process "${process.name}".\nAvailable commands: ${r}\nUse "setup" to run setup commands.`}],isError:!0}}l=[{name:r.name,command:r.command,timeout:r.timeout||300}]}const{execSync:d}=await Promise.resolve().then(t.t.bind(t,5317,23)),u=globalThis.process
1345
+ return a({success:!1,error:"command_not_found"}),{content:[{type:"text",text:`Command "${e.commandName}" not found in test process "${process.name}".\nAvailable commands: ${r}\nUse "setup" to run setup commands.`}],isError:!0}}l=[{name:r.name,command:r.command,timeout:r.timeout||300}]}const{runShellCommand:d}=await Promise.resolve().then(t.bind(t,4600)),u=globalThis.process
1346
1346
  let h=null
1347
1347
  const p=[]
1348
1348
  try{const{sessionIdentity:e}=await Promise.resolve().then(t.bind(t,6688)),r=e.getIdentity()
@@ -1351,9 +1351,8 @@ if(p.push("reqId: "+(t||"NONE")),t){const e=await o.proxyGet(`/api/projects/${n.
1351
1351
  p.push(`runs: ${Array.isArray(r)?r.length:0}, withWorktree: ${s?.id||"NONE"}, path: ${s?.worktreePath||s?.worktree_path||"NONE"}`),s&&(h=s.worktreePath||s.worktree_path,c.info("Using test worktree from request test run",{worktreePath:h,reqId:t}))}}}catch(e){c.warn("Failed to resolve test worktree",{error:(0,r.getErrorMessage)(e)})}if(!h)return a({success:!1,error:"no_worktree"}),{content:[{type:"text",text:`No test worktree found.\n\nDiagnostics:\n${p.length>0?p.map((e=>"\u2022 "+e)).join("\n"):"\u2022 No session identity found"}\n\nEnsure this session is linked to a test process and a test run has been created.`}],isError:!0}
1352
1352
  const g=[]
1353
1353
  for(const t of l){c.info("Executing test command",{name:t.name,command:t.command,timeout:t.timeout,cwd:h})
1354
- try{const e=d(t.command,{cwd:h,timeout:1e3*t.timeout,encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"win32"===process.platform?"cmd.exe":"/bin/sh",env:{...u.env,FORCE_COLOR:"0"}})
1355
- g.push({name:t.name,command:t.command,exitCode:0,output:e.slice(-4e3),timedOut:!1})}catch(r){const n=!0===r.killed,o=((r.stdout||"")+"\n"+(r.stderr||"")).trim().slice(-4e3),s=r.status??1
1356
- if(g.push({name:t.name,command:t.command,exitCode:s,output:o,timedOut:n}),"setup"===e.commandName)break}}const m=g.every((e=>0===e.exitCode)),f=g.map((e=>{const t=e.timedOut?"\u23f1 TIMEOUT":0===e.exitCode?"\u2705 PASS":`\u274c FAIL (exit ${e.exitCode})`
1354
+ const r=await d(t.command,{cwd:h,timeoutMs:1e3*t.timeout,env:{...u.env,FORCE_COLOR:"0"}}),n=(r.stdout+(r.stderr?"\n"+r.stderr:"")).trim().slice(-4e3)
1355
+ if(g.push({name:t.name,command:t.command,exitCode:r.exitCode,output:n,timedOut:r.timedOut}),"setup"===e.commandName&&0!==r.exitCode)break}const m=g.every((e=>0===e.exitCode)),f=g.map((e=>{const t=e.timedOut?"\u23f1 TIMEOUT":0===e.exitCode?"\u2705 PASS":`\u274c FAIL (exit ${e.exitCode})`
1357
1356
  return`### ${e.name}: ${t}\n\`\`\`\n$ ${e.command}\n${e.output}\n\`\`\``})).join("\n\n")
1358
1357
  return a({success:!0}),{content:[{type:"text",text:`## ${"setup"===e.commandName?"Setup":e.commandName} \u2014 ${m?"PASSED":"FAILED"}\n\n${f}`}]}}catch(e){return a({success:!1,error:"execution_error"}),{content:[{type:"text",text:"Failed to execute test command: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleReportTestResults=async function(e,s,i,a,c,l){const d=c.startTimer("tool_execution_time")
1359
1358
  try{const{sessionIdentity:u}=await Promise.resolve().then(t.bind(t,6688)),h=await u.getSessionRequestId(i,a,l?.sessionId)
@@ -2712,14 +2711,15 @@ return p({success:!1,error:"not_found"}),{content:[{type:"text",text:`Command "$
2712
2711
  e.grep&&(I=`${I} 2>&1 | grep -i ${JSON.stringify(e.grep)}`)
2713
2712
  const b=t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath||t.basePath,k=w.cwd?o.default.resolve(b,w.cwd):b
2714
2713
  l.info("Running command",{name:w.name,command:I,cwd:k,configuredCwd:w.cwd})
2715
- let v="",C="",_=0,E=!1
2716
- try{v=(0,n.execSync)(I,{cwd:k,timeout:1e3*(w.timeout||60),encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"win32"===process.platform?"cmd.exe":"/bin/sh",env:{...process.env,FORCE_COLOR:"0"}})||""}catch(e){_=e.status??1,v=e.stdout||"",C=e.stderr||"",e.killed&&(E=!0)}const S=8e3,q=4e3
2717
- v.length>S&&(v="\u2026"+v.slice(-S)),C.length>q&&(C="\u2026"+C.slice(-q))
2718
- const T=E?"TIMEOUT":null,P=[v,C].filter(Boolean).join("\n")||"(no output)"
2719
- let x
2720
- if(u)try{const e=await u.createToolRecord({projectId:t.projectId,type:"run-command",metadata:{name:w.name,command:I,exitCode:_,timedOut:E,status:E?"timeout":0===_?"success":"failed",timestamp:(new Date).toISOString()}})
2721
- x=e?.id}catch(e){l.debug("Failed to create tool record for command run",{error:(0,i.getErrorMessage)(e)})}return p({success:!0}),{content:[{type:"text",text:`## ${w.name}${T?` \u2014 ${T} (killed after ${w.timeout}s)`:""}\n\`\`\`\n$ ${I}\n${P}\n\`\`\`\n\nExit code: ${_}`}],...(0,s.alchemyMeta)({kind:"run-command",toolRecordId:x})}}catch(e){return p({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to run command: "+(0,i.getErrorMessage)(e)}],isError:!0}}}
2722
- const n=t(5317),o=r(t(6928)),s=t(2272),i=t(8824),a=t(6688),c=t(9414)},4466(e,exports,t){"use strict"
2714
+ const v=await(0,n.runShellCommand)(I,{cwd:k,timeoutMs:1e3*(w.timeout||60),env:{...process.env,FORCE_COLOR:"0"}})
2715
+ let C=v.stdout,_=v.stderr
2716
+ const E=v.exitCode,S=v.timedOut,q=8e3,T=4e3
2717
+ C.length>q&&(C="\u2026"+C.slice(-q)),_.length>T&&(_="\u2026"+_.slice(-T))
2718
+ const P=S?"TIMEOUT":null,x=[C,_].filter(Boolean).join("\n")||"(no output)"
2719
+ let j
2720
+ if(u)try{const e=await u.createToolRecord({projectId:t.projectId,type:"run-command",metadata:{name:w.name,command:I,exitCode:E,timedOut:S,status:S?"timeout":0===E?"success":"failed",timestamp:(new Date).toISOString()}})
2721
+ j=e?.id}catch(e){l.debug("Failed to create tool record for command run",{error:(0,i.getErrorMessage)(e)})}return p({success:!0}),{content:[{type:"text",text:`## ${w.name}${P?` \u2014 ${P} (killed after ${w.timeout}s)`:""}\n\`\`\`\n$ ${I}\n${x}\n\`\`\`\n\nExit code: ${E}`}],...(0,s.alchemyMeta)({kind:"run-command",toolRecordId:j})}}catch(e){return p({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to run command: "+(0,i.getErrorMessage)(e)}],isError:!0}}}
2722
+ const n=t(4600),o=r(t(6928)),s=t(2272),i=t(8824),a=t(6688),c=t(9414)},4466(e,exports,t){"use strict"
2723
2723
  function r(e,t){return`${e}#${t.toString().padStart(2,"0")}`}async function n(e,t,n,s){if(exports.TASK_REF_PATTERN.test(e))return e
2724
2724
  const i=e.match(exports.TASK_REF_SHORT_PATTERN)
2725
2725
  if(!i)return e
@@ -2775,7 +2775,12 @@ return(0,i.withAlchemyKind)(f,y)}catch(e){return h.error("Planner ops failed",{e
2775
2775
  const r=t(9860),n=t(8824),o=t(6688),s=t(1725),i=t(2272),a={request:"planner-request","check-status":"planner-check-status",list:"planner-list"}},4600(e,exports,t){"use strict"
2776
2776
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.execAsync=async function(e,t={}){return new Promise(((n,o)=>{const[s,...i]=e.split(" "),a=(0,r.spawn)(s,i,{cwd:t.cwd||process.cwd(),stdio:["pipe","pipe","pipe"]})
2777
2777
  let c="",l=""
2778
- a.stdout?.on("data",(e=>{c+=e.toString()})),a.stderr?.on("data",(e=>{l+=e.toString()})),a.on("close",(e=>{n({stdout:c,stderr:l,exitCode:e||0})})),a.on("error",(t=>{o(Error(`Command failed: ${e}\nError: ${t.message}`))})),t.timeout&&setTimeout((()=>{a.kill(),o(Error("Command timed out: "+e))}),t.timeout)}))}
2778
+ a.stdout?.on("data",(e=>{c+=e.toString()})),a.stderr?.on("data",(e=>{l+=e.toString()})),a.on("close",(e=>{n({stdout:c,stderr:l,exitCode:e||0})})),a.on("error",(t=>{o(Error(`Command failed: ${e}\nError: ${t.message}`))})),t.timeout&&setTimeout((()=>{a.kill(),o(Error("Command timed out: "+e))}),t.timeout)}))},exports.runShellCommand=async function(e,r={}){const{spawn:n}=await Promise.resolve().then(t.t.bind(t,5317,23))
2779
+ return new Promise((t=>{const o="win32"===process.platform,s=n(e,{cwd:r.cwd??process.cwd(),env:r.env,shell:o?"cmd.exe":"/bin/sh",stdio:["pipe","pipe","pipe"]}),i=[],a=[]
2780
+ let c,l,d=!1,u=!1
2781
+ r.timeoutMs&&r.timeoutMs>0&&(l=setTimeout((()=>{d=!0,s.kill("SIGTERM"),c=setTimeout((()=>{u||s.kill("SIGKILL")}),2e3)}),r.timeoutMs)),s.stdout?.on("data",(e=>i.push(e))),s.stderr?.on("data",(e=>a.push(e)))
2782
+ const h=e=>{u||(u=!0,l&&clearTimeout(l),c&&clearTimeout(c),t({stdout:Buffer.concat(i).toString("utf-8"),stderr:Buffer.concat(a).toString("utf-8"),exitCode:e,timedOut:d}))}
2783
+ s.on("close",(e=>h(e??(d?124:1)))),s.on("error",(()=>h(1)))}))}
2779
2784
  const r=t(5317)},4607(e,exports,t){"use strict"
2780
2785
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
2781
2786
  const r=t(6183),n=t(3306)
@@ -5236,11 +5241,11 @@ module.exports=require("net")},9373(e,exports,t){"use strict"
5236
5241
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
5237
5242
  const r=t(6183),n=t(6416)
5238
5243
  exports.def={name:"revert_request",description:"Cancel open/in-progress requests or revert completed requests.\n\nCancel (open/in-progress):\n\u2022 Status set to 'canceled', request becomes read-only\n\u2022 No git operations, branch preserved\n\nRevert (completed):\n\u2022 All file changes undone via git revert\n\u2022 Request/task status set to 'reverted'\n\u2022 Request branch deleted\n\u2022 Fails fast if conflicts detected\n\nPreserved: Documents, git history, tool records (for audit trail).",schema:r.RevertRequestSchema,annotations:{title:"Revert Request",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},hidden:!0,handler(e,t){return(0,n.handleRevertRequest)(e,t.context,t.apiClient,t.logger,t.metrics)}}},9414(e,exports,t){"use strict"
5239
- function r(){const e=Date.now()
5240
- if(null!==c&&d>e-l)return c
5241
- const t=(0,o.join)(process.env.ALCHEMY_PROJECT_PATH||process.cwd(),i,a)
5242
- if((0,n.existsSync)(t))try{const r=(0,n.readFileSync)(t,"utf-8"),o=JSON.parse(r)
5243
- if("number"==typeof o.port&&o.port>0&&65536>o.port)return c=o.port,l=e,c}catch{}return c=s,l=e,c}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getActionServerPort=r,exports.getActionServerBaseUrl=function(){return"http://localhost:"+r()}
5244
+ function r(e){const t=Date.now(),r=!e
5245
+ if(r&&null!==c&&d>t-l)return c
5246
+ const u=(0,o.join)(e||process.env.ALCHEMY_PROJECT_PATH||process.cwd(),i,a)
5247
+ if((0,n.existsSync)(u))try{const e=(0,n.readFileSync)(u,"utf-8"),o=JSON.parse(e)
5248
+ if("number"==typeof o.port&&o.port>0&&65536>o.port)return r&&(c=o.port,l=t),o.port}catch{}return r&&(c=s,l=t),s}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getActionServerPort=r,exports.getActionServerBaseUrl=function(){return"http://localhost:"+r()}
5244
5249
  const n=t(9896),o=t(6928),s=3456,i=".alchemy_lab",a="action-server.json"
5245
5250
  let c=null,l=0
5246
5251
  const d=5e3},9422(e,exports,t){"use strict"
@@ -5579,7 +5584,7 @@ e.r(s)
5579
5584
  var i={}
5580
5585
  t=t||[null,r({}),r([]),r(r)]
5581
5586
  for(var a=2&o&&n;"object"==typeof a&&!~t.indexOf(a);a=r(a))Object.getOwnPropertyNames(a).forEach((e=>i[e]=()=>n[e]))
5582
- return i.default=()=>n,e.d(s,i),s},e.d=(exports,t)=>{for(var r in t)e.o(t,r)&&!e.o(exports,r)&&Object.defineProperty(exports,r,{enumerable:!0,get:t[r]})},e.f={},e.e=t=>Promise.all(Object.keys(e.f).reduce(((r,n)=>(e.f[n](t,r),r)),[])),e.u=e=>e+".js",e.o=(e,t)=>({}.hasOwnProperty.call(e,t)),e.r=exports=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},n={60:1,249:1},e.f.require=t=>{n[t]||(t=>{var r=t.modules,o=t.ids,s=t.runtime
5587
+ return i.default=()=>n,e.d(s,i),s},e.d=(exports,t)=>{for(var r in t)e.o(t,r)&&!e.o(exports,r)&&Object.defineProperty(exports,r,{enumerable:!0,get:t[r]})},e.f={},e.e=t=>Promise.all(Object.keys(e.f).reduce(((r,n)=>(e.f[n](t,r),r)),[])),e.u=e=>e+".js",e.o=(e,t)=>({}.hasOwnProperty.call(e,t)),e.r=exports=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},n={60:1,249:1,414:1},e.f.require=t=>{n[t]||(t=>{var r=t.modules,o=t.ids,s=t.runtime
5583
5588
  for(var i in r)e.o(r,i)&&(e.m[i]=r[i])
5584
5589
  s&&s(e)
5585
5590
  for(var a=0;a<o.length;a++)n[o[a]]=1})(require("./"+e.u(t)))}
package/dist/index.js CHANGED
@@ -1344,7 +1344,7 @@ if("setup"===e.commandName){const e=process.config.setup||[]
1344
1344
  if(0===e.length)return a({success:!0}),{content:[{type:"text",text:"No setup commands configured for this test process."}]}
1345
1345
  l=e.map(((e,t)=>({name:"setup-"+(t+1),command:e,timeout:120})))}else{const t=process.config.commands||[],r=t.find((t=>t.name===e.commandName))
1346
1346
  if(!r){const r=t.map((e=>e.name)).join(", ")
1347
- return a({success:!1,error:"command_not_found"}),{content:[{type:"text",text:`Command "${e.commandName}" not found in test process "${process.name}".\nAvailable commands: ${r}\nUse "setup" to run setup commands.`}],isError:!0}}l=[{name:r.name,command:r.command,timeout:r.timeout||300}]}const{execSync:d}=await Promise.resolve().then(t.t.bind(t,5317,23)),u=globalThis.process
1347
+ return a({success:!1,error:"command_not_found"}),{content:[{type:"text",text:`Command "${e.commandName}" not found in test process "${process.name}".\nAvailable commands: ${r}\nUse "setup" to run setup commands.`}],isError:!0}}l=[{name:r.name,command:r.command,timeout:r.timeout||300}]}const{runShellCommand:d}=await Promise.resolve().then(t.bind(t,4600)),u=globalThis.process
1348
1348
  let h=null
1349
1349
  const p=[]
1350
1350
  try{const{sessionIdentity:e}=await Promise.resolve().then(t.bind(t,6688)),r=e.getIdentity()
@@ -1353,9 +1353,8 @@ if(p.push("reqId: "+(t||"NONE")),t){const e=await o.proxyGet(`/api/projects/${n.
1353
1353
  p.push(`runs: ${Array.isArray(r)?r.length:0}, withWorktree: ${s?.id||"NONE"}, path: ${s?.worktreePath||s?.worktree_path||"NONE"}`),s&&(h=s.worktreePath||s.worktree_path,c.info("Using test worktree from request test run",{worktreePath:h,reqId:t}))}}}catch(e){c.warn("Failed to resolve test worktree",{error:(0,r.getErrorMessage)(e)})}if(!h)return a({success:!1,error:"no_worktree"}),{content:[{type:"text",text:`No test worktree found.\n\nDiagnostics:\n${p.length>0?p.map((e=>"\u2022 "+e)).join("\n"):"\u2022 No session identity found"}\n\nEnsure this session is linked to a test process and a test run has been created.`}],isError:!0}
1354
1354
  const g=[]
1355
1355
  for(const t of l){c.info("Executing test command",{name:t.name,command:t.command,timeout:t.timeout,cwd:h})
1356
- try{const e=d(t.command,{cwd:h,timeout:1e3*t.timeout,encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"win32"===process.platform?"cmd.exe":"/bin/sh",env:{...u.env,FORCE_COLOR:"0"}})
1357
- g.push({name:t.name,command:t.command,exitCode:0,output:e.slice(-4e3),timedOut:!1})}catch(r){const n=!0===r.killed,o=((r.stdout||"")+"\n"+(r.stderr||"")).trim().slice(-4e3),s=r.status??1
1358
- if(g.push({name:t.name,command:t.command,exitCode:s,output:o,timedOut:n}),"setup"===e.commandName)break}}const m=g.every((e=>0===e.exitCode)),f=g.map((e=>{const t=e.timedOut?"\u23f1 TIMEOUT":0===e.exitCode?"\u2705 PASS":`\u274c FAIL (exit ${e.exitCode})`
1356
+ const r=await d(t.command,{cwd:h,timeoutMs:1e3*t.timeout,env:{...u.env,FORCE_COLOR:"0"}}),n=(r.stdout+(r.stderr?"\n"+r.stderr:"")).trim().slice(-4e3)
1357
+ if(g.push({name:t.name,command:t.command,exitCode:r.exitCode,output:n,timedOut:r.timedOut}),"setup"===e.commandName&&0!==r.exitCode)break}const m=g.every((e=>0===e.exitCode)),f=g.map((e=>{const t=e.timedOut?"\u23f1 TIMEOUT":0===e.exitCode?"\u2705 PASS":`\u274c FAIL (exit ${e.exitCode})`
1359
1358
  return`### ${e.name}: ${t}\n\`\`\`\n$ ${e.command}\n${e.output}\n\`\`\``})).join("\n\n")
1360
1359
  return a({success:!0}),{content:[{type:"text",text:`## ${"setup"===e.commandName?"Setup":e.commandName} \u2014 ${m?"PASSED":"FAILED"}\n\n${f}`}]}}catch(e){return a({success:!1,error:"execution_error"}),{content:[{type:"text",text:"Failed to execute test command: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleReportTestResults=async function(e,s,i,a,c,l){const d=c.startTimer("tool_execution_time")
1361
1360
  try{const{sessionIdentity:u}=await Promise.resolve().then(t.bind(t,6688)),h=await u.getSessionRequestId(i,a,l?.sessionId)
@@ -2719,14 +2718,15 @@ return p({success:!1,error:"not_found"}),{content:[{type:"text",text:`Command "$
2719
2718
  e.grep&&(b=`${b} 2>&1 | grep -i ${JSON.stringify(e.grep)}`)
2720
2719
  const I=t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath||t.basePath,k=w.cwd?o.default.resolve(I,w.cwd):I
2721
2720
  l.info("Running command",{name:w.name,command:b,cwd:k,configuredCwd:w.cwd})
2722
- let v="",C="",_=0,E=!1
2723
- try{v=(0,n.execSync)(b,{cwd:k,timeout:1e3*(w.timeout||60),encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"win32"===process.platform?"cmd.exe":"/bin/sh",env:{...process.env,FORCE_COLOR:"0"}})||""}catch(e){_=e.status??1,v=e.stdout||"",C=e.stderr||"",e.killed&&(E=!0)}const S=8e3,T=4e3
2724
- v.length>S&&(v="\u2026"+v.slice(-S)),C.length>T&&(C="\u2026"+C.slice(-T))
2725
- const q=E?"TIMEOUT":null,P=[v,C].filter(Boolean).join("\n")||"(no output)"
2726
- let j
2727
- if(u)try{const e=await u.createToolRecord({projectId:t.projectId,type:"run-command",metadata:{name:w.name,command:b,exitCode:_,timedOut:E,status:E?"timeout":0===_?"success":"failed",timestamp:(new Date).toISOString()}})
2728
- j=e?.id}catch(e){l.debug("Failed to create tool record for command run",{error:(0,i.getErrorMessage)(e)})}return p({success:!0}),{content:[{type:"text",text:`## ${w.name}${q?` \u2014 ${q} (killed after ${w.timeout}s)`:""}\n\`\`\`\n$ ${b}\n${P}\n\`\`\`\n\nExit code: ${_}`}],...(0,s.alchemyMeta)({kind:"run-command",toolRecordId:j})}}catch(e){return p({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to run command: "+(0,i.getErrorMessage)(e)}],isError:!0}}}
2729
- const n=t(5317),o=r(t(6928)),s=t(2272),i=t(8824),a=t(6688),c=t(9414)},4466(e,exports,t){"use strict"
2721
+ const v=await(0,n.runShellCommand)(b,{cwd:k,timeoutMs:1e3*(w.timeout||60),env:{...process.env,FORCE_COLOR:"0"}})
2722
+ let C=v.stdout,_=v.stderr
2723
+ const E=v.exitCode,S=v.timedOut,T=8e3,q=4e3
2724
+ C.length>T&&(C="\u2026"+C.slice(-T)),_.length>q&&(_="\u2026"+_.slice(-q))
2725
+ const P=S?"TIMEOUT":null,j=[C,_].filter(Boolean).join("\n")||"(no output)"
2726
+ let R
2727
+ if(u)try{const e=await u.createToolRecord({projectId:t.projectId,type:"run-command",metadata:{name:w.name,command:b,exitCode:E,timedOut:S,status:S?"timeout":0===E?"success":"failed",timestamp:(new Date).toISOString()}})
2728
+ R=e?.id}catch(e){l.debug("Failed to create tool record for command run",{error:(0,i.getErrorMessage)(e)})}return p({success:!0}),{content:[{type:"text",text:`## ${w.name}${P?` \u2014 ${P} (killed after ${w.timeout}s)`:""}\n\`\`\`\n$ ${b}\n${j}\n\`\`\`\n\nExit code: ${E}`}],...(0,s.alchemyMeta)({kind:"run-command",toolRecordId:R})}}catch(e){return p({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to run command: "+(0,i.getErrorMessage)(e)}],isError:!0}}}
2729
+ const n=t(4600),o=r(t(6928)),s=t(2272),i=t(8824),a=t(6688),c=t(9414)},4466(e,exports,t){"use strict"
2730
2730
  function r(e,t){return`${e}#${t.toString().padStart(2,"0")}`}async function n(e,t,n,s){if(exports.TASK_REF_PATTERN.test(e))return e
2731
2731
  const i=e.match(exports.TASK_REF_SHORT_PATTERN)
2732
2732
  if(!i)return e
@@ -2782,7 +2782,12 @@ return(0,i.withAlchemyKind)(f,y)}catch(e){return h.error("Planner ops failed",{e
2782
2782
  const r=t(9860),n=t(8824),o=t(6688),s=t(1725),i=t(2272),a={request:"planner-request","check-status":"planner-check-status",list:"planner-list"}},4600(e,exports,t){"use strict"
2783
2783
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.execAsync=async function(e,t={}){return new Promise(((n,o)=>{const[s,...i]=e.split(" "),a=(0,r.spawn)(s,i,{cwd:t.cwd||process.cwd(),stdio:["pipe","pipe","pipe"]})
2784
2784
  let c="",l=""
2785
- a.stdout?.on("data",(e=>{c+=e.toString()})),a.stderr?.on("data",(e=>{l+=e.toString()})),a.on("close",(e=>{n({stdout:c,stderr:l,exitCode:e||0})})),a.on("error",(t=>{o(Error(`Command failed: ${e}\nError: ${t.message}`))})),t.timeout&&setTimeout((()=>{a.kill(),o(Error("Command timed out: "+e))}),t.timeout)}))}
2785
+ a.stdout?.on("data",(e=>{c+=e.toString()})),a.stderr?.on("data",(e=>{l+=e.toString()})),a.on("close",(e=>{n({stdout:c,stderr:l,exitCode:e||0})})),a.on("error",(t=>{o(Error(`Command failed: ${e}\nError: ${t.message}`))})),t.timeout&&setTimeout((()=>{a.kill(),o(Error("Command timed out: "+e))}),t.timeout)}))},exports.runShellCommand=async function(e,r={}){const{spawn:n}=await Promise.resolve().then(t.t.bind(t,5317,23))
2786
+ return new Promise((t=>{const o="win32"===process.platform,s=n(e,{cwd:r.cwd??process.cwd(),env:r.env,shell:o?"cmd.exe":"/bin/sh",stdio:["pipe","pipe","pipe"]}),i=[],a=[]
2787
+ let c,l,d=!1,u=!1
2788
+ r.timeoutMs&&r.timeoutMs>0&&(l=setTimeout((()=>{d=!0,s.kill("SIGTERM"),c=setTimeout((()=>{u||s.kill("SIGKILL")}),2e3)}),r.timeoutMs)),s.stdout?.on("data",(e=>i.push(e))),s.stderr?.on("data",(e=>a.push(e)))
2789
+ const h=e=>{u||(u=!0,l&&clearTimeout(l),c&&clearTimeout(c),t({stdout:Buffer.concat(i).toString("utf-8"),stderr:Buffer.concat(a).toString("utf-8"),exitCode:e,timedOut:d}))}
2790
+ s.on("close",(e=>h(e??(d?124:1)))),s.on("error",(()=>h(1)))}))}
2786
2791
  const r=t(5317)},4607(e,exports,t){"use strict"
2787
2792
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
2788
2793
  const r=t(6183),n=t(3306)
@@ -5324,11 +5329,11 @@ module.exports=require("net")},9373(e,exports,t){"use strict"
5324
5329
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
5325
5330
  const r=t(6183),n=t(6416)
5326
5331
  exports.def={name:"revert_request",description:"Cancel open/in-progress requests or revert completed requests.\n\nCancel (open/in-progress):\n\u2022 Status set to 'canceled', request becomes read-only\n\u2022 No git operations, branch preserved\n\nRevert (completed):\n\u2022 All file changes undone via git revert\n\u2022 Request/task status set to 'reverted'\n\u2022 Request branch deleted\n\u2022 Fails fast if conflicts detected\n\nPreserved: Documents, git history, tool records (for audit trail).",schema:r.RevertRequestSchema,annotations:{title:"Revert Request",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},hidden:!0,handler(e,t){return(0,n.handleRevertRequest)(e,t.context,t.apiClient,t.logger,t.metrics)}}},9414(e,exports,t){"use strict"
5327
- function r(){const e=Date.now()
5328
- if(null!==c&&d>e-l)return c
5329
- const t=(0,o.join)(process.env.ALCHEMY_PROJECT_PATH||process.cwd(),i,a)
5330
- if((0,n.existsSync)(t))try{const r=(0,n.readFileSync)(t,"utf-8"),o=JSON.parse(r)
5331
- if("number"==typeof o.port&&o.port>0&&65536>o.port)return c=o.port,l=e,c}catch{}return c=s,l=e,c}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getActionServerPort=r,exports.getActionServerBaseUrl=function(){return"http://localhost:"+r()}
5332
+ function r(e){const t=Date.now(),r=!e
5333
+ if(r&&null!==c&&d>t-l)return c
5334
+ const u=(0,o.join)(e||process.env.ALCHEMY_PROJECT_PATH||process.cwd(),i,a)
5335
+ if((0,n.existsSync)(u))try{const e=(0,n.readFileSync)(u,"utf-8"),o=JSON.parse(e)
5336
+ if("number"==typeof o.port&&o.port>0&&65536>o.port)return r&&(c=o.port,l=t),o.port}catch{}return r&&(c=s,l=t),s}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getActionServerPort=r,exports.getActionServerBaseUrl=function(){return"http://localhost:"+r()}
5332
5337
  const n=t(9896),o=t(6928),s=3456,i=".alchemy_lab",a="action-server.json"
5333
5338
  let c=null,l=0
5334
5339
  const d=5e3},9422(e,exports,t){"use strict"
@@ -5684,7 +5689,7 @@ e.r(s)
5684
5689
  var i={}
5685
5690
  t=t||[null,r({}),r([]),r(r)]
5686
5691
  for(var a=2&o&&n;"object"==typeof a&&!~t.indexOf(a);a=r(a))Object.getOwnPropertyNames(a).forEach((e=>i[e]=()=>n[e]))
5687
- return i.default=()=>n,e.d(s,i),s},e.d=(exports,t)=>{for(var r in t)e.o(t,r)&&!e.o(exports,r)&&Object.defineProperty(exports,r,{enumerable:!0,get:t[r]})},e.f={},e.e=t=>Promise.all(Object.keys(e.f).reduce(((r,n)=>(e.f[n](t,r),r)),[])),e.u=e=>e+".js",e.o=(e,t)=>({}.hasOwnProperty.call(e,t)),e.r=exports=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},n={57:1,60:1,249:1,873:1,939:1},e.f.require=t=>{n[t]||(t=>{var r=t.modules,o=t.ids,s=t.runtime
5692
+ return i.default=()=>n,e.d(s,i),s},e.d=(exports,t)=>{for(var r in t)e.o(t,r)&&!e.o(exports,r)&&Object.defineProperty(exports,r,{enumerable:!0,get:t[r]})},e.f={},e.e=t=>Promise.all(Object.keys(e.f).reduce(((r,n)=>(e.f[n](t,r),r)),[])),e.u=e=>e+".js",e.o=(e,t)=>({}.hasOwnProperty.call(e,t)),e.r=exports=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},n={57:1,60:1,249:1,414:1,873:1,939:1},e.f.require=t=>{n[t]||(t=>{var r=t.modules,o=t.ids,s=t.runtime
5688
5693
  for(var i in r)e.o(r,i)&&(e.m[i]=r[i])
5689
5694
  s&&s(e)
5690
5695
  for(var a=0;a<o.length;a++)n[o[a]]=1})(require("./"+e.u(t)))}
@@ -48,8 +48,8 @@ t.push(` ${s+1}. ${n}`)}))),t.push("Created: "+e.createdAt),t.push("Updated: "+
48
48
  t.push(` [${s}] ${e.id} ${e.title||"(untitled)"}`)}))),t.join("\n")}(i.data)):n(i.error||"Failed to get request")}case"wait-for-idle":{if(!e.sessionId)return n("sessionId is required for wait-for-idle")
49
49
  const i=e.timeoutSeconds||120,o=Date.now()+1e3*i,r=2e3
50
50
  let a=!1
51
- const d=Date.now()+1e4
52
- for(;Date.now()<d&&Date.now()<o;){const s=await t.sendCommand("get-session-status",{sessionId:e.sessionId})
51
+ const c=Date.now()+1e4
52
+ for(;Date.now()<c&&Date.now()<o;){const s=await t.sendCommand("get-session-status",{sessionId:e.sessionId})
53
53
  if(!s.success)return n(s.error||"Failed to check session status")
54
54
  if("busy"===s.data.status){a=!0
55
55
  break}await new Promise((e=>setTimeout(e,500)))}for(;Date.now()<o;){const i=await t.sendCommand("get-session-status",{sessionId:e.sessionId})
@@ -58,7 +58,7 @@ const o=i.data
58
58
  if(("idle"===o.status||"error"===o.status)&&a)return s(`Session ${o.id} is now ${o.status}. Title: ${o.title||"(none)"}`)
59
59
  "busy"===o.status&&(a=!0),await new Promise((e=>setTimeout(e,r)))}return n(`Session ${e.sessionId} did not become idle within ${i}s`)}default:return n("Unknown operation: "+i)}var r}
60
60
  const i=t(1569),o=t(8524)
61
- exports.CLIBridgeOperationsSchema=i.z.object({operation:i.z.enum(["list-sessions","get-messages","send-message","create-session","get-session-status","wait-for-idle","list-notifications","get-notification","respond-to-notification","list-requests","get-request"]).describe("The CLI operation to perform"),sessionId:i.z.string().optional().describe("OpenCode session ID"),message:i.z.string().optional().describe("Message text to send"),agent:i.z.string().optional().describe("Agent to use for the message"),variant:i.z.string().optional().describe('Thinking variant (e.g., "high", "max")'),system:i.z.string().optional().describe("System instructions for the message"),limit:i.z.number().optional().describe("Max number of messages to return"),directory:i.z.string().optional().describe("Directory for the new session"),title:i.z.string().optional().describe("Title for the new session"),parentId:i.z.string().optional().describe("Parent session ID for child sessions"),timeoutSeconds:i.z.number().optional().describe("Max seconds to wait (default: 120)"),includeArchived:i.z.boolean().optional().describe("Include archived sessions"),status:i.z.string().optional().describe('Filter sessions by status (e.g., "idle", "busy")'),agentType:i.z.string().optional().describe("Filter sessions by agent type"),search:i.z.string().optional().describe("Search sessions by title (case-insensitive substring match)"),requestId:i.z.string().optional().describe('Request ID (e.g., "req004643")'),type:i.z.string().optional().describe('Filter requests by type (e.g., "feature", "bugfix")'),offset:i.z.number().optional().describe("Offset for paginated results"),sortBy:i.z.string().optional().describe('Sort field (e.g., "created", "updated")'),sortDirection:i.z.string().optional().describe('Sort direction ("asc" or "desc")'),messageCode:i.z.string().optional().describe('Session message code (e.g., "Q-abc-001" or "R-abc-001")'),responseText:i.z.string().optional().describe("Text answer for free-form questions"),selectedOptions:i.z.array(i.z.string()).optional().describe("Selected option IDs for alternatives questions"),messageType:i.z.string().optional().describe('Filter by message type: "question" or "report"')})},5086(module){module.exports=require("ws")},5106(module){module.exports=require("zod-to-json-schema")},5802(module){module.exports=require("@modelcontextprotocol/sdk/server/stdio.js")},6865(module){module.exports=require("@modelcontextprotocol/sdk/server/index.js")},6982(module){module.exports=require("crypto")},8524(e,exports,t){var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
61
+ exports.CLIBridgeOperationsSchema=i.z.object({operation:i.z.enum(["list-sessions","get-messages","send-message","create-session","get-session-status","wait-for-idle","list-notifications","get-notification","respond-to-notification","list-requests","get-request"]).describe("The CLI operation to perform"),sessionId:i.z.string().optional().describe("OpenCode session ID"),message:i.z.string().optional().describe("Message text to send"),agent:i.z.string().optional().describe("Agent to use for the message"),variant:i.z.string().optional().describe('Thinking variant (e.g., "high", "max")'),system:i.z.string().optional().describe("System instructions for the message"),limit:i.z.number().optional().describe("Max number of messages to return"),directory:i.z.string().optional().describe("Directory for the new session"),title:i.z.string().optional().describe("Title for the new session"),parentId:i.z.string().optional().describe("Parent session ID for child sessions"),timeoutSeconds:i.z.number().optional().describe("Max seconds to wait (default: 120)"),includeArchived:i.z.boolean().optional().describe("Include archived sessions"),status:i.z.string().optional().describe('Filter sessions by status (e.g., "idle", "busy")'),agentType:i.z.string().optional().describe("Filter sessions by agent type"),search:i.z.string().optional().describe("Search sessions by title (case-insensitive substring match)"),requestId:i.z.string().optional().describe('Request ID (e.g., "req004643")'),type:i.z.string().optional().describe('Filter requests by type (e.g., "feature", "bugfix")'),offset:i.z.number().optional().describe("Offset for paginated results"),sortBy:i.z.string().optional().describe('Sort field (e.g., "created", "updated")'),sortDirection:i.z.string().optional().describe('Sort direction ("asc" or "desc")'),messageCode:i.z.string().optional().describe('Session message code (e.g., "Q-abc-001" or "R-abc-001")'),responseText:i.z.string().optional().describe("Text answer for free-form questions"),selectedOptions:i.z.array(i.z.string()).optional().describe("Selected option IDs for alternatives questions"),messageType:i.z.string().optional().describe('Filter by message type: "question" or "report"')})},5086(module){module.exports=require("ws")},5106(module){module.exports=require("zod-to-json-schema")},5802(module){module.exports=require("@modelcontextprotocol/sdk/server/stdio.js")},6865(module){module.exports=require("@modelcontextprotocol/sdk/server/index.js")},6928(module){module.exports=require("path")},6982(module){module.exports=require("crypto")},8524(e,exports,t){var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
62
62
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.CLIBridgeWebSocket=void 0,exports.setCLIBridge=function(e){o=e},exports.getCLIBridge=function(){return o}
63
63
  const n=t(5086),i=s(t(6982))
64
64
  exports.CLIBridgeWebSocket=class{constructor(e,t=3e4){this.wss=null,this.uiClient=null,this.pendingCommands=new Map,this.heartbeatInterval=null,this.logger=e,this.defaultTimeout=t}attach(e){this.wss=new n.WebSocketServer({server:e,path:"/ws/cli-bridge"}),this.wss.on("connection",(e=>{this.logger.info("[CLIBridge] UI client connected"),this.uiClient&&(this.logger.warn("[CLIBridge] Replacing existing UI client connection"),this.uiClient.close(1e3,"Replaced by new connection")),this.uiClient=e,this.startHeartbeat(),e.on("message",(e=>{try{const t=JSON.parse(e.toString())
@@ -71,18 +71,26 @@ try{this.uiClient.send(JSON.stringify(o))}catch(e){clearTimeout(r),this.pendingC
71
71
  t?(clearTimeout(t.timer),this.pendingCommands.delete(e.id),t.resolve(e)):this.logger.warn("[CLIBridge] Received response for unknown command",{id:e.id})}startHeartbeat(){this.stopHeartbeat(),this.heartbeatInterval=setInterval((()=>{this.uiClient&&this.uiClient.readyState===n.WebSocket.OPEN&&this.uiClient.ping()}),3e4)}stopHeartbeat(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null)}close(){this.stopHeartbeat()
72
72
  for(const[e,t]of this.pendingCommands)clearTimeout(t.timer),t.reject(Error("Bridge shutting down")),this.pendingCommands.delete(e)
73
73
  this.uiClient&&(this.uiClient.close(1e3,"Server shutting down"),this.uiClient=null),this.wss&&(this.wss.close(),this.wss=null)}}
74
- let o=null},9150(module){module.exports=require("@modelcontextprotocol/sdk/types.js")}},s={},n={};(()=>{Object.defineProperty(n,"__esModule",{value:!0})
75
- const t=e(6865),s=e(5802),i=e(9150),o=e(5106),r=e(2904),a=e(8524),d=process.argv.slice(2)
76
- let c=3456
77
- for(let e=0;e<d.length;e++)"--port"===d[e]&&d[e+1]&&(c=parseInt(d[e+1],10),e++)
78
- const u=(...e)=>process.stderr.write(`[cli-bridge] ${e.join(" ")}\n`),l=new class{isUIConnected(){return!0}async sendCommand(e,t={},s){return async function(e,t,s){const n=`http://127.0.0.1:${c}/api/cli-bridge/command`,i=new AbortController,o=s||3e4,r=setTimeout((()=>i.abort()),o+5e3)
74
+ let o=null},9150(module){module.exports=require("@modelcontextprotocol/sdk/types.js")},9414(e,exports,t){function s(e){const t=Date.now(),s=!e
75
+ if(s&&null!==c&&u>t-d)return c
76
+ const l=(0,i.join)(e||process.env.ALCHEMY_PROJECT_PATH||process.cwd(),r,a)
77
+ if((0,n.existsSync)(l))try{const e=(0,n.readFileSync)(l,"utf-8"),i=JSON.parse(e)
78
+ if("number"==typeof i.port&&i.port>0&&65536>i.port)return s&&(c=i.port,d=t),i.port}catch{}return s&&(c=o,d=t),o}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getActionServerPort=s,exports.getActionServerBaseUrl=function(){return"http://localhost:"+s()}
79
+ const n=t(9896),i=t(6928),o=3456,r=".alchemy_lab",a="action-server.json"
80
+ let c=null,d=0
81
+ const u=5e3},9896(module){module.exports=require("fs")}},s={},n={};(()=>{Object.defineProperty(n,"__esModule",{value:!0})
82
+ const t=e(6865),s=e(5802),i=e(9150),o=e(5106),r=e(2904),a=e(8524),c=process.argv.slice(2)
83
+ let d=null
84
+ for(let e=0;e<c.length;e++)"--port"===c[e]&&c[e+1]&&(d=parseInt(c[e+1],10),e++)
85
+ if(null===d)try{const{getActionServerPort:t}=e(9414)
86
+ d=t()}catch{d=3456}const u=(...e)=>process.stderr.write(`[cli-bridge] ${e.join(" ")}\n`),l=new class{isUIConnected(){return!0}async sendCommand(e,t={},s){return async function(e,t,s){const n=`http://127.0.0.1:${d}/api/cli-bridge/command`,i=new AbortController,o=s||3e4,r=setTimeout((()=>i.abort()),o+5e3)
79
87
  try{const s=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:e,params:t,timeout:o}),signal:i.signal})
80
88
  if(!s.ok){const e=await s.text()
81
- return{success:!1,error:`Action server error (${s.status}): ${e}`}}return await s.json()}catch(e){return"AbortError"===e.name?{success:!1,error:`Command timed out after ${o/1e3}s`}:{success:!1,error:`Cannot reach action server at port ${c}: ${e.message}`}}finally{clearTimeout(r)}}(e,t,s)}};(0,a.setCLIBridge)(l)
89
+ return{success:!1,error:`Action server error (${s.status}): ${e}`}}return await s.json()}catch(e){return"AbortError"===e.name?{success:!1,error:`Command timed out after ${o/1e3}s`}:{success:!1,error:`Cannot reach action server at port ${d}: ${e.message}`}}finally{clearTimeout(r)}}(e,t,s)}};(0,a.setCLIBridge)(l)
82
90
  const p=new t.Server({name:"alchemy-cli-bridge",version:"1.0.0"},{capabilities:{tools:{}}})
83
91
  p.setRequestHandler(i.ListToolsRequestSchema,(async()=>({tools:[{name:"cli_operations",description:"Interact with OpenCode CLI sessions through alchemy-ui.\n\nOperations:\n list-sessions \u2014 List all active sessions with status and agent type\n Filters: status?, agentType?, search? (title), limit?, includeArchived?\n get-messages \u2014 Get messages from a session (params: sessionId, limit?)\n send-message \u2014 Send a message to a session (params: sessionId, message, agent?, variant?, system?)\n create-session \u2014 Create a new session (params: directory?, title?, parentId?)\n get-session-status \u2014 Check if a session is busy/idle (params: sessionId)\n wait-for-idle \u2014 Wait until a session finishes (params: sessionId, timeoutSeconds?)\n\nRequest operations (no UI connection needed):\n list-requests \u2014 List recent requests (params: status?, type?, limit?, offset?)\n get-request \u2014 Get a specific request with its tasks (params: requestId)\n\nNotification operations (no UI connection needed):\n list-notifications \u2014 List recent notifications (params: sessionId?, messageType?, status?, limit?)\n get-notification \u2014 Get a specific notification by code (params: messageCode)\n respond-to-notification \u2014 Answer a question or acknowledge a report (params: messageCode, responseText?, selectedOptions?)\n\nRequires: alchemy action server running (port 3456) and OpenCode running.",inputSchema:(0,o.zodToJsonSchema)(r.CLIBridgeOperationsSchema)}]}))),p.setRequestHandler(i.CallToolRequestSchema,(async e=>{const{name:t,arguments:s={}}=e.params
84
92
  if("cli_operations"!==t)return{content:[{type:"text",text:"Unknown tool: "+t}],isError:!0}
85
93
  try{const e=r.CLIBridgeOperationsSchema.parse(s)
86
- return await(0,r.handleCLIBridgeOperations)(e)}catch(e){return{content:[{type:"text",text:"Error: "+e.message}],isError:!0}}})),async function(){u("Starting CLI Bridge MCP server"),u("Action server port: "+c)
94
+ return await(0,r.handleCLIBridgeOperations)(e)}catch(e){return{content:[{type:"text",text:"Error: "+e.message}],isError:!0}}})),async function(){u("Starting CLI Bridge MCP server"),u("Action server port: "+d)
87
95
  const e=new s.StdioServerTransport
88
96
  await p.connect(e),u("CLI Bridge MCP server ready (stdio)")}().catch((e=>{u("Fatal error:",e),process.exit(1)})),process.on("SIGTERM",(()=>process.exit(0))),process.on("SIGINT",(()=>process.exit(0)))})(),module.exports=n})()
@@ -1241,7 +1241,7 @@ if("setup"===e.commandName){const e=process.config.setup||[]
1241
1241
  if(0===e.length)return a({success:!0}),{content:[{type:"text",text:"No setup commands configured for this test process."}]}
1242
1242
  l=e.map(((e,t)=>({name:"setup-"+(t+1),command:e,timeout:120})))}else{const t=process.config.commands||[],r=t.find((t=>t.name===e.commandName))
1243
1243
  if(!r){const r=t.map((e=>e.name)).join(", ")
1244
- return a({success:!1,error:"command_not_found"}),{content:[{type:"text",text:`Command "${e.commandName}" not found in test process "${process.name}".\nAvailable commands: ${r}\nUse "setup" to run setup commands.`}],isError:!0}}l=[{name:r.name,command:r.command,timeout:r.timeout||300}]}const{execSync:d}=await Promise.resolve().then(t.t.bind(t,5317,23)),u=globalThis.process
1244
+ return a({success:!1,error:"command_not_found"}),{content:[{type:"text",text:`Command "${e.commandName}" not found in test process "${process.name}".\nAvailable commands: ${r}\nUse "setup" to run setup commands.`}],isError:!0}}l=[{name:r.name,command:r.command,timeout:r.timeout||300}]}const{runShellCommand:d}=await Promise.resolve().then(t.bind(t,4600)),u=globalThis.process
1245
1245
  let h=null
1246
1246
  const p=[]
1247
1247
  try{const{sessionIdentity:e}=await Promise.resolve().then(t.bind(t,6688)),r=e.getIdentity()
@@ -1250,9 +1250,8 @@ if(p.push("reqId: "+(t||"NONE")),t){const e=await o.proxyGet(`/api/projects/${n.
1250
1250
  p.push(`runs: ${Array.isArray(r)?r.length:0}, withWorktree: ${s?.id||"NONE"}, path: ${s?.worktreePath||s?.worktree_path||"NONE"}`),s&&(h=s.worktreePath||s.worktree_path,c.info("Using test worktree from request test run",{worktreePath:h,reqId:t}))}}}catch(e){c.warn("Failed to resolve test worktree",{error:(0,r.getErrorMessage)(e)})}if(!h)return a({success:!1,error:"no_worktree"}),{content:[{type:"text",text:`No test worktree found.\n\nDiagnostics:\n${p.length>0?p.map((e=>"\u2022 "+e)).join("\n"):"\u2022 No session identity found"}\n\nEnsure this session is linked to a test process and a test run has been created.`}],isError:!0}
1251
1251
  const g=[]
1252
1252
  for(const t of l){c.info("Executing test command",{name:t.name,command:t.command,timeout:t.timeout,cwd:h})
1253
- try{const e=d(t.command,{cwd:h,timeout:1e3*t.timeout,encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"win32"===process.platform?"cmd.exe":"/bin/sh",env:{...u.env,FORCE_COLOR:"0"}})
1254
- g.push({name:t.name,command:t.command,exitCode:0,output:e.slice(-4e3),timedOut:!1})}catch(r){const n=!0===r.killed,o=((r.stdout||"")+"\n"+(r.stderr||"")).trim().slice(-4e3),s=r.status??1
1255
- if(g.push({name:t.name,command:t.command,exitCode:s,output:o,timedOut:n}),"setup"===e.commandName)break}}const m=g.every((e=>0===e.exitCode)),f=g.map((e=>{const t=e.timedOut?"\u23f1 TIMEOUT":0===e.exitCode?"\u2705 PASS":`\u274c FAIL (exit ${e.exitCode})`
1253
+ const r=await d(t.command,{cwd:h,timeoutMs:1e3*t.timeout,env:{...u.env,FORCE_COLOR:"0"}}),n=(r.stdout+(r.stderr?"\n"+r.stderr:"")).trim().slice(-4e3)
1254
+ if(g.push({name:t.name,command:t.command,exitCode:r.exitCode,output:n,timedOut:r.timedOut}),"setup"===e.commandName&&0!==r.exitCode)break}const m=g.every((e=>0===e.exitCode)),f=g.map((e=>{const t=e.timedOut?"\u23f1 TIMEOUT":0===e.exitCode?"\u2705 PASS":`\u274c FAIL (exit ${e.exitCode})`
1256
1255
  return`### ${e.name}: ${t}\n\`\`\`\n$ ${e.command}\n${e.output}\n\`\`\``})).join("\n\n")
1257
1256
  return a({success:!0}),{content:[{type:"text",text:`## ${"setup"===e.commandName?"Setup":e.commandName} \u2014 ${m?"PASSED":"FAILED"}\n\n${f}`}]}}catch(e){return a({success:!1,error:"execution_error"}),{content:[{type:"text",text:"Failed to execute test command: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleReportTestResults=async function(e,s,i,a,c,l){const d=c.startTimer("tool_execution_time")
1258
1257
  try{const{sessionIdentity:u}=await Promise.resolve().then(t.bind(t,6688)),h=await u.getSessionRequestId(i,a,l?.sessionId)
@@ -2488,14 +2487,15 @@ return p({success:!1,error:"not_found"}),{content:[{type:"text",text:`Command "$
2488
2487
  e.grep&&(b=`${b} 2>&1 | grep -i ${JSON.stringify(e.grep)}`)
2489
2488
  const I=t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath||t.basePath,k=w.cwd?o.default.resolve(I,w.cwd):I
2490
2489
  l.info("Running command",{name:w.name,command:b,cwd:k,configuredCwd:w.cwd})
2491
- let v="",C="",_=0,E=!1
2492
- try{v=(0,n.execSync)(b,{cwd:k,timeout:1e3*(w.timeout||60),encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"win32"===process.platform?"cmd.exe":"/bin/sh",env:{...process.env,FORCE_COLOR:"0"}})||""}catch(e){_=e.status??1,v=e.stdout||"",C=e.stderr||"",e.killed&&(E=!0)}const S=8e3,q=4e3
2493
- v.length>S&&(v="\u2026"+v.slice(-S)),C.length>q&&(C="\u2026"+C.slice(-q))
2494
- const T=E?"TIMEOUT":null,x=[v,C].filter(Boolean).join("\n")||"(no output)"
2495
- let P
2496
- if(u)try{const e=await u.createToolRecord({projectId:t.projectId,type:"run-command",metadata:{name:w.name,command:b,exitCode:_,timedOut:E,status:E?"timeout":0===_?"success":"failed",timestamp:(new Date).toISOString()}})
2497
- P=e?.id}catch(e){l.debug("Failed to create tool record for command run",{error:(0,i.getErrorMessage)(e)})}return p({success:!0}),{content:[{type:"text",text:`## ${w.name}${T?` \u2014 ${T} (killed after ${w.timeout}s)`:""}\n\`\`\`\n$ ${b}\n${x}\n\`\`\`\n\nExit code: ${_}`}],...(0,s.alchemyMeta)({kind:"run-command",toolRecordId:P})}}catch(e){return p({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to run command: "+(0,i.getErrorMessage)(e)}],isError:!0}}}
2498
- const n=t(5317),o=r(t(6928)),s=t(2272),i=t(8824),a=t(6688),c=t(9414)},4466(e,exports,t){"use strict"
2490
+ const v=await(0,n.runShellCommand)(b,{cwd:k,timeoutMs:1e3*(w.timeout||60),env:{...process.env,FORCE_COLOR:"0"}})
2491
+ let C=v.stdout,_=v.stderr
2492
+ const E=v.exitCode,S=v.timedOut,q=8e3,T=4e3
2493
+ C.length>q&&(C="\u2026"+C.slice(-q)),_.length>T&&(_="\u2026"+_.slice(-T))
2494
+ const x=S?"TIMEOUT":null,P=[C,_].filter(Boolean).join("\n")||"(no output)"
2495
+ let R
2496
+ if(u)try{const e=await u.createToolRecord({projectId:t.projectId,type:"run-command",metadata:{name:w.name,command:b,exitCode:E,timedOut:S,status:S?"timeout":0===E?"success":"failed",timestamp:(new Date).toISOString()}})
2497
+ R=e?.id}catch(e){l.debug("Failed to create tool record for command run",{error:(0,i.getErrorMessage)(e)})}return p({success:!0}),{content:[{type:"text",text:`## ${w.name}${x?` \u2014 ${x} (killed after ${w.timeout}s)`:""}\n\`\`\`\n$ ${b}\n${P}\n\`\`\`\n\nExit code: ${E}`}],...(0,s.alchemyMeta)({kind:"run-command",toolRecordId:R})}}catch(e){return p({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to run command: "+(0,i.getErrorMessage)(e)}],isError:!0}}}
2498
+ const n=t(4600),o=r(t(6928)),s=t(2272),i=t(8824),a=t(6688),c=t(9414)},4466(e,exports,t){"use strict"
2499
2499
  function r(e,t){return`${e}#${t.toString().padStart(2,"0")}`}async function n(e,t,n,s){if(exports.TASK_REF_PATTERN.test(e))return e
2500
2500
  const i=e.match(exports.TASK_REF_SHORT_PATTERN)
2501
2501
  if(!i)return e
@@ -2551,7 +2551,12 @@ return(0,i.withAlchemyKind)(f,y)}catch(e){return h.error("Planner ops failed",{e
2551
2551
  const r=t(9860),n=t(8824),o=t(6688),s=t(1725),i=t(2272),a={request:"planner-request","check-status":"planner-check-status",list:"planner-list"}},4600(e,exports,t){"use strict"
2552
2552
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.execAsync=async function(e,t={}){return new Promise(((n,o)=>{const[s,...i]=e.split(" "),a=(0,r.spawn)(s,i,{cwd:t.cwd||process.cwd(),stdio:["pipe","pipe","pipe"]})
2553
2553
  let c="",l=""
2554
- a.stdout?.on("data",(e=>{c+=e.toString()})),a.stderr?.on("data",(e=>{l+=e.toString()})),a.on("close",(e=>{n({stdout:c,stderr:l,exitCode:e||0})})),a.on("error",(t=>{o(Error(`Command failed: ${e}\nError: ${t.message}`))})),t.timeout&&setTimeout((()=>{a.kill(),o(Error("Command timed out: "+e))}),t.timeout)}))}
2554
+ a.stdout?.on("data",(e=>{c+=e.toString()})),a.stderr?.on("data",(e=>{l+=e.toString()})),a.on("close",(e=>{n({stdout:c,stderr:l,exitCode:e||0})})),a.on("error",(t=>{o(Error(`Command failed: ${e}\nError: ${t.message}`))})),t.timeout&&setTimeout((()=>{a.kill(),o(Error("Command timed out: "+e))}),t.timeout)}))},exports.runShellCommand=async function(e,r={}){const{spawn:n}=await Promise.resolve().then(t.t.bind(t,5317,23))
2555
+ return new Promise((t=>{const o="win32"===process.platform,s=n(e,{cwd:r.cwd??process.cwd(),env:r.env,shell:o?"cmd.exe":"/bin/sh",stdio:["pipe","pipe","pipe"]}),i=[],a=[]
2556
+ let c,l,d=!1,u=!1
2557
+ r.timeoutMs&&r.timeoutMs>0&&(l=setTimeout((()=>{d=!0,s.kill("SIGTERM"),c=setTimeout((()=>{u||s.kill("SIGKILL")}),2e3)}),r.timeoutMs)),s.stdout?.on("data",(e=>i.push(e))),s.stderr?.on("data",(e=>a.push(e)))
2558
+ const h=e=>{u||(u=!0,l&&clearTimeout(l),c&&clearTimeout(c),t({stdout:Buffer.concat(i).toString("utf-8"),stderr:Buffer.concat(a).toString("utf-8"),exitCode:e,timedOut:d}))}
2559
+ s.on("close",(e=>h(e??(d?124:1)))),s.on("error",(()=>h(1)))}))}
2555
2560
  const r=t(5317)},4607(e,exports,t){"use strict"
2556
2561
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
2557
2562
  const r=t(6183),n=t(3306)
@@ -5019,11 +5024,11 @@ exports.def={name:"list_tasks",description:"List and filter tasks",schema:r.List
5019
5024
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
5020
5025
  const r=t(6183),n=t(6416)
5021
5026
  exports.def={name:"revert_request",description:"Cancel open/in-progress requests or revert completed requests.\n\nCancel (open/in-progress):\n\u2022 Status set to 'canceled', request becomes read-only\n\u2022 No git operations, branch preserved\n\nRevert (completed):\n\u2022 All file changes undone via git revert\n\u2022 Request/task status set to 'reverted'\n\u2022 Request branch deleted\n\u2022 Fails fast if conflicts detected\n\nPreserved: Documents, git history, tool records (for audit trail).",schema:r.RevertRequestSchema,annotations:{title:"Revert Request",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},hidden:!0,handler(e,t){return(0,n.handleRevertRequest)(e,t.context,t.apiClient,t.logger,t.metrics)}}},9414(e,exports,t){"use strict"
5022
- function r(){const e=Date.now()
5023
- if(null!==c&&d>e-l)return c
5024
- const t=(0,o.join)(process.env.ALCHEMY_PROJECT_PATH||process.cwd(),i,a)
5025
- if((0,n.existsSync)(t))try{const r=(0,n.readFileSync)(t,"utf-8"),o=JSON.parse(r)
5026
- if("number"==typeof o.port&&o.port>0&&65536>o.port)return c=o.port,l=e,c}catch{}return c=s,l=e,c}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getActionServerPort=r,exports.getActionServerBaseUrl=function(){return"http://localhost:"+r()}
5027
+ function r(e){const t=Date.now(),r=!e
5028
+ if(r&&null!==c&&d>t-l)return c
5029
+ const u=(0,o.join)(e||process.env.ALCHEMY_PROJECT_PATH||process.cwd(),i,a)
5030
+ if((0,n.existsSync)(u))try{const e=(0,n.readFileSync)(u,"utf-8"),o=JSON.parse(e)
5031
+ if("number"==typeof o.port&&o.port>0&&65536>o.port)return r&&(c=o.port,l=t),o.port}catch{}return r&&(c=s,l=t),s}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getActionServerPort=r,exports.getActionServerBaseUrl=function(){return"http://localhost:"+r()}
5027
5032
  const n=t(9896),o=t(6928),s=3456,i=".alchemy_lab",a="action-server.json"
5028
5033
  let c=null,l=0
5029
5034
  const d=5e3},9422(e,exports,t){"use strict"
@@ -5366,7 +5371,7 @@ e.r(s)
5366
5371
  var i={}
5367
5372
  t=t||[null,r({}),r([]),r(r)]
5368
5373
  for(var a=2&o&&n;"object"==typeof a&&!~t.indexOf(a);a=r(a))Object.getOwnPropertyNames(a).forEach((e=>i[e]=()=>n[e]))
5369
- return i.default=()=>n,e.d(s,i),s},e.d=(exports,t)=>{for(var r in t)e.o(t,r)&&!e.o(exports,r)&&Object.defineProperty(exports,r,{enumerable:!0,get:t[r]})},e.f={},e.e=t=>Promise.all(Object.keys(e.f).reduce(((r,n)=>(e.f[n](t,r),r)),[])),e.u=e=>e+".js",e.o=(e,t)=>({}.hasOwnProperty.call(e,t)),e.r=exports=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},n={60:1,873:1,939:1},e.f.require=t=>{n[t]||(t=>{var r=t.modules,o=t.ids,s=t.runtime
5374
+ return i.default=()=>n,e.d(s,i),s},e.d=(exports,t)=>{for(var r in t)e.o(t,r)&&!e.o(exports,r)&&Object.defineProperty(exports,r,{enumerable:!0,get:t[r]})},e.f={},e.e=t=>Promise.all(Object.keys(e.f).reduce(((r,n)=>(e.f[n](t,r),r)),[])),e.u=e=>e+".js",e.o=(e,t)=>({}.hasOwnProperty.call(e,t)),e.r=exports=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},n={60:1,414:1,873:1,939:1},e.f.require=t=>{n[t]||(t=>{var r=t.modules,o=t.ids,s=t.runtime
5370
5375
  for(var i in r)e.o(r,i)&&(e.m[i]=r[i])
5371
5376
  s&&s(e)
5372
5377
  for(var a=0;a<o.length;a++)n[o[a]]=1})(require("./"+e.u(t)))}
@@ -8,10 +8,29 @@
8
8
  */
9
9
  import { join } from "path";
10
10
  import { readFileSync, existsSync } from "fs";
11
- const ACTION_SERVER_PORT = 3456;
12
- const ACTION_SERVER_BASE = `http://localhost:${ACTION_SERVER_PORT}`;
13
- const HOOK_EVENT_URL = `${ACTION_SERVER_BASE}/api/hook-event`;
14
- const TOOL_EVENT_URL = `${ACTION_SERVER_BASE}/api/tool-event`;
11
+ const DEFAULT_ACTION_SERVER_PORT = 3456;
12
+ /**
13
+ * Read the action server's actual port from `${directory}/.alchemy_lab/action-server.json`.
14
+ * The action server auto-increments from 3456 when the preferred port is busy
15
+ * (multiple alchemylab installs on one machine), and writes the resolved port
16
+ * to that file. Without this discovery we'd POST events to whatever first
17
+ * action server happens to own 3456 — wrong project, hooks never fire.
18
+ *
19
+ * Falls back to 3456 if the file is missing or malformed.
20
+ */
21
+ function readActionServerPort(directory) {
22
+ try {
23
+ const filePath = join(directory, '.alchemy_lab', 'action-server.json');
24
+ if (!existsSync(filePath))
25
+ return DEFAULT_ACTION_SERVER_PORT;
26
+ const data = JSON.parse(readFileSync(filePath, 'utf-8'));
27
+ if (typeof data.port === 'number' && data.port > 0 && data.port < 65536) {
28
+ return data.port;
29
+ }
30
+ }
31
+ catch { /* fall through */ }
32
+ return DEFAULT_ACTION_SERVER_PORT;
33
+ }
15
34
  // Singleton guard — OpenCode calls plugin init twice
16
35
  let initializedDirectory = null;
17
36
  let cachedHandlers = null;
@@ -68,6 +87,14 @@ export default async ({ directory }) => {
68
87
  return cachedHandlers;
69
88
  }
70
89
  initializedDirectory = dir;
90
+ // Discover the action server's actual port from the project's
91
+ // .alchemy_lab/action-server.json. Multiple alchemylab installs on one
92
+ // machine race for 3456; without this discovery hooks for any non-first
93
+ // project would silently land at the wrong action server (or nowhere).
94
+ const actionServerPort = readActionServerPort(dir);
95
+ const actionServerBase = `http://localhost:${actionServerPort}`;
96
+ const HOOK_EVENT_URL = `${actionServerBase}/api/hook-event`;
97
+ const TOOL_EVENT_URL = `${actionServerBase}/api/tool-event`;
71
98
  const handlers = {
72
99
  "tool.execute.before": async (input) => {
73
100
  const toolName = input?.tool || input?.name;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alchemylab",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "private": false,
5
5
  "description": "AlchemyLab - agentic coding IDEm",
6
6
  "author": "Testfire Software",
@@ -31,7 +31,7 @@
31
31
  "build": "npm run build:dev",
32
32
  "build:dev": "tsc -p tsconfig.json && chmod +x dist-dev/*.js",
33
33
  "dev:watch": "tsc -p tsconfig.json --watch",
34
- "build:prod": "webpack --mode=production && chmod +x dist/*.js && npm run copy:plugins",
34
+ "build:prod": "npm run build:dev && webpack --mode=production && chmod +x dist/*.js && npm run copy:plugins",
35
35
  "copy:plugins": "mkdir -p dist/plugins/hooks/lib && cp dist-dev/plugins/hooks/plugin.js dist/plugins/hooks/ && cp dist-dev/plugins/hooks/lib/*.js dist/plugins/hooks/lib/ && cp dist-dev/plugins/inject-session.js dist/plugins/ && cp dist-dev/plugins/system-logger.js dist/plugins/",
36
36
  "build:analyze": "ANALYZE=true webpack --mode=production",
37
37
  "test": "npm run build:dev && mocha --require ts-node/register test/**/*.test.ts",