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 +9 -0
- package/dist/cli.js +8 -8
- package/dist/http-server.js +24 -19
- package/dist/index.js +24 -19
- package/dist/mcp-cli-bridge.js +18 -10
- package/dist/mcp-direct.js +24 -19
- package/dist/plugins/hooks/plugin.js +31 -4
- package/package.json +2 -2
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,
|
|
894
|
-
if(!
|
|
895
|
-
const
|
|
896
|
-
if(!
|
|
897
|
-
const{notifiedSessions:
|
|
898
|
-
await Promise.allSettled(
|
|
899
|
-
return s(l,t.externalSessionId,
|
|
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={}
|
package/dist/http-server.js
CHANGED
|
@@ -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{
|
|
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
|
-
|
|
1355
|
-
g.push({name:t.name,command:t.command,exitCode:
|
|
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
|
-
|
|
2716
|
-
|
|
2717
|
-
v.
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
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
|
|
5240
|
-
if(null!==c&&d>
|
|
5241
|
-
const
|
|
5242
|
-
if((0,n.existsSync)(
|
|
5243
|
-
if("number"==typeof o.port&&o.port>0&&65536>o.port)return c=o.port,l=
|
|
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{
|
|
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
|
-
|
|
1357
|
-
g.push({name:t.name,command:t.command,exitCode:
|
|
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
|
-
|
|
2723
|
-
|
|
2724
|
-
v.
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
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
|
|
5328
|
-
if(null!==c&&d>
|
|
5329
|
-
const
|
|
5330
|
-
if((0,n.existsSync)(
|
|
5331
|
-
if("number"==typeof o.port&&o.port>0&&65536>o.port)return c=o.port,l=
|
|
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)))}
|
package/dist/mcp-cli-bridge.js
CHANGED
|
@@ -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
|
|
52
|
-
for(;Date.now()<
|
|
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")}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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 ${
|
|
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: "+
|
|
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})()
|
package/dist/mcp-direct.js
CHANGED
|
@@ -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{
|
|
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
|
-
|
|
1254
|
-
g.push({name:t.name,command:t.command,exitCode:
|
|
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
|
-
|
|
2492
|
-
|
|
2493
|
-
v.
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
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
|
|
5023
|
-
if(null!==c&&d>
|
|
5024
|
-
const
|
|
5025
|
-
if((0,n.existsSync)(
|
|
5026
|
-
if("number"==typeof o.port&&o.port>0&&65536>o.port)return c=o.port,l=
|
|
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
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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.
|
|
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",
|