alchemylab 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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"
@@ -5236,11 +5236,11 @@ module.exports=require("net")},9373(e,exports,t){"use strict"
5236
5236
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
5237
5237
  const r=t(6183),n=t(6416)
5238
5238
  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()}
5239
+ function r(e){const t=Date.now(),r=!e
5240
+ if(r&&null!==c&&d>t-l)return c
5241
+ const u=(0,o.join)(e||process.env.ALCHEMY_PROJECT_PATH||process.cwd(),i,a)
5242
+ if((0,n.existsSync)(u))try{const e=(0,n.readFileSync)(u,"utf-8"),o=JSON.parse(e)
5243
+ 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
5244
  const n=t(9896),o=t(6928),s=3456,i=".alchemy_lab",a="action-server.json"
5245
5245
  let c=null,l=0
5246
5246
  const d=5e3},9422(e,exports,t){"use strict"
@@ -5579,7 +5579,7 @@ e.r(s)
5579
5579
  var i={}
5580
5580
  t=t||[null,r({}),r([]),r(r)]
5581
5581
  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
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,414:1},e.f.require=t=>{n[t]||(t=>{var r=t.modules,o=t.ids,s=t.runtime
5583
5583
  for(var i in r)e.o(r,i)&&(e.m[i]=r[i])
5584
5584
  s&&s(e)
5585
5585
  for(var a=0;a<o.length;a++)n[o[a]]=1})(require("./"+e.u(t)))}
package/dist/index.js CHANGED
@@ -5324,11 +5324,11 @@ module.exports=require("net")},9373(e,exports,t){"use strict"
5324
5324
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
5325
5325
  const r=t(6183),n=t(6416)
5326
5326
  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()}
5327
+ function r(e){const t=Date.now(),r=!e
5328
+ if(r&&null!==c&&d>t-l)return c
5329
+ const u=(0,o.join)(e||process.env.ALCHEMY_PROJECT_PATH||process.cwd(),i,a)
5330
+ if((0,n.existsSync)(u))try{const e=(0,n.readFileSync)(u,"utf-8"),o=JSON.parse(e)
5331
+ 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
5332
  const n=t(9896),o=t(6928),s=3456,i=".alchemy_lab",a="action-server.json"
5333
5333
  let c=null,l=0
5334
5334
  const d=5e3},9422(e,exports,t){"use strict"
@@ -5684,7 +5684,7 @@ e.r(s)
5684
5684
  var i={}
5685
5685
  t=t||[null,r({}),r([]),r(r)]
5686
5686
  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
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,414:1,873:1,939:1},e.f.require=t=>{n[t]||(t=>{var r=t.modules,o=t.ids,s=t.runtime
5688
5688
  for(var i in r)e.o(r,i)&&(e.m[i]=r[i])
5689
5689
  s&&s(e)
5690
5690
  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})()
@@ -5019,11 +5019,11 @@ exports.def={name:"list_tasks",description:"List and filter tasks",schema:r.List
5019
5019
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
5020
5020
  const r=t(6183),n=t(6416)
5021
5021
  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()}
5022
+ function r(e){const t=Date.now(),r=!e
5023
+ if(r&&null!==c&&d>t-l)return c
5024
+ const u=(0,o.join)(e||process.env.ALCHEMY_PROJECT_PATH||process.cwd(),i,a)
5025
+ if((0,n.existsSync)(u))try{const e=(0,n.readFileSync)(u,"utf-8"),o=JSON.parse(e)
5026
+ 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
5027
  const n=t(9896),o=t(6928),s=3456,i=".alchemy_lab",a="action-server.json"
5028
5028
  let c=null,l=0
5029
5029
  const d=5e3},9422(e,exports,t){"use strict"
@@ -5366,7 +5366,7 @@ e.r(s)
5366
5366
  var i={}
5367
5367
  t=t||[null,r({}),r([]),r(r)]
5368
5368
  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
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,414:1,873:1,939:1},e.f.require=t=>{n[t]||(t=>{var r=t.modules,o=t.ids,s=t.runtime
5370
5370
  for(var i in r)e.o(r,i)&&(e.m[i]=r[i])
5371
5371
  s&&s(e)
5372
5372
  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.1",
4
4
  "private": false,
5
5
  "description": "AlchemyLab - agentic coding IDEm",
6
6
  "author": "Testfire Software",