alchemylab 1.0.0
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/LICENSE.txt +33 -0
- package/dist/478.js +109 -0
- package/dist/60.js +14 -0
- package/dist/800.js +23 -0
- package/dist/939.js +13 -0
- package/dist/994.js +126 -0
- package/dist/cli.js +899 -0
- package/dist/http-server.js +5659 -0
- package/dist/index.js +5731 -0
- package/dist/mcp-cli-bridge.js +88 -0
- package/dist/mcp-direct.js +5436 -0
- package/package.json +67 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
(()=>{"use strict"
|
|
2
|
+
function e(n){var i=s[n]
|
|
3
|
+
if(void 0!==i)return i.exports
|
|
4
|
+
var module=s[n]={exports:{}}
|
|
5
|
+
return t[n].call(module.exports,module,module.exports,e),module.exports}var t={1569(module){module.exports=require("zod")},2904(e,exports,t){function s(e){return{content:[{type:"text",text:e}]}}function n(e){return{content:[{type:"text",text:"Error: "+e}]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.CLIBridgeOperationsSchema=void 0,exports.handleCLIBridgeOperations=async function(e){const t=(0,o.getCLIBridge)()
|
|
6
|
+
if(!t)return n("CLI Bridge not initialized. The action server may not be running.")
|
|
7
|
+
const{operation:i}=e
|
|
8
|
+
switch(i){case"list-sessions":{const i=await t.sendCommand("list-sessions",{includeArchived:e.includeArchived,status:e.status,agentType:e.agentType,search:e.search,limit:e.limit})
|
|
9
|
+
return i.success?s(function(e){if(!e||0===e.length)return"No sessions found."
|
|
10
|
+
const t=e.map((e=>{const t=[`[${e.status?.toUpperCase()||"?"}]`,e.id,e.agentType?`(${e.agentType})`:"",e.title||"(untitled)"]
|
|
11
|
+
return e.parentId&&t.push(" parent: "+e.parentId),t.filter(Boolean).join(" ")}))
|
|
12
|
+
return`Sessions (${e.length}):\n${t.join("\n")}`}(i.data)):n(i.error||"Failed to list sessions")}case"get-messages":{if(!e.sessionId)return n("sessionId is required for get-messages")
|
|
13
|
+
const i=await t.sendCommand("get-messages",{sessionId:e.sessionId,limit:e.limit})
|
|
14
|
+
return i.success?s((r=i.data)&&0!==r.length?r.map((e=>{const t=e.role?.toUpperCase()||"?",s=e.agent?` [${e.agent}]`:"",n=e.time?new Date(e.time).toLocaleTimeString():"",i=e.completedAt?"":" (streaming...)",o=(e.parts||[]).filter((e=>"text"===e.type&&e.text)).map((e=>e.text)).join("\n"),r=(e.parts||[]).filter((e=>"tool"===e.type||"patch"===e.type)).map((e=>{if("tool"===e.type){const t=e.state?.status||"unknown"
|
|
15
|
+
return` [tool: ${e.tool} \u2192 ${t}]`}return"patch"===e.type?` [patch: ${(Array.isArray(e.files)?e.files:[]).map((e=>e.split("/").pop())).join(", ")}]`:""})).filter(Boolean).join("\n")
|
|
16
|
+
return`--- ${t}${s} ${n}${i} ---\n${[o,r].filter(Boolean).join("\n")}`})).join("\n\n"):"No messages found."):n(i.error||"Failed to get messages")}case"send-message":{if(!e.sessionId)return n("sessionId is required for send-message")
|
|
17
|
+
if(!e.message)return n("message is required for send-message")
|
|
18
|
+
let i=e.agent
|
|
19
|
+
if(!i){const s=await t.sendCommand("get-session-status",{sessionId:e.sessionId})
|
|
20
|
+
s.success&&s.data?.agentType&&(i=s.data.agentType)}const o=await t.sendCommand("send-message",{sessionId:e.sessionId,message:e.message,agent:i,variant:e.variant,system:e.system})
|
|
21
|
+
return o.success?s("Message sent successfully."+(o.data?.messageId?" Message ID: "+o.data.messageId:"")):n(o.error||"Failed to send message")}case"create-session":{const i=await t.sendCommand("create-session",{directory:e.directory,title:e.title,parentId:e.parentId,agent:e.agent})
|
|
22
|
+
if(!i.success)return n(i.error||"Failed to create session")
|
|
23
|
+
const o=i.data
|
|
24
|
+
return s(`Session created: ${o.id}\nTitle: ${o.title||"(none)"}\nAgent: ${o.agentType||"default"}\nStatus: ${o.status}`)}case"get-session-status":{if(!e.sessionId)return n("sessionId is required for get-session-status")
|
|
25
|
+
const i=await t.sendCommand("get-session-status",{sessionId:e.sessionId})
|
|
26
|
+
if(!i.success)return n(i.error||"Failed to get session status")
|
|
27
|
+
const o=i.data
|
|
28
|
+
return s(`Session: ${o.id}\nTitle: ${o.title||"(none)"}\nStatus: ${o.status}\nAgent: ${o.agentType||"default"}`)}case"list-notifications":{const i=await t.sendCommand("list-notifications",{sessionId:e.sessionId,messageType:e.messageType,status:e.status,limit:e.limit})
|
|
29
|
+
return i.success?s(function(e){if(!e||0===e.length)return"No notifications found."
|
|
30
|
+
const t=e.map((e=>{const t="question"===e.messageType?"Q":"R",s="question"===e.messageType?"pending"===e.status?"PENDING":e.status?.toUpperCase():"unread"===e.readStatus?"UNREAD":e.readStatus?.toUpperCase(),n=e.content?.question||e.content?.summary||"(no summary)",i=e.createdAt?new Date(e.createdAt).toLocaleTimeString():""
|
|
31
|
+
return`[${t}] [${s}] ${e.messageCode} ${n} (${i})`}))
|
|
32
|
+
return`Notifications (${e.length}):\n${t.join("\n")}`}(i.data)):n(i.error||"Failed to list notifications")}case"get-notification":{if(!e.messageCode)return n("messageCode is required for get-notification")
|
|
33
|
+
const i=await t.sendCommand("get-notification",{messageCode:e.messageCode})
|
|
34
|
+
return i.success?s(function(e){if(!e)return"Notification not found."
|
|
35
|
+
const t=[]
|
|
36
|
+
return t.push("Code: "+e.messageCode),t.push("Type: "+e.messageType),t.push("Status: "+e.status),t.push("From: "+e.fromSessionId),e.toSessionId&&t.push("To: "+e.toSessionId),t.push("Created: "+e.createdAt),"question"===e.messageType?(t.push("Question: "+(e.content?.question||"(none)")),e.content?.questionType&&t.push("Question Type: "+e.content.questionType),e.content?.context&&t.push("Context: "+e.content.context),e.content?.options?.length&&(t.push("Options:"),e.content.options.forEach((e=>{t.push(` - ${e.id}: ${e.label}${e.description?" \u2014 "+e.description:""}`)})))):"report"===e.messageType&&(t.push("Read Status: "+e.readStatus),e.content?.reportStatus&&t.push("Report Status: "+e.content.reportStatus),t.push("Summary: "+(e.content?.summary||"(none)")),e.content?.details&&t.push("Details: "+e.content.details)),e.response&&(t.push("Response: "+JSON.stringify(e.response)),e.respondedBySessionId&&t.push("Responded By: "+e.respondedBySessionId),e.respondedAt&&t.push("Responded At: "+e.respondedAt)),t.join("\n")}(i.data)):n(i.error||"Failed to get notification")}case"respond-to-notification":{if(!e.messageCode)return n("messageCode is required for respond-to-notification")
|
|
37
|
+
const i=await t.sendCommand("respond-to-notification",{messageCode:e.messageCode,responseText:e.responseText,selectedOptions:e.selectedOptions})
|
|
38
|
+
return i.success?s(`Response sent for ${e.messageCode}.`):n(i.error||"Failed to respond to notification")}case"list-requests":{const i=await t.sendCommand("list-requests",{status:e.status,type:e.type,limit:e.limit,offset:e.offset,sortBy:e.sortBy,sortDirection:e.sortDirection})
|
|
39
|
+
return i.success?s(function(e){if(!e||0===e.length)return"No requests found."
|
|
40
|
+
const t=e.map((e=>{const t=e.status?.toUpperCase()||"?",s=e.type||"",n=e.updatedAt?new Date(e.updatedAt).toLocaleDateString():""
|
|
41
|
+
return`[${t}] ${e.id} ${s} ${e.title||"(untitled)"} (${n})`}))
|
|
42
|
+
return`Requests (${e.length}):\n${t.join("\n")}`}(i.data)):n(i.error||"Failed to list requests")}case"get-request":{if(!e.requestId)return n("requestId is required for get-request")
|
|
43
|
+
const i=await t.sendCommand("get-request",{requestId:e.requestId})
|
|
44
|
+
return i.success?s(function(e){if(!e)return"Request not found."
|
|
45
|
+
const t=[]
|
|
46
|
+
return t.push("ID: "+e.id),t.push("Title: "+(e.title||"(none)")),t.push("Type: "+(e.type||"(none)")),t.push("Status: "+e.status),e.description&&t.push("Description: "+e.description),e.acceptanceCriteria?.length&&(t.push("Acceptance Criteria:"),e.acceptanceCriteria.forEach(((e,s)=>{const n="string"==typeof e?e:e.description||JSON.stringify(e)
|
|
47
|
+
t.push(` ${s+1}. ${n}`)}))),t.push("Created: "+e.createdAt),t.push("Updated: "+e.updatedAt),e.tasks?.length&&(t.push(""),t.push(`Tasks (${e.tasks.length}):`),e.tasks.forEach((e=>{const s=e.status?.toUpperCase()||"?"
|
|
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
|
+
const i=e.timeoutSeconds||120,o=Date.now()+1e3*i,r=2e3
|
|
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})
|
|
53
|
+
if(!s.success)return n(s.error||"Failed to check session status")
|
|
54
|
+
if("busy"===s.data.status){a=!0
|
|
55
|
+
break}await new Promise((e=>setTimeout(e,500)))}for(;Date.now()<o;){const i=await t.sendCommand("get-session-status",{sessionId:e.sessionId})
|
|
56
|
+
if(!i.success)return n(i.error||"Failed to check session status")
|
|
57
|
+
const o=i.data
|
|
58
|
+
if(("idle"===o.status||"error"===o.status)&&a)return s(`Session ${o.id} is now ${o.status}. Title: ${o.title||"(none)"}`)
|
|
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
|
+
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}}
|
|
62
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.CLIBridgeWebSocket=void 0,exports.setCLIBridge=function(e){o=e},exports.getCLIBridge=function(){return o}
|
|
63
|
+
const n=t(5086),i=s(t(6982))
|
|
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())
|
|
65
|
+
this.handleResponse(t)}catch(e){this.logger.error("[CLIBridge] Failed to parse message from UI",{error:e instanceof Error?e.message:e+""})}})),e.on("close",(()=>{this.logger.info("[CLIBridge] UI client disconnected"),this.uiClient===e&&(this.uiClient=null),this.stopHeartbeat()
|
|
66
|
+
for(const[e,t]of this.pendingCommands)clearTimeout(t.timer),t.resolve({id:e,success:!1,error:"UI client disconnected during command. The command may have been partially executed."}),this.pendingCommands.delete(e)})),e.on("error",(e=>{this.logger.error("[CLIBridge] WebSocket error",{error:e.message})}))})),this.logger.info("[CLIBridge] WebSocket server attached at /ws/cli-bridge")}isUIConnected(){return null!==this.uiClient&&this.uiClient.readyState===n.WebSocket.OPEN}async sendCommand(e,t={},s){if(!this.isUIConnected())return{id:"",success:!1,error:"alchemy-ui is not connected. Please open alchemy-ui in your browser first."}
|
|
67
|
+
const n=i.default.randomUUID(),o={id:n,action:e,params:t}
|
|
68
|
+
return new Promise(((t,i)=>{const r=setTimeout((()=>{this.pendingCommands.delete(n),t({id:n,success:!1,error:`Command '${e}' timed out after ${(s||this.defaultTimeout)/1e3}s`})}),s||this.defaultTimeout)
|
|
69
|
+
this.pendingCommands.set(n,{resolve:t,reject:i,timer:r})
|
|
70
|
+
try{this.uiClient.send(JSON.stringify(o))}catch(e){clearTimeout(r),this.pendingCommands.delete(n),t({id:n,success:!1,error:"Failed to send command: "+(e instanceof Error?e.message:e+"")})}}))}handleResponse(e){const t=this.pendingCommands.get(e.id)
|
|
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
|
+
for(const[e,t]of this.pendingCommands)clearTimeout(t.timer),t.reject(Error("Bridge shutting down")),this.pendingCommands.delete(e)
|
|
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)
|
|
79
|
+
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
|
+
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)
|
|
82
|
+
const p=new t.Server({name:"alchemy-cli-bridge",version:"1.0.0"},{capabilities:{tools:{}}})
|
|
83
|
+
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
|
+
if("cli_operations"!==t)return{content:[{type:"text",text:"Unknown tool: "+t}],isError:!0}
|
|
85
|
+
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)
|
|
87
|
+
const e=new s.StdioServerTransport
|
|
88
|
+
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})()
|