alchemylab 1.2.3 → 1.3.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/dist/640.js +14 -0
- package/dist/cli.js +190 -111
- package/dist/http-server.js +619 -582
- package/dist/index.js +411 -374
- package/dist/mcp-direct.js +152 -115
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -126,8 +126,8 @@ const m=await f.json()
|
|
|
126
126
|
if(!m.id)throw Error("Session created in OpenCode but no ID returned")
|
|
127
127
|
const y=m.id
|
|
128
128
|
d.info("Pre-created OpenCode session",{sessionId:y,title:g,agent:t})
|
|
129
|
-
const v=this.registerSession({sessionId:y,parentSessionId:a,projectId:u,agent:t,title:g,specialization:c,callerAgentType:l,requestId:i}),w=this.resolveModel(t,r),
|
|
130
|
-
return{sessionId:y,model:
|
|
129
|
+
const v=this.registerSession({sessionId:y,parentSessionId:a,projectId:u,agent:t,title:g,specialization:c,callerAgentType:l,requestId:i}),w=this.resolveModel(t,r),I=i?(0,o.ensureRequestWorktree)(r,i,d).catch((e=>{d.warn("Pre-spawn worktree creation failed (non-fatal)",{requestId:i,error:(0,n.getErrorMessage)(e)})})):Promise.resolve(),[,k]=await Promise.all([v,w,I])
|
|
130
|
+
return{sessionId:y,model:k,serverUrl:h,initialBranch:i?"requests/"+i:void 0}}async registerSession(e){const{sessionId:t,parentSessionId:s,projectId:r,agent:o,title:i,specialization:a,callerAgentType:c,requestId:l}=e,{apiClient:d,logger:u}=this.deps
|
|
131
131
|
try{let e,h
|
|
132
132
|
if(s)try{const t=await d.post(`/api/projects/${r}/cli-sessions`,{externalSessionId:s,providerId:"opencode",status:"active",agentType:c||void 0}),o=t?.data||t
|
|
133
133
|
e=o?.id,h=o?.workflowId,u.debug("Registered parent session",{parentSessionId:s,parentDbId:e,parentWorkflowId:h})}catch(e){u.warn("Failed to register parent session",{error:(0,n.getErrorMessage)(e)})}if(await d.post(`/api/projects/${r}/cli-sessions`,{externalSessionId:t,providerId:"opencode",parentSessionId:e||null,title:i,status:"active",agentType:o,specialization:a,workflowId:h||void 0}),u.info("Registered session in alchemy DB",{sessionId:t,agent:o,parentSessionId:s}),l){try{await d.post(`/api/projects/${r}/session-request-associations`,{opencodeSessionId:t,requestLocalId:l}),u.info("Created session-request association",{sessionId:t,requestId:l})}catch(e){u.warn("Failed to create session-request association",{error:(0,n.getErrorMessage)(e)})}if("coder"===o)try{await d.patch(`/api/projects/${r}/requests/${l}`,{cliOrigin:{providerId:"opencode",sessionId:t,createdAt:(new Date).toISOString()}}),u.debug("Updated request cliOrigin",{sessionId:t,requestId:l})}catch(e){u.warn("Failed to update request cliOrigin",{error:(0,n.getErrorMessage)(e)})}}}catch(e){u.warn("Failed to register session in alchemy DB",{error:(0,n.getErrorMessage)(e)})}}async resolveModel(e,t){const{logger:s}=this.deps,r=this.fetchProjectDefaultModel()||void 0
|
|
@@ -137,7 +137,22 @@ if(s){const e=s.preferredProvider||"inherit",n=s.preferredModelTier||"same",a=(0
|
|
|
137
137
|
a.resolved&&(o=a.fullModel)}else{const s=(0,a.getBuiltInAgentConfig)(e)
|
|
138
138
|
if(s){const e=(0,i.resolveAgentModel)(t,s.preferredProvider||"inherit",s.preferredModelTier||"same",r)
|
|
139
139
|
e.resolved&&(o=e.fullModel)}}}catch(t){s.debug("Error resolving agent model",{agent:e,error:(0,n.getErrorMessage)(t)})}return!o&&r&&(o=r),o}async fetchAgentConfig(e){try{const t=await this.deps.apiClient.get(`/api/projects/${this.deps.projectId}/agents`)
|
|
140
|
-
return(t?.data||t||[]).find((t=>t.name===e))||null}catch{return null}}fetchProjectDefaultModel(){return this.deps.getDefaultModel()||null}}},
|
|
140
|
+
return(t?.data||t||[]).find((t=>t.name===e))||null}catch{return null}}fetchProjectDefaultModel(){return this.deps.getDefaultModel()||null}}},1405(e,exports,t){"use strict"
|
|
141
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleStartBuildingAction=async function(e,t){const{plannerSessionId:o,parentSessionId:n,requestIds:i,summary:a}=e,{logger:c,apiClient:l,projectId:d,projectPath:u}=t
|
|
142
|
+
if(!o)return{success:!1,message:"plannerSessionId is required",error:"missing_params"}
|
|
143
|
+
c.info("Starting start-building action",{plannerSessionId:o,requestIds:i,payloadParent:n})
|
|
144
|
+
let h=null
|
|
145
|
+
try{const e=await l.get(`/api/projects/${d}/cli-sessions/resolve/${o}`)
|
|
146
|
+
e.success&&(h=e.data)}catch(e){c.warn("Failed to resolve planner session",{error:(0,s.getErrorMessage)(e)})}const g=n||h?.parent?.externalSessionId||null
|
|
147
|
+
if(!g)return{success:!1,message:"Could not find parent session for the planner. The planner session may not have been properly linked at creation time.",error:"parent_not_found"}
|
|
148
|
+
c.info("Resolved parent session",{plannerSessionId:o,parentSessionId:g})
|
|
149
|
+
try{await l.post(`/api/projects/${d}/session-messages/reports`,{fromSessionId:o,toSessionId:g,status:"success",summary:"Plan complete. Created requests: "+(i||[]).join(", "),details:a||"Plan complete"})}catch(e){c.warn("Failed to create report",{error:(0,s.getErrorMessage)(e)})}const p=h?.id
|
|
150
|
+
if(p)try{await l.patch(`/api/projects/${d}/cli-sessions/${p}`,{status:"archived"}),c.info("Archived planner session",{plannerSessionId:o,sessionLocalId:p})}catch(e){c.warn("Failed to archive planner session (non-fatal)",{error:(0,s.getErrorMessage)(e)})}const f=(0,r.getOpenCodeServerUrl)(u,c)
|
|
151
|
+
if(f){const e=`\x3c!-- inserted-prompt --\x3eThe planner has finished creating the plan.\n\nCreated requests: ${(i||[]).join(", ")}\n\nPlease begin working on these requests.`
|
|
152
|
+
try{const s=await fetch(`${f}/session/${g}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:e}],...t.cachedDefaultModel?(()=>{const[e,...s]=t.cachedDefaultModel.split("/")
|
|
153
|
+
return{model:{providerID:e,modelID:s.join("/")}}})():{}})})
|
|
154
|
+
s.ok?c.info("Notified parent session to start building",{parentSessionId:g}):c.warn("Failed to notify parent session",{status:s.status,parentSessionId:g})}catch(e){c.warn("Could not notify parent session (may be offline)",{error:(0,s.getErrorMessage)(e)})}}return{success:!0,message:"Build report sent to parent session",data:{parentSessionId:g}}}
|
|
155
|
+
const s=t(8824),r=t(8148)},1943(module){"use strict"
|
|
141
156
|
module.exports=require("fs/promises")},2096(e,exports,t){"use strict"
|
|
142
157
|
var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
143
158
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.ProjectConfigService=void 0
|
|
@@ -159,13 +174,15 @@ c=JSON.parse(e)}catch(e){}c[e]={preset:t,disabledTools:s||[],allowedAlchemyTools
|
|
|
159
174
|
var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
160
175
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
|
|
161
176
|
const r=s(t(7252)),o=s(t(8577)),n=t(4192),i=t(1152),a=s(t(6928)),c=s(t(9896)),l=s(t(857)),d=t(5607),u=t(8984),h=t(8524),g=t(8824),p=t(2096),f=t(7210),m=t(2928),y=t(6563),v=t(1292),w=t(5317)
|
|
162
|
-
class
|
|
177
|
+
class I{updateAuthToken(e){this.options.authToken=e,this.apiClient.authToken=e,this.configurer.authToken=e}constructor(e,t){this.directReportDedup=new Map,this.activeMcpCalls=new Map,this.pendingAgentResync=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.isPolling=!1,this.isProcessingAction=!1,this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.logger=e,this.options=t,this.port=t.port||3456,this.startTime=new Date
|
|
163
178
|
const s=(0,i.createMetricsCollector)(!1)
|
|
164
179
|
this.apiClient=new n.BaseApiClient(t.remoteServerUrl,e,s,t.authToken,{timeout:35e3}),this.configurer=new u.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.cliBridge=new h.CLIBridgeWebSocket(e),(0,h.setCLIBridge)(this.cliBridge),this.hookExecutor=new f.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,r.default)()
|
|
165
180
|
return e.use((0,o.default)()),e.use(r.default.json({limit:"10mb"})),e.get("/health",this.handleHealth.bind(this)),e.get("/status",this.handleStatus.bind(this)),e.get("/opencode",this.handleOpenCodeConfig.bind(this)),e.post("/sync-agents",this.handleSyncAgents.bind(this)),e.post("/api/generate-journal",this.handleGenerateJournal.bind(this)),e.get("/api/event-hooks/by-event/:eventType",this.handleEventHooksProxy.bind(this)),e.post("/api/hook-triggers",this.handleHookTriggersProxy.bind(this)),e.get("/api/hook-iterations/:sessionId/:hookId",this.handleGetHookIteration.bind(this)),e.post("/api/hook-iterations/:sessionId/:hookId/increment",this.handleIncrementHookIteration.bind(this)),e.get("/api/project-settings",((e,t)=>{t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null}})})),e.post("/api/sync-settings",(async(e,t)=>{await this.syncProjectSettings(),t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null}})})),e.get("/api/agents/by-name/:agentName",this.handleGetAgentByName.bind(this)),e.get("/api/sessions/:sessionId/agent-type",this.handleGetSessionAgentType.bind(this)),e.get("/api/sessions/:sessionId/parent",this.handleGetSessionParent.bind(this)),e.get("/api/sessions/:sessionId/ancestors",(async(e,t)=>{try{const{sessionId:s}=e.params,r=this.projectId
|
|
166
181
|
if(!r)return void t.status(400).json({success:!1,error:"No project"})
|
|
167
182
|
const o=await this.apiClient.get(`/api/projects/${r}/cli-sessions/external/opencode/${encodeURIComponent(s)}/ancestors`),n=(o?.data||o||[]).map((e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title})))
|
|
168
|
-
t.json({success:!0,ancestors:n})}catch(e){t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.
|
|
183
|
+
t.json({success:!0,ancestors:n})}catch(e){t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.get("/api/registry/projects",(async(e,s)=>{try{const{listLive:e}=await t.e(640).then(t.bind(t,2640))
|
|
184
|
+
s.json({success:!0,data:e()})}catch(e){s.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.post("/api/mcp/tool-call-event",((e,t)=>{const{action:s,callId:r}=e.body||{}
|
|
185
|
+
!r||"start"!==s&&"end"!==s?t.status(400).json({success:!1,error:"action must be start|end, callId required"}):(this.handleMcpToolCallEvent(s,r+""),t.json({success:!0}))})),e.post("/api/hook-event",(async(e,t)=>{try{const{eventType:s,sessionId:r,variables:o,timing:n}=e.body
|
|
169
186
|
if(!s)return void t.status(400).json({success:!1,error:"eventType is required"})
|
|
170
187
|
const i=await this.hookExecutor.processEvent({eventType:s,sessionId:r||"",variables:o||{},timing:n})
|
|
171
188
|
t.json({success:!0,data:i})}catch(e){this.logger.error("Hook event processing failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.post("/api/direct-report",this.handleDirectReport.bind(this)),e.post("/api/cli-bridge/command",this.handleCLIBridgeCommand.bind(this)),e.get("/api/cli-bridge/status",this.handleCLIBridgeStatus.bind(this)),e.post("/api/spawn-session",this.handleSpawnSession.bind(this)),e.get("/api/timer-hooks/status",((e,t)=>{const s=this.timerHookScheduler?.getStatus()??{running:!1,trackedHooks:0,inFlight:[],hooks:[]}
|
|
@@ -180,7 +197,11 @@ try{c.default.accessSync(r,c.default.constants.W_OK)}catch{return void s.json({s
|
|
|
180
197
|
s.json({success:!0,message:"Update started. The CLI will restart when complete."}),o("npm install -g alchemylab@latest",{timeout:12e4},((e,t,s)=>{e?this.logger.error("Self-update failed",{error:e.message,stderr:s}):(this.logger.info("Self-update complete, shutting down for restart",{stdout:t.trim()}),setTimeout((()=>{console.log("\n\x1b[32mUpdate complete. Please restart alchemylab.\x1b[0m\n"),process.exit(0)}),1e3))}))}catch(e){s.json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.use("/api/opencode",((e,t)=>this.handleOpenCodeProxy(e,t))),e}handleHealth(e,t){t.json({status:"healthy",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),timestamp:(new Date).toISOString()})}handleStatus(e,t){t.json({status:"running",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),startTime:this.startTime.toISOString(),projectId:this.projectId,serverId:this.serverId,isPolling:this.isPolling,port:this.port,timestamp:(new Date).toISOString()})}handleOpenCodeConfig(e,t){const s=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
181
198
|
try{if(!c.default.existsSync(s))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
|
|
182
199
|
const e=c.default.readFileSync(s,"utf-8"),r=JSON.parse(e)
|
|
183
|
-
t.json({port:r.port,pid:r.pid,startedAt:r.startedAt,baseUrl:"http://localhost:"+r.port,directory:this.options.projectPath,proxyEnabled:!0})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,g.getErrorMessage)(e)})}}updateDefaultModel(e){void 0!==e&&e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),e&&this.projectId&&this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,g.getErrorMessage)(e)}))))}
|
|
200
|
+
t.json({port:r.port,pid:r.pid,startedAt:r.startedAt,baseUrl:"http://localhost:"+r.port,directory:this.options.projectPath,proxyEnabled:!0})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,g.getErrorMessage)(e)})}}updateDefaultModel(e){void 0!==e&&e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),e&&this.projectId&&this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,g.getErrorMessage)(e)}))))}handleMcpToolCallEvent(e,t){"start"!==e?(this.activeMcpCalls.delete(t),0===this.activeMcpCalls.size&&this.pendingAgentResync&&(this.pendingAgentResync=!1,this.logger.info("Firing deferred agent resync now that MCP is idle"),this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Deferred resync failed",{error:(0,g.getErrorMessage)(e)}))))):this.activeMcpCalls.set(t,Date.now())}startMcpStaleCleanup(){this.mcpStaleCleanupInterval||(this.mcpStaleCleanupInterval=setInterval((()=>{const e=Date.now()-12e4
|
|
201
|
+
let t=0
|
|
202
|
+
for(const[s,r]of this.activeMcpCalls)e>r&&(this.activeMcpCalls.delete(s),t++)
|
|
203
|
+
t>0&&(this.logger.warn("Cleaned up stale MCP tool-call tracking entries",{removed:t}),0===this.activeMcpCalls.size&&this.pendingAgentResync&&(this.pendingAgentResync=!1,this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Deferred resync after cleanup failed",{error:(0,g.getErrorMessage)(e)})))))}),6e4))}async resyncAgentMarkdowns(){if(!this.projectId)return
|
|
204
|
+
if(this.activeMcpCalls.size>0)return this.pendingAgentResync=!0,void this.logger.info("Deferring agent resync until MCP tool calls complete",{inFlight:this.activeMcpCalls.size})
|
|
184
205
|
const e=await this.configurer.fetchAgentsFromRemote(this.projectId)
|
|
185
206
|
if(!e?.length)return
|
|
186
207
|
const t=await this.configurer.fetchProjectFromRemote(this.projectId),s=t?.metadata?.globalSystemPrompt,r=a.default.join(this.options.projectPath,".opencode","agent")
|
|
@@ -190,34 +211,26 @@ for(const t of e){if(!t.enabled)continue
|
|
|
190
211
|
const e=this.configurer.generateAgentMarkdown(t,"alchemy",o,s)
|
|
191
212
|
await c.default.promises.writeFile(a.default.join(r,t.name+".md"),e,"utf8")}this.logger.info("Re-synced agent markdowns after model change",{model:o,count:e.length})
|
|
192
213
|
try{const e=new v.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,getDefaultModel:()=>this.cachedDefaultModel||void 0}).getOpenCodeServerUrl(this.options.projectPath)
|
|
193
|
-
e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3)})).ok&&this.logger.info("Triggered OpenCode config reload")}catch(e){this.logger.debug("Failed to trigger OpenCode config reload",{error:(0,g.getErrorMessage)(e)})}}async handleSyncAgents(e,
|
|
194
|
-
if(!e?.id)return void
|
|
214
|
+
e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3)})).ok&&this.logger.info("Triggered OpenCode config reload")}catch(e){this.logger.debug("Failed to trigger OpenCode config reload",{error:(0,g.getErrorMessage)(e)})}}async handleSyncAgents(e,t){try{const e=this.readProjectConfig()
|
|
215
|
+
if(!e?.id)return void t.status(400).json({success:!1,error:"No project configuration found"})
|
|
195
216
|
this.logger.info("Syncing agents from remote",{projectId:e.id})
|
|
196
|
-
const
|
|
197
|
-
if(!
|
|
198
|
-
const
|
|
199
|
-
this.logger.debug("Fetched project for global prompt",{projectId:e.id,hasProject:!!
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
await c.default.promises.mkdir(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),
|
|
209
|
-
if(
|
|
210
|
-
c.default.writeFileSync(e,JSON.stringify(
|
|
211
|
-
try{const e=
|
|
212
|
-
|
|
213
|
-
if(r.pid)try{process.kill(r.pid,"SIGTERM"),this.logger.info("Killed existing OpenCode process",{pid:r.pid}),await new Promise((e=>setTimeout(e,500)))}catch(e){"ESRCH"!==e?.code&&this.logger.warn("Could not kill OpenCode",{error:(0,g.getErrorMessage)(e)})}const o=r.port||0,{resolveServeCommand:n}=await Promise.resolve().then(t.bind(t,9068)),i=n(),a=(0,w.spawn)(i.command,[...i.prependArgs,"--port",o+""],{stdio:["ignore","pipe","pipe"],cwd:this.options.projectPath,detached:!0,env:process.env}),l=new Promise((e=>{let t=""
|
|
214
|
-
const s=setTimeout((()=>{this.logger.warn("Timeout waiting for OpenCode port, killing process")
|
|
215
|
-
try{a.kill("SIGTERM")}catch{}e(null)}),1e4)
|
|
216
|
-
a.stdout?.on("data",(r=>{t+=r.toString()
|
|
217
|
-
const o=t.match(/listening on http:\/\/[^:]+:(\d+)/)
|
|
218
|
-
o&&(clearTimeout(s),a.unref(),e({port:parseInt(o[1],10),pid:a.pid}))})),a.on("error",(()=>{clearTimeout(s)
|
|
219
|
-
try{a.kill("SIGTERM")}catch{}e(null)}))})),u=await l
|
|
220
|
-
u&&(this.trackChildPid(u.pid),c.default.writeFileSync(e,JSON.stringify({port:u.port,pid:u.pid,startedAt:(new Date).toISOString()},null,2)),this.logger.info("Restarted OpenCode",{port:u.port,pid:u.pid}),this.setOpenCodePort(u.port),d=!0)}}catch(e){this.logger.warn("Could not restart OpenCode",{error:(0,g.getErrorMessage)(e)})}s.json({success:!0,message:`Synced ${l} agent(s) to local files${d?" - OpenCode reloading":""}`,count:l,restarted:d})}catch(e){this.logger.error("Failed to sync agents",{error:(0,g.getErrorMessage)(e)}),s.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGenerateJournal(e,s){try{const{requestId:r}=e.body
|
|
217
|
+
const s=await this.configurer.fetchAgentsFromRemote(e.id)
|
|
218
|
+
if(!s||0===s.length)return void t.json({success:!0,message:"No agents to sync",count:0})
|
|
219
|
+
const r=await this.configurer.fetchProjectFromRemote(e.id)
|
|
220
|
+
this.logger.debug("Fetched project for global prompt",{projectId:e.id,hasProject:!!r,hasGlobalSystemPrompt:!!r?.metadata?.globalSystemPrompt})
|
|
221
|
+
const o=r?.metadata?.globalSystemPrompt
|
|
222
|
+
o?this.logger.info("Using global system prompt from project metadata",{projectId:e.id,promptLength:o.length}):this.logger.debug("No global system prompt found in project metadata")
|
|
223
|
+
const n=a.default.join(this.options.projectPath,".opencode","agent")
|
|
224
|
+
await c.default.promises.mkdir(n,{recursive:!0})
|
|
225
|
+
const i="alchemy",l=this.cachedDefaultModel||"anthropic/claude-opus-4-5",d=s.filter((e=>e.enabled)),u=await Promise.allSettled(d.map((e=>{const t=this.configurer.generateAgentMarkdown(e,i,l,o),s=a.default.join(n,e.name+".md")
|
|
226
|
+
return c.default.promises.writeFile(s,t,"utf8").then((()=>{this.logger.info("Synced agent file",{name:e.name,path:s,mode:e.mode})}))}))),h=u.filter((e=>"fulfilled"===e.status)).length,p=u.filter((e=>"rejected"===e.status)).length
|
|
227
|
+
p>0&&this.logger.warn("Some agent file writes failed",{writeFails:p})
|
|
228
|
+
try{const e=a.default.join(this.options.projectPath,"opencode.json")
|
|
229
|
+
if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),o=JSON.parse(t),n=r?.metadata?.bashToolAgents||{}
|
|
230
|
+
if(o.agent){for(const e of s)e.enabled&&o.agent[e.name]&&e.name in n&&(o.agent[e.name].tools.bash=!!n[e.name])
|
|
231
|
+
c.default.writeFileSync(e,JSON.stringify(o,null,2),"utf-8"),this.logger.info("Updated opencode.json agent tools",{bashOverrides:Object.keys(n).length})}}}catch(e){this.logger.warn("Failed to update opencode.json agent tools",{error:(0,g.getErrorMessage)(e)})}let f=!1
|
|
232
|
+
try{const e=new v.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,getDefaultModel:()=>this.cachedDefaultModel||void 0}).getOpenCodeServerUrl(this.options.projectPath)
|
|
233
|
+
e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3)})).ok&&(this.logger.info("Triggered OpenCode config reload after agent sync"),f=!0)}catch(e){this.logger.warn("Could not reload OpenCode config",{error:(0,g.getErrorMessage)(e)})}t.json({success:!0,message:`Synced ${h} agent(s) to local files${f?" - config reloaded":""}`,count:h,reloaded:f})}catch(e){this.logger.error("Failed to sync agents",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGenerateJournal(e,s){try{const{requestId:r}=e.body
|
|
221
234
|
if(!r)return void s.status(400).json({success:!1,error:"requestId is required"})
|
|
222
235
|
if(!/^req\d+$/.test(r))return void s.status(400).json({success:!1,error:"Invalid requestId format"})
|
|
223
236
|
this.logger.info("Generating journal for request",{requestId:r})
|
|
@@ -292,7 +305,7 @@ t.json({success:!0,data:h})}catch(e){this.logger.error("Spawn session failed",{e
|
|
|
292
305
|
if(!s?.id)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
293
306
|
const{agentName:r}=e.params,o=await this.apiClient.get(`/api/projects/${s.id}/agents`),n=(o?.data||o||[]).find((e=>e.name===r))
|
|
294
307
|
if(!n)return void t.status(404).json({success:!1,error:`Agent '${r}' not found`})
|
|
295
|
-
t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}readProjectConfig(){if(this.cachedProjectConfig&&Date.now()-this.cachedProjectConfig.timestamp<
|
|
308
|
+
t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}readProjectConfig(){if(this.cachedProjectConfig&&Date.now()-this.cachedProjectConfig.timestamp<I.PROJECT_CONFIG_TTL)return this.cachedProjectConfig.config
|
|
296
309
|
const e=a.default.join(this.options.projectPath,".alchemy_lab","project.json")
|
|
297
310
|
try{if(!c.default.existsSync(e))return this.cachedProjectConfig={config:null,timestamp:Date.now()},null
|
|
298
311
|
const t=c.default.readFileSync(e,"utf-8"),s=JSON.parse(t)
|
|
@@ -354,7 +367,8 @@ if(r.success&&r.data?.serverId){this.serverId=r.data.serverId,this.logger.info("
|
|
|
354
367
|
const e=r.data.versionWarnings
|
|
355
368
|
if(e&&e.length>0){const t=e.some((e=>"required"===e.severity))
|
|
356
369
|
for(const t of e)"required"===t.severity?(this.logger.error("[UPDATE REQUIRED] "+t.message),console.error("\n\x1b[31m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 UPDATE REQUIRED \u2551"),console.error("\x1b[31m\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\x1b[0m"),console.error(`\x1b[31m${t.message}\x1b[0m\n`)):(this.logger.warn("[UPDATE RECOMMENDED] "+t.message),console.warn(`\n\x1b[33m[UPDATE RECOMMENDED] ${t.message}\x1b[0m\n`))
|
|
357
|
-
this.versionWarnings=e,t&&(this._pendingUpdateBlock=!0)}}else this.logger.warn("Registration failed, continuing without remote connection",{error:r.error?.message||"Unknown error"})}startHeartbeat(){this.serverId&&(this.heartbeatInterval=setInterval((async()=>{try{const
|
|
370
|
+
this.versionWarnings=e,t&&(this._pendingUpdateBlock=!0)}}else this.logger.warn("Registration failed, continuing without remote connection",{error:r.error?.message||"Unknown error"})}startHeartbeat(){this.serverId&&(this.heartbeatInterval=setInterval((async()=>{if(this.projectId)try{const{touchHeartbeat:e}=await t.e(640).then(t.bind(t,2640))
|
|
371
|
+
e(this.projectId)}catch{}try{const e=await this.apiClient.post(`/api/servers/${this.serverId}/heartbeat`,{timestamp:(new Date).toISOString(),clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}),t=e?.data?.versionWarnings
|
|
358
372
|
if(t&&t.length>0){if(t.map((e=>`${e.component}:${e.severity}`)).join(",")!==this.versionWarnings.map((e=>`${e.component}:${e.severity}`)).join(",")){for(const e of t)"required"===e.severity?(this.logger.error("[UPDATE REQUIRED] "+e.message),console.error(`\n\x1b[31m[UPDATE REQUIRED] ${e.message}\x1b[0m\n`)):(this.logger.warn("[UPDATE RECOMMENDED] "+e.message),console.warn(`\n\x1b[33m[UPDATE RECOMMENDED] ${e.message}\x1b[0m\n`))
|
|
359
373
|
this.versionWarnings=t}!t.some((e=>"required"===e.severity))&&!this._pendingUpdateBlock||this.updateBlocked||await this.enterUpdateMode()}else this.versionWarnings.length>0&&(this.versionWarnings=[])}catch(e){this.logger.warn("Heartbeat failed",{error:(0,g.getErrorMessage)(e)})}}),3e4))}startSSEEventForwarder(){this.logger.info("Starting SSE event forwarder"),this.connectToOpenCodeSSE(),this.sseForwarderInterval=setInterval((async()=>{await this.flushSSEEvents()}),2e3)}stopSSEEventForwarder(){this.sseForwarderInterval&&(clearInterval(this.sseForwarderInterval),this.sseForwarderInterval=void 0),this.sseAbortController&&(this.sseAbortController.abort(),this.sseAbortController=void 0),this.flushSSEEvents().catch((e=>this.logger.warn("Failed to flush remaining SSE events",{error:(0,g.getErrorMessage)(e)})))}async connectToOpenCodeSSE(){const e=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
360
374
|
if(!c.default.existsSync(e))return this.logger.debug("SSE forwarder: opencode.json not found, will retry"),void setTimeout((()=>this.connectToOpenCodeSSE()),5e3)
|
|
@@ -369,8 +383,8 @@ if(e)break
|
|
|
369
383
|
o+=r.decode(s,{stream:!0})
|
|
370
384
|
const n=o.split("\n")
|
|
371
385
|
o=n.pop()||""
|
|
372
|
-
for(const e of n)if(e.startsWith("data: "))try{const t=JSON.parse(e.slice(6))
|
|
373
|
-
this.sseEventBuffer.push({type:
|
|
386
|
+
for(const e of n)if(e.startsWith("data: "))try{const t=JSON.parse(e.slice(6)),s=t.type||"unknown"
|
|
387
|
+
this.sseEventBuffer.push({type:s,data:t}),this.handleSSEHookEvent(t).catch((e=>this.logger.warn("SSE hook event processing failed",{type:t?.type,error:(0,g.getErrorMessage)(e)}))),"session.idle"===s&&this.flushSSEEvents().catch((e=>this.logger.debug("Immediate SSE flush failed",{error:(0,g.getErrorMessage)(e)})))}catch{}}}catch(e){"AbortError"===e?.name||(e?.message+"").includes("terminated")||this.logger.debug("SSE forwarder stream ended",{error:(0,g.getErrorMessage)(e)})}this.sseForwarderInterval&&(this.logger.info("SSE forwarder reconnecting in 3s"),setTimeout((()=>this.connectToOpenCodeSSE()),3e3))})()}catch(e){"AbortError"===e?.name||(e?.message+"").includes("terminated")||(this.logger.debug("SSE forwarder connection failed",{error:(0,g.getErrorMessage)(e)}),this.sseForwarderInterval&&setTimeout((()=>this.connectToOpenCodeSSE()),5e3))}}async handleSSEHookEvent(e){const t=e?.type
|
|
374
388
|
if(t&&this.projectId)if("session.idle"===t){const t=e?.properties?.sessionID||e?.sessionID||""
|
|
375
389
|
if(!t)return
|
|
376
390
|
this.logger.debug("SSE hook: session_idle",{sessionId:t}),await this.hookExecutor.processEvent({eventType:"session_idle",sessionId:t,variables:{sessionId:t}})}else if("session.created"===t){const t=e?.properties?.info||e?.info,s=t?.id||""
|
|
@@ -422,14 +436,17 @@ const e=encodeURIComponent(t.requestId),[r,o]=await Promise.all([this.apiClient.
|
|
|
422
436
|
return{success:!0,data:{...r.data,tasks:o.data||[]}}}default:return{success:!1,error:"Unknown request action: "+e}}}catch(e){return{success:!1,error:(0,g.getErrorMessage)(e)}}}handleCLIBridgeStatus(e,t){t.json({uiConnected:this.cliBridge.isUIConnected()})}async start(){this.logger.info("Starting AlchemyActionServer",{projectPath:this.options.projectPath,port:this.port})
|
|
423
437
|
for(let e=0;10>e;e++)try{await new Promise(((e,t)=>{try{this.server=this.app.listen(this.port,"127.0.0.1",(()=>{this.logger.info("HTTP server started",{port:this.port}),this.cliBridge.attach(this.server),e()})),this.server.on("error",(e=>{e.code,t(e)}))}catch(e){t(e)}}))
|
|
424
438
|
break}catch(t){if("EADDRINUSE"===t?.code&&9>e){this.logger.info(`Port ${this.port} in use, trying ${this.port+1}`),this.port++
|
|
425
|
-
continue}throw Error(`Port ${this.port} is already in use (tried 10 ports)`)}try{
|
|
439
|
+
continue}throw Error(`Port ${this.port} is already in use (tried 10 ports)`)}try{const e=a.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
|
|
440
|
+
c.default.mkdirSync(a.default.dirname(e),{recursive:!0}),c.default.writeFileSync(e,JSON.stringify({port:this.port},null,2),"utf-8"),this.logger.info("Wrote action-server port file",{port:this.port,path:e})}catch(e){this.logger.warn("Failed to write action-server port file",{error:(0,g.getErrorMessage)(e)})}try{const e=this.readProjectConfig()
|
|
441
|
+
if(e?.id){const{upsertEntry:s}=await t.e(640).then(t.bind(t,2640))
|
|
442
|
+
s({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,g.getErrorMessage)(e)})}try{await this.register(),this.startHeartbeat()}catch(e){const t=(0,g.getErrorMessage)(e)
|
|
426
443
|
t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=5000`,t={"Content-Type":"application/json",Accept:"application/json"}
|
|
427
444
|
this.apiClient.authToken&&(t.Authorization="Bearer "+this.apiClient.authToken)
|
|
428
445
|
const s=process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"
|
|
429
446
|
t["X-Service-Secret"]=s
|
|
430
447
|
const r=await fetch(e,{headers:t,signal:AbortSignal.timeout(8e3)})
|
|
431
448
|
if(r.ok){const e=await r.json(),t=e?.data?.settings||e?.settings
|
|
432
|
-
void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel}))}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,g.getErrorMessage)(e)})}this.startPolling(),this.projectId&&this.startSSEEventForwarder(),this.projectId&&this.startStuckSessionWatchdog(),this.projectId&&this.startTimerHookScheduler()}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,g.getErrorMessage)(t)})}}this.trackedChildPids.clear()
|
|
449
|
+
void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel}))}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,g.getErrorMessage)(e)})}this.startPolling(),this.projectId&&this.startSSEEventForwarder(),this.projectId&&this.startStuckSessionWatchdog(),this.projectId&&this.startTimerHookScheduler(),this.startMcpStaleCleanup()}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,g.getErrorMessage)(t)})}}this.trackedChildPids.clear()
|
|
433
450
|
try{const e=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
434
451
|
if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),s=JSON.parse(t)
|
|
435
452
|
if(s.pid)try{process.kill(-s.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:s.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(s.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:s.pid,error:(0,g.getErrorMessage)(e)})}}}}catch{}}startTimerHookScheduler(){this.timerHookScheduler=new y.TimerHookScheduler({hookExecutor:this.hookExecutor,apiClient:this.apiClient,logger:this.logger,getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath}),this.timerHookScheduler.start()}startStuckSessionWatchdog(){this.stuckSessionWatchdog=new m.StuckSessionWatchdog({getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,logger:this.logger,onStuckDetected:e=>this.handleStuckSessionDetected(e),onStuckResolved:e=>this.handleStuckSessionResolved(e)}),this.stuckSessionWatchdog.start()}handleStuckSessionDetected(e){this.sseEventBuffer.push({type:"session.stuck",data:{type:"session.stuck",properties:{sessionId:e.sessionId,messageId:e.messageId,detectedAt:e.detectedAt,sessionTitle:e.sessionTitle,retryCount:e.retryCount}}}),this.logger.warn("Stuck session detected",{sessionId:e.sessionId,title:e.sessionTitle})}handleStuckSessionResolved(e){this.sseEventBuffer.push({type:"session.unstuck",data:{type:"session.unstuck",properties:{sessionId:e}}})}handleGetStuckSessions(e,t){const s=this.stuckSessionWatchdog?.getStuckSessions()??[]
|
|
@@ -442,8 +459,11 @@ r.success?t.json({success:!0}):t.status(400).json({success:!1,error:r.error})}as
|
|
|
442
459
|
for(const e of this.versionWarnings)console.error(`\x1b[31m ${e.message}\x1b[0m`)
|
|
443
460
|
console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.timerHookScheduler?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
|
|
444
461
|
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.clear(),this.cliBridge.close(),this.logger.info("Update-required mode active. HTTP server still running on port "+this.port)}}async stop(){this.logger.info("Stopping AlchemyActionServer...")
|
|
445
|
-
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}
|
|
446
|
-
|
|
462
|
+
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.timerHookScheduler?.stop(),this.stopSSEEventForwarder(),this.mcpStaleCleanupInterval&&(clearInterval(this.mcpStaleCleanupInterval),this.mcpStaleCleanupInterval=void 0)
|
|
463
|
+
try{const e=a.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
|
|
464
|
+
c.default.existsSync(e)&&c.default.unlinkSync(e)}catch{}if(this.projectId)try{const{removeEntry:e}=await t.e(640).then(t.bind(t,2640))
|
|
465
|
+
e(this.projectId)}catch{}if(this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval),this.heartbeatInterval&&clearInterval(this.heartbeatInterval),this.serverId)try{await this.apiClient.post(`/api/servers/${this.serverId}/disconnect`)}catch{}this.cliBridge.close(),await new Promise((e=>{if(this.server){const t=setTimeout((()=>{this.logger.warn("Graceful shutdown timed out after 10s, forcing close"),e()}),1e4)
|
|
466
|
+
this.server.close((()=>{clearTimeout(t),e()}))}else e()})),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=I,I.PROJECT_CONFIG_TTL=3e4},2424(e,exports,t){"use strict"
|
|
447
467
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.ensureRequestWorktree=async function(e,t,i){const a="requests/"+t,c=(0,r.join)(e,n.ALCHEMY_DIR_NAME,"worktrees"),l=(0,r.join)(c,t)
|
|
448
468
|
if((0,s.existsSync)(l))return void i.info("Worktree already exists on disk, skipping pre-spawn creation",{requestId:t,worktreePath:l})
|
|
449
469
|
const d=(0,o.simpleGit)(e);(0,s.mkdirSync)(c,{recursive:!0})
|
|
@@ -542,7 +562,26 @@ Object.defineProperty(exports,"createLogger",{enumerable:!0,get(){return r.creat
|
|
|
542
562
|
const o=s(t(2782))
|
|
543
563
|
exports.defaultLogger=o.default
|
|
544
564
|
const n=t(6540)
|
|
545
|
-
Object.defineProperty(exports,"LogLevel",{enumerable:!0,get(){return n.LogLevel}}),exports.default=o.default},
|
|
565
|
+
Object.defineProperty(exports,"LogLevel",{enumerable:!0,get(){return n.LogLevel}}),exports.default=o.default},3614(e,exports,t){"use strict"
|
|
566
|
+
async function s(e,t,s){const r=a.default.join(e,".alchemy_lab","project.json")
|
|
567
|
+
try{const o=await i.default.readFile(r,"utf-8"),n=JSON.parse(o)
|
|
568
|
+
n.id=t,n.registeredRemotely=!0,n.metadata&&(n.metadata.basePath=e,n.metadata.alchemyPath=a.default.join(e,".alchemy_lab")),await i.default.writeFile(r,JSON.stringify(n,null,2),"utf-8"),s.info("Updated project.json",{projectJsonPath:r,newProjectId:t})}catch(e){s.warn("Could not update project.json (will be created on launch)",{error:e instanceof Error?e.message:e+""})}}async function r(e,t,s){const r=a.default.join(t,".alchemy_lab","opencode","opencode-local.db")
|
|
569
|
+
try{await i.default.access(r)}catch{return void s.info("No OpenCode database found, skipping session directory update")}try{await l("sqlite3",[r,`UPDATE session SET directory = replace(directory, '${e.replace(/'/g,"''")}', '${t.replace(/'/g,"''")}') WHERE directory LIKE '${e.replace(/'/g,"''")}%';`],{timeout:1e4}),s.info("Updated OpenCode session directories",{sourcePath:e,targetPath:t})}catch(e){s.warn("Could not update OpenCode session directories",{error:e instanceof Error?e.message:e+""})}}async function o(e){const t=[]
|
|
570
|
+
for(const s of d){const r=a.default.join(e,s)
|
|
571
|
+
t.push(i.default.rm(r,{recursive:!0,force:!0}).catch((()=>{})))}t.push((async()=>{try{const t=a.default.join(e,".alchemy_lab"),s=await i.default.readdir(t)
|
|
572
|
+
await Promise.all(s.filter((e=>e.endsWith(".lock"))).map((e=>i.default.unlink(a.default.join(t,e)).catch((()=>{})))))}catch{}})()),await Promise.all(t)}var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
573
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleForkFilesystemAction=async function(e,t){const{logger:n}=t,{sourcePath:a,targetPath:c,newProjectId:h}=e
|
|
574
|
+
if(!a||!c||!h)return{success:!1,message:"sourcePath, targetPath, and newProjectId are required",error:"missing_params"}
|
|
575
|
+
n.info("Starting filesystem fork",{sourcePath:a,targetPath:c,newProjectId:h})
|
|
576
|
+
try{const e=await async function(e,t,s){const r=e.endsWith("/")?e:e+"/"
|
|
577
|
+
try{const o=[...d,...u].flatMap((e=>["--exclude",e]))
|
|
578
|
+
return await l("rsync",["-a",...o,r,t],{timeout:3e5}),s.info("Directory copied via rsync",{source:e,target:t}),!0}catch(e){s.warn("rsync failed, falling back to cp -R",{error:e instanceof Error?e.message:e+""})}try{await i.default.mkdir(t,{recursive:!0})
|
|
579
|
+
const r=e.endsWith("/")?e+".":e+"/."
|
|
580
|
+
return await l("cp",["-R",r,t+"/"],{timeout:3e5}),s.info("Directory copied via cp -R",{source:e,target:t}),!0}catch(e){return s.error("cp -R failed",{error:e instanceof Error?e.message:e+""}),!1}}(a,c,n)
|
|
581
|
+
return e?(await Promise.all([s(c,h,n),r(a,c,n),o(c)]),n.info("Filesystem fork complete",{targetPath:c,newProjectId:h}),{success:!0,message:"Project forked to "+c,data:{targetPath:c,newProjectId:h}}):{success:!1,message:"Failed to copy project directory",error:"copy_failed"}}catch(e){const t=e instanceof Error?e.message:e+""
|
|
582
|
+
n.error("Filesystem fork failed",{error:t})
|
|
583
|
+
try{await i.default.rm(c,{recursive:!0,force:!0}),n.info("Cleaned up partial fork directory",{targetPath:c})}catch(e){n.warn("Failed to clean up partial fork",{error:e instanceof Error?e.message:e+""})}return{success:!1,message:"Filesystem fork failed: "+t,error:t}}}
|
|
584
|
+
const i=n(t(1943)),a=n(t(6928)),c=t(5317),l=(0,t(9023).promisify)(c.execFile),d=[".alchemy_lab/opencode.json",".alchemy_lab/server.log",".alchemy_lab/action-server-server.log",".alchemy_lab/mcp-identity-debug.log",".alchemy_lab/worktrees",".opencode/node_modules"],u=[".alchemy_lab/*.lock"]},3785(module){"use strict"
|
|
546
585
|
module.exports=require("readline")},3807(module){"use strict"
|
|
547
586
|
module.exports=require("simple-git")},3962(e,exports,t){"use strict"
|
|
548
587
|
function s(){if(n)return n
|
|
@@ -643,7 +682,33 @@ return o.set(e,t),!1},exports.tryMarkMessageProcessed=function(e,t){if(!t)return
|
|
|
643
682
|
if(i.get(e)===t)return!1
|
|
644
683
|
if(i.set(e,t),i.size>100){const e=i.keys().next().value
|
|
645
684
|
e&&i.delete(e)}return!0}
|
|
646
|
-
const s=new Map,r=new Map,o=new Map,n=2e3,i=new Map},
|
|
685
|
+
const s=new Map,r=new Map,o=new Map,n=2e3,i=new Map},4654(e,exports,t){"use strict"
|
|
686
|
+
async function s(e,t){try{const s=await fetch(e+"/session/status",{signal:AbortSignal.timeout(5e3)})
|
|
687
|
+
if(!s.ok)return"error"
|
|
688
|
+
const r=await s.json()
|
|
689
|
+
return r[t]?.type||"unknown"}catch{return"error"}}function r(e){return new Promise((t=>setTimeout(t,e)))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handlePromptAndWaitAction=async function(e,t){const{logger:n,projectPath:i,projectId:a,apiClient:c,trackChildPid:l}=t,{sessionId:d,message:u,prompt:h,timeoutSeconds:g=300,agent:p="coder",title:f}=e,m=u||h
|
|
690
|
+
if(!m)return{success:!1,message:"message is required",error:"missing_params"}
|
|
691
|
+
const y=(0,o.getOpenCodeServerUrl)(i,n)
|
|
692
|
+
if(!y)return{success:!1,message:"OpenCode not running",error:"no_opencode"}
|
|
693
|
+
const v=Date.now()
|
|
694
|
+
let w=d
|
|
695
|
+
if(w){try{const e=await fetch(`${y}/session/${w}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:m}],...t.cachedDefaultModel?(()=>{const[e,...s]=t.cachedDefaultModel.split("/")
|
|
696
|
+
return{model:{providerID:e,modelID:s.join("/")}}})():{}}),signal:AbortSignal.timeout(1e4)})
|
|
697
|
+
if(!e.ok)return{success:!1,message:"prompt_async failed: "+e.status,error:"prompt_failed"}}catch(e){return{success:!1,message:"Failed to inject prompt: "+(e instanceof Error?e.message:e+""),error:"prompt_failed"}}n.info("Injected prompt into existing session",{sessionId:w})}else{const e=await(0,o.spawnActionAgent)({agent:p,prompt:m,cwd:i,projectPath:i,projectId:a,title:f||"prompt-and-wait",apiClient:c,logger:n,trackChildPid:l,getDefaultModel(){return t.cachedDefaultModel||void 0}})
|
|
698
|
+
if(!e.success||!e.sessionId)return{success:!1,message:"Failed to spawn session: "+e.error,error:"spawn_failed"}
|
|
699
|
+
w=e.sessionId,n.info("Spawned session for prompt-and-wait",{sessionId:w})}const I=await async function(e,t,o){const n=Date.now()+1e3*o
|
|
700
|
+
let i=!1
|
|
701
|
+
const a=Date.now()+15e3
|
|
702
|
+
for(;Date.now()<a&&Date.now()<n;){const o=await s(e,t)
|
|
703
|
+
if("busy"===o||"running"===o){i=!0
|
|
704
|
+
break}await r(500)}for(;Date.now()<n;){const o=await s(e,t)
|
|
705
|
+
if("error"===o)return{status:"error"}
|
|
706
|
+
if(("idle"===o||"completed"===o)&&i)return{status:"completed"}
|
|
707
|
+
"busy"!==o&&"running"!==o||(i=!0),await r(2e3)}return{status:"timeout"}}(y,w,g),k=Date.now()-v,S=await async function(e,t){try{const s=await fetch(`${e}/session/${t}/message?limit=200`,{signal:AbortSignal.timeout(1e4)})
|
|
708
|
+
return s.ok?(await s.json()).map((e=>({role:e.info?.role||"unknown",text:(e.parts||[]).filter((e=>"text"===e.type)).map((e=>e.text)).join("\n"),toolCalls:(e.parts||[]).filter((e=>"tool_use"===e.type)).map((e=>({name:e.name,id:e.id}))),timestamp:e.info?.time?.created}))):[]}catch{return[]}}(y,w),C=function(e){return e.reduce(((e,t)=>e+(t.toolCalls?.length||0)),0)}(S),j=function(e){for(let t=e.length-1;t>=0;t--)if("assistant"===e[t].role&&e[t].text)return e[t].text
|
|
709
|
+
return""}(S)
|
|
710
|
+
return{success:!0,message:"Prompt completed",data:{sessionId:w,status:I.status,messages:S,toolCallCount:C,elapsedMs:k,finalResponse:j}}}
|
|
711
|
+
const o=t(8148)},4769(e,exports,t){"use strict"
|
|
647
712
|
function s(e){return(0,o.getOpenCodeServerUrl)(e.projectPath,e.logger)}async function r(e,t,r){const o=s(r)
|
|
648
713
|
if(!o||!t)return!1
|
|
649
714
|
try{const s=await async function(e,t){try{const s=await t.apiClient.get(`/api/projects/${t.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),r=s?.data||s
|
|
@@ -653,8 +718,8 @@ if(r.cachedDefaultModel){const[e,...t]=r.cachedDefaultModel.split("/")
|
|
|
653
718
|
n={providerID:e,modelID:t.join("/")}}const i=`${o}/session/${t}/prompt_async`,a=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:e}],...s?{agent:s}:{},...n?{model:n}:{}})})
|
|
654
719
|
return a.ok?(r.logger.debug("Message sent to session",{sessionId:t}),!0):(r.logger.warn("Failed to send message to session",{sessionId:t,status:a.status}),!1)}catch(e){return r.logger.warn("Error sending message to session",{sessionId:t,error:e+""}),!1}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.executeBackground=async function(e,t,s,r,n){const i=await(0,o.spawnActionAgent)({agent:e.agent,prompt:t,cwd:n.projectPath,projectPath:n.projectPath,projectId:n.projectId,title:"Hook: "+e.name,requestId:r,parentSessionId:"independent_session"===e.target?void 0:s||void 0,apiClient:n.apiClient,logger:n.logger,trackChildPid:n.trackChildPid})
|
|
655
720
|
i.success||n.logger.warn("Background hook spawn failed",{hook:e.name,error:i.error})},exports.executeSameSession=async function(e,t,s){await r(e,t,s)},exports.executeInjectPrompt=async function(e,s,o,n,i){let a=[]
|
|
656
|
-
if("ancestor_chain"===e.target)try{const e=await i.apiClient.get(`/api/projects/${i.projectId}/cli-sessions/external/opencode/${encodeURIComponent(o)}/ancestors`)
|
|
657
|
-
a=(e?.data||e||[]).map((e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title}))),i.logger.debug("inject_prompt: ancestors fetched",{sessionId:o,count:a.length,ancestors:a.map((e=>e.externalSessionId))})}catch(e){i.logger.warn("inject_prompt: ancestors fetch failed, falling back to parent",{sessionId:o,error:e+""})}if(0===a.length)try{const e=await i.apiClient.get(`/api/projects/${i.projectId}/cli-sessions/external/opencode/${encodeURIComponent(o)}/parent`),t=e?.data?.parentExternalSessionId||e?.parentExternalSessionId
|
|
721
|
+
if("ancestor_chain"===e.target)try{const e=await i.apiClient.get(`/api/projects/${i.projectId}/cli-sessions/external/opencode/${encodeURIComponent(o)}/ancestors`),t=/^ses_[0-9A-Z]{26}$/
|
|
722
|
+
a=(e?.data||e||[]).map((e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title}))).filter((e=>!(!e.externalSessionId||t.test(e.externalSessionId))||(i.logger.warn("inject_prompt: skipping corrupt ancestor (internal-ULID-format externalSessionId)",{sessionId:o,corruptExternalSessionId:e.externalSessionId}),!1))),i.logger.debug("inject_prompt: ancestors fetched",{sessionId:o,count:a.length,ancestors:a.map((e=>e.externalSessionId))})}catch(e){i.logger.warn("inject_prompt: ancestors fetch failed, falling back to parent",{sessionId:o,error:e+""})}if(0===a.length)try{const e=await i.apiClient.get(`/api/projects/${i.projectId}/cli-sessions/external/opencode/${encodeURIComponent(o)}/parent`),t=e?.data?.parentExternalSessionId||e?.parentExternalSessionId
|
|
658
723
|
t&&(a=[{externalSessionId:t}])}catch{}if(0===a.length)return void i.logger.debug("inject_prompt: no ancestors found",{sessionId:o})
|
|
659
724
|
const c=a.map((e=>`${e.agentType||"agent"}${e.title?" "+e.title:""} (${e.externalSessionId})`)).join(" \u2192 "),{interpolatePrompt:l}=await Promise.resolve().then(t.bind(t,4627)),d={...n,chainInfo:c}
|
|
660
725
|
i.logger.debug("inject_prompt: interpolating",{variables:Object.keys(d),chainInfo:c,sessionId:n.sessionId,agentType:n.agentType})
|
|
@@ -672,17 +737,17 @@ const o=t(8148)},5086(module){"use strict"
|
|
|
672
737
|
module.exports=require("ws")},5124(module){"use strict"
|
|
673
738
|
module.exports=require("winston")},5317(module){"use strict"
|
|
674
739
|
module.exports=require("child_process")},5607(e,exports,t){"use strict"
|
|
675
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.executeAction=async function(e,t,s){const r=
|
|
740
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.executeAction=async function(e,t,s){const r=f[e]
|
|
676
741
|
if(!r)return{success:!1,message:"Unknown action type: "+e,error:"No handler registered for action type: "+e}
|
|
677
|
-
try{return await r(t,s)}catch(t){const r=(0,
|
|
742
|
+
try{return await r(t,s)}catch(t){const r=(0,p.getErrorMessage)(t)
|
|
678
743
|
return s.logger.error("Action handler failed",{actionType:e,error:r}),{success:!1,message:"Action failed: "+r,error:r}}}
|
|
679
|
-
const s=t(6636),r=t(7453),o=t(5700),n=t(848),i=t(9735),a=t(7767),c=t(9266),l=t(6555),d=t(
|
|
744
|
+
const s=t(6636),r=t(7453),o=t(5700),n=t(848),i=t(9735),a=t(7767),c=t(9266),l=t(6555),d=t(3614),u=t(4654),h=t(7078),g=t(1405),p=t(8824),f={revert_request:s.handleRevertRequestAction,"revert-request":s.handleRevertRequestAction,complete_request:r.handleCompleteRequestAction,"complete-request":r.handleCompleteRequestAction,"finalize-request":n.handleFinalizeRequestAction,"approve-request":i.handleApproveRequestAction,"approve-and-work-request":a.handleApproveAndWorkRequestAction,"restart-mcp":o.handleRestartMcpAction,"opencode-proxy":c.handleOpenCodeProxyAction,"run-tests":l.handleRunTestsAction,async"inject-prompt"(e,s){const{sessionId:r,message:o}=e
|
|
680
745
|
if(!r||!o)return{success:!1,message:"sessionId and message are required",error:"missing_params"}
|
|
681
746
|
const n=await Promise.resolve().then(t.t.bind(t,9896,23)),i=(await Promise.resolve().then(t.t.bind(t,6928,23))).join(s.projectPath,".alchemy_lab","opencode.json")
|
|
682
747
|
let a
|
|
683
748
|
try{a="http://127.0.0.1:"+JSON.parse(n.readFileSync(i,"utf-8")).port}catch{return{success:!1,message:"Cannot read OpenCode config",error:"no_opencode"}}try{const e=await fetch(`${a}/session/${r}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:o}],...s.cachedDefaultModel?(()=>{const[e,...t]=s.cachedDefaultModel.split("/")
|
|
684
749
|
return{model:{providerID:e,modelID:t.join("/")}}})():{}}),signal:AbortSignal.timeout(1e4)})
|
|
685
|
-
return e.ok?{success:!0,message:"Prompt injected into session "+r}:{success:!1,message:"prompt_async failed: "+e.status,error:"prompt_failed"}}catch(e){return{success:!1,message:(0,
|
|
750
|
+
return e.ok?{success:!0,message:"Prompt injected into session "+r}:{success:!1,message:"prompt_async failed: "+e.status,error:"prompt_failed"}}catch(e){return{success:!1,message:(0,p.getErrorMessage)(e),error:"prompt_error"}}},"fork-filesystem":d.handleForkFilesystemAction,"prompt-and-wait":u.handlePromptAndWaitAction,"run-ab-prompt":u.handlePromptAndWaitAction,"spawn-auto-planner":h.handleSpawnAutoPlannerAction,"start-building":g.handleStartBuildingAction}},5671(e,exports,t){"use strict"
|
|
686
751
|
function s(e){const[t,...s]=e.split("/")
|
|
687
752
|
return{provider:t||"anthropic",modelId:s.join("/")||e}}function r(e){const t={anthropic:!1,openai:!1}
|
|
688
753
|
try{const r=(0,a.join)(e,"opencode.json")
|
|
@@ -758,34 +823,34 @@ let i=`You are a test runner agent. Your job is to verify that request ${e.local
|
|
|
758
823
|
for(let e=0;e<n.length;e++){const t=n[e]
|
|
759
824
|
i+=`\n### ${e+1}. ${t.name}${!1!==t.required?" (REQUIRED)":" (optional)"}\n- **Command:** \`${t.command}\`\n- **Timeout:** ${t.timeout||300}s\n`}if(i+=r?`\n## Instructions\n\nYou are in interactive mode. Present the test plan to the user and **wait for their direction** before running anything.\n\n1. Greet the user and list the test commands available for this process.\n2. Wait for the user to tell you to proceed (they may want to run specific commands, skip some, or run all).\n3. Use \`execute_test_command\` with processId="${process.id}" and the command name to run each command.\n4. After each command, explain the result and wait for the user before continuing.\n5. When the user is satisfied, call \`report_test_results\` with the overall status.\n\nYou are read-only \u2014 do NOT modify any source code. Your job is to verify, not fix.`:`\n## Instructions\n\nSetup commands have already been executed. Use the \`execute_test_command\` tool to run each test command:\n\n1. For each command above, call \`execute_test_command\` with processId="${process.id}" and the command name.\n2. Check if it passed or failed.\n3. If it fails: try to determine why \u2014 is it a real bug, a flaky test, or an environment issue? Retry once if it looks flaky.\n4. When all commands are done, call \`report_test_results\` with the overall status and per-command results.\n - If all REQUIRED commands passed \u2192 status "passed"\n - If any REQUIRED command failed \u2192 status "failed" with diagnosis of each failure\n\nYou are read-only \u2014 do NOT modify any source code. Your job is to verify, not fix.`,o.verifyAcceptanceCriteria&&e.acceptanceCriteria?.length>0){i+="\n\n## Acceptance Criteria Verification\n\nAfter running all commands, verify each acceptance criterion by reading code changes and running ad-hoc commands as needed. This is **advisory only** \u2014 it does not affect the overall pass/fail status.\n\nCriteria to verify:\n"
|
|
760
825
|
for(const t of e.acceptanceCriteria)i+=`- ${t}\n`
|
|
761
|
-
i+='\nFor each criterion, report in the `acceptanceCriteria` array of your results:\n- **criterion**: The criterion text\n- **status**: "verified" (confirmed in code), "unverified" (not found/not implemented), or "unclear" (ambiguous)\n- **evidence**: How you verified it, or why you couldn\'t\n'}return i}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRunTestsAction=async function(e,d){const{requestId:u,testProcessIds:h,parentSessionId:g,mode:p}=e,f="interactive"===p,{logger:m,apiClient:y,projectId:v,projectPath:w,trackChildPid:
|
|
826
|
+
i+='\nFor each criterion, report in the `acceptanceCriteria` array of your results:\n- **criterion**: The criterion text\n- **status**: "verified" (confirmed in code), "unverified" (not found/not implemented), or "unclear" (ambiguous)\n- **evidence**: How you verified it, or why you couldn\'t\n'}return i}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRunTestsAction=async function(e,d){const{requestId:u,testProcessIds:h,parentSessionId:g,mode:p}=e,f="interactive"===p,{logger:m,apiClient:y,projectId:v,projectPath:w,trackChildPid:I}=d
|
|
762
827
|
if(!u)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
763
828
|
m.info("Starting run-tests action",{requestId:u,testProcessIds:h})
|
|
764
829
|
try{const d=await y.get(`/api/projects/${v}/requests/${u}`)
|
|
765
830
|
if(!d.success||!d.data)return{success:!1,message:`Request ${u} not found`,error:"Request not found"}
|
|
766
|
-
const p=d.data,
|
|
831
|
+
const p=d.data,k=p.branchName||"requests/"+u
|
|
767
832
|
let S
|
|
768
833
|
if(h?.length)S=(await Promise.all(h.map((e=>y.get(`/api/projects/${v}/test-processes/${e}`))))).filter((e=>e.success&&e.data)).map((e=>e.data))
|
|
769
834
|
else{const e=await y.get(`/api/projects/${v}/test-processes/active`)
|
|
770
835
|
S=e.success&&e.data?Array.isArray(e.data)?e.data:[e.data]:[]}if(0===S.length)return{success:!1,message:"No test processes configured for this project. Set up testing first.",error:"No test processes"}
|
|
771
|
-
const C=(0,o.join)(w,c,l),
|
|
772
|
-
if((0,r.existsSync)(
|
|
773
|
-
await e.raw(["reset","--hard",
|
|
774
|
-
try{await
|
|
775
|
-
try{if(!(await
|
|
776
|
-
await
|
|
777
|
-
m.info("Test worktree already exists, using as-is",{worktreePath:
|
|
836
|
+
const C=(0,o.join)(w,c,l),j=(0,o.join)(C,u),b=(0,n.simpleGit)(w)
|
|
837
|
+
if((0,r.existsSync)(j))try{const e=(0,n.simpleGit)(j)
|
|
838
|
+
await e.raw(["reset","--hard",k]),m.info("Reset test worktree to latest branch HEAD",{worktreePath:j,branchName:k})}catch(e){m.warn("Could not reset test worktree, recreating",{error:(0,i.getErrorMessage)(e)})
|
|
839
|
+
try{await b.raw(["worktree","remove","--force",j])}catch{}}if(!(0,r.existsSync)(j)){(0,r.mkdirSync)(C,{recursive:!0})
|
|
840
|
+
try{if(!(await b.branch()).all.includes(k))return{success:!1,message:`Branch ${k} not found`,error:"Branch not found"}
|
|
841
|
+
await b.raw(["worktree","add","--detach",j,k]),m.info("Created test worktree (detached)",{worktreePath:j,branchName:k})}catch(e){if(!(e+"").includes("already exists"))return{success:!1,message:"Failed to create test worktree: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}
|
|
842
|
+
m.info("Test worktree already exists, using as-is",{worktreePath:j})}}const{execSync:E}=await Promise.resolve().then(t.t.bind(t,5317,23))
|
|
778
843
|
for(const e of S){const t=e.config?.setup||[]
|
|
779
844
|
if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length})
|
|
780
|
-
for(const s of t)try{m.info("Setup command",{command:s,worktreePath:
|
|
845
|
+
for(const s of t)try{m.info("Setup command",{command:s,worktreePath:j}),E(s,{cwd:j,timeout:12e4,encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"/bin/sh",env:{...process.env,FORCE_COLOR:"0"}})}catch(t){m.warn("Setup command failed",{command:s,processName:e.name,exitCode:t.status,stderr:(t.stderr||"").slice(-500)})}}}const T=[],A=[]
|
|
781
846
|
for(const process of S){let t
|
|
782
847
|
const r=e.testRunIds?.[T.length]
|
|
783
|
-
try{r?(await y.patch(`/api/projects/${v}/test-runs/${r}`,{worktreePath:
|
|
848
|
+
try{r?(await y.patch(`/api/projects/${v}/test-runs/${r}`,{worktreePath:j}),t=(await y.get(`/api/projects/${v}/test-runs/${r}`)).data,T.push(t.id)):(t=(await y.post(`/api/projects/${v}/test-runs`,{requestId:u,testProcessId:process.id,worktreePath:j,parentSessionId:g})).data,T.push(t.id))}catch(e){m.error("Failed to get/create test run record",{requestId:u,processId:process.id,preCreatedId:r,error:(0,i.getErrorMessage)(e)})
|
|
784
849
|
continue}const o=s(p,process,0,v,f),n=f?"tester":"auto-tester"
|
|
785
|
-
try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:o,cwd:
|
|
850
|
+
try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:o,cwd:j,projectPath:w,projectId:v,title:`[${u}] Test: ${process.name}`,requestId:u,parentSessionId:g,allowParallel:!0,apiClient:y,logger:m,trackChildPid:I})
|
|
786
851
|
if(!e.success)throw Error(e.error||"Spawn failed")
|
|
787
|
-
A.push(t.id),e.sessionId&&y.patch(`/api/projects/${v}/test-runs/${t.id}`,{testerSessionId:e.sessionId}).catch((e=>m.warn("Failed to set testerSessionId on test run",{error:(0,i.getErrorMessage)(e)}))),m.info("Spawned test-runner agent",{testRunId:t.id,processName:process.name,worktreePath:
|
|
788
|
-
try{await y.patch(`/api/projects/${v}/test-runs/${t.id}`,{status:"error",results:{steps:[],summary:"Failed to spawn test agent: "+(0,i.getErrorMessage)(e)}})}catch{}}}return 0===A.length?{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}:{success:!0,message:`Started ${A.length} test run(s) for request ${u}`,data:{testRunIds:T,agentsSpawned:A.length,processCount:S.length,worktreePath:
|
|
852
|
+
A.push(t.id),e.sessionId&&y.patch(`/api/projects/${v}/test-runs/${t.id}`,{testerSessionId:e.sessionId}).catch((e=>m.warn("Failed to set testerSessionId on test run",{error:(0,i.getErrorMessage)(e)}))),m.info("Spawned test-runner agent",{testRunId:t.id,processName:process.name,worktreePath:j,pid:e.pid,sessionId:e.sessionId})}catch(e){m.error("Failed to spawn test-runner agent",{testRunId:t.id,error:(0,i.getErrorMessage)(e)})
|
|
853
|
+
try{await y.patch(`/api/projects/${v}/test-runs/${t.id}`,{status:"error",results:{steps:[],summary:"Failed to spawn test agent: "+(0,i.getErrorMessage)(e)}})}catch{}}}return 0===A.length?{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}:{success:!0,message:`Started ${A.length} test run(s) for request ${u}`,data:{testRunIds:T,agentsSpawned:A.length,processCount:S.length,worktreePath:j}}}catch(e){const t=(0,i.getErrorMessage)(e)
|
|
789
854
|
return m.error("run-tests action failed",{requestId:u,error:t}),{success:!1,message:"Failed to run tests: "+t,error:t}}}
|
|
790
855
|
const r=t(9896),o=t(6928),n=t(3807),i=t(8824),a=t(8148),c=".alchemy_lab",l="test-worktrees"},6563(e,exports,t){"use strict"
|
|
791
856
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.TimerHookScheduler=void 0
|
|
@@ -869,7 +934,16 @@ if(await s(t))return t}throw Error(`No available port found after ${t} attempts
|
|
|
869
934
|
const o=r(t(9278))},6895(module){"use strict"
|
|
870
935
|
module.exports=require("chalk")},6928(module){"use strict"
|
|
871
936
|
module.exports=require("path")},6982(module){"use strict"
|
|
872
|
-
module.exports=require("crypto")},
|
|
937
|
+
module.exports=require("crypto")},7078(e,exports,t){"use strict"
|
|
938
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleSpawnAutoPlannerAction=async function(e,t){const{requestId:o,parentSessionId:n}=e,{logger:i,apiClient:a,projectId:c,projectPath:l,trackChildPid:d}=t
|
|
939
|
+
if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
940
|
+
i.info("Starting spawn-auto-planner action",{requestId:o})
|
|
941
|
+
try{const e=await a.get(`/api/projects/${c}/requests/${o}`)
|
|
942
|
+
if(!e.success||!e.data)return{success:!1,message:`Request ${o} not found`,error:"Request not found"}
|
|
943
|
+
const t=e.data,s=Array.isArray(t.acceptanceCriteria)?t.acceptanceCriteria.map((e=>e.description||e)).join("\n- "):"",u=`Create a plan for request ${o}: ${t.title}\n\nDescription: ${t.description||"No description provided."}\n\n${s?"Acceptance Criteria:\n- "+s:""}\n\nResearch the codebase and create a comprehensive implementation plan.`,h=await(0,r.spawnActionAgent)({agent:"auto-planner",prompt:u,cwd:l,projectPath:l,projectId:c,title:`[${o}] Plan: ${t.title}`,requestId:o,parentSessionId:n,apiClient:a,logger:i,trackChildPid:d})
|
|
944
|
+
return h.success?(i.info("Spawned auto-planner for request",{requestId:o,sessionId:h.sessionId,pid:h.pid}),{success:!0,message:"Auto-planner spawned for request "+o,data:{sessionId:h.sessionId,pid:h.pid}}):{success:!1,message:"Failed to spawn auto-planner: "+h.error,error:h.error||"Spawn failed"}}catch(e){const t=(0,s.getErrorMessage)(e)
|
|
945
|
+
return i.error("spawn-auto-planner action failed",{requestId:o,error:t}),{success:!1,message:"Failed to spawn auto-planner: "+t,error:t}}}
|
|
946
|
+
const s=t(8824),r=t(8148)},7163(e,exports){"use strict"
|
|
873
947
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.debugTokenAccess=function(){},exports.compareTokens=function(){},exports.debugTokenStorage=function(){},exports.disableTokenDebugging=function(){},exports.enableTokenDebugging=function(){},new WeakMap},7210(e,exports,t){"use strict"
|
|
874
948
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.tryMarkMessageProcessed=exports.createToolCallHash=exports.isDuplicateToolCall=exports.determineEventToEmit=exports.interpolatePrompt=exports.evaluateCondition=exports.TimerConditionEvaluator=exports.TimerHookScheduler=exports.HookCache=exports.HookExecutor=void 0
|
|
875
949
|
var s=t(3077)
|
|
@@ -916,13 +990,17 @@ const d=e.data
|
|
|
916
990
|
if("pending_approval"!==d.status)return{success:!1,message:`Cannot approve request with status '${d.status}'. Only requests in 'pending_approval' status can be approved.`,error:"Invalid status: "+d.status}
|
|
917
991
|
if(!(await i.patch(`/api/projects/${a}/requests/${o}`,{status:"open"})).success)return{success:!1,message:"Failed to update request status",error:"Update failed"}
|
|
918
992
|
const u=Array.isArray(d.acceptanceCriteria)?d.acceptanceCriteria.map((e=>e.description||e)).join("\n- "):"",h=`Work on request ${o}: ${d.title}\n\nDescription: ${d.description}\n\n${u?"Acceptance Criteria:\n- "+u:""}\n\nThis request has just been approved. Please begin implementation.`
|
|
919
|
-
let g=!1,
|
|
920
|
-
if(
|
|
993
|
+
let g,p=!1,f=!1,m=d.cliOrigin?.sessionId||null
|
|
994
|
+
if(m)try{const e=await i.get(`/api/projects/${a}/cli-sessions/resolve/${m}`)
|
|
995
|
+
e.success&&e.data?.parent?.externalSessionId&&(g=e.data.parent.externalSessionId,m=g,n.info("Resolved parent session for notification",{cliOriginSession:d.cliOrigin.sessionId,parentSession:m}))}catch(e){n.warn("Failed to resolve session parent, using cliOrigin directly",{error:(0,s.getErrorMessage)(e)})}if(!m)try{const e=await i.get(`/api/projects/${a}/session-request-associations/by-request/${o}?activeOnly=true`)
|
|
996
|
+
if(e.success&&Array.isArray(e.data)&&e.data.length>0){const t=e.data[0].opencodeSessionId
|
|
997
|
+
try{const e=await i.get(`/api/projects/${a}/cli-sessions/resolve/${t}`)
|
|
998
|
+
e.data?.parent?.externalSessionId?(m=e.data.parent.externalSessionId,g=m):m=t}catch{m=t}}}catch{}if(m)try{await i.post(`/api/projects/${a}/session-messages/reports`,{fromSessionId:"gui-approval",toSessionId:m,status:"success",summary:`Request ${o} "${d.title}" has been approved. Please begin implementation.`,details:h}),p=!0,n.info("Stored session report for target session",{requestId:o,sessionId:m})
|
|
921
999
|
try{const e=(0,r.getOpenCodeServerUrl)(c,n)
|
|
922
|
-
if(e){const s=`\u2705 [Request Approved]\n\nRequest ${o} "${d.title}" has been approved.\nPlease begin implementation.\n\nDetails:\n${h}`,r=`${e}/session/${
|
|
1000
|
+
if(e){const s=`\u2705 [Request Approved]\n\nRequest ${o} "${d.title}" has been approved.\nPlease begin implementation.\n\nDetails:\n${h}`,r=`${e}/session/${m}/prompt_async`,i=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:s}],...t.cachedDefaultModel?(()=>{const[e,...s]=t.cachedDefaultModel.split("/")
|
|
923
1001
|
return{model:{providerID:e,modelID:s.join("/")}}})():{}})})
|
|
924
|
-
i.ok?n.info("Pushed notification to CLI session",{sessionId:
|
|
925
|
-
|
|
1002
|
+
i.ok?n.info("Pushed notification to CLI session",{sessionId:m}):n.warn("Failed to push notification to CLI session",{status:i.status})}}catch(e){n.warn("Could not push notification to CLI session (may be offline)",{error:(0,s.getErrorMessage)(e)})}}catch(e){n.warn("Failed to notify target session, spawning work agent instead",{requestId:o,error:(0,s.getErrorMessage)(e)})}if(!p)try{const e=await(0,r.spawnActionAgent)({agent:"work",prompt:h,cwd:c,projectPath:c,projectId:a,title:`[${o}] ${d.title}`,requestId:o,parentSessionId:g,apiClient:i,logger:n,trackChildPid:l})
|
|
1003
|
+
f=e.success,f?n.info("Spawned work agent for request",{requestId:o,sessionId:e.sessionId}):n.warn("Failed to spawn work agent",{requestId:o,error:e.error})}catch(e){n.warn("Failed to spawn work agent",{requestId:o,error:(0,s.getErrorMessage)(e)})}return n.info("Approve-and-work-request action completed",{requestId:o,notifiedSession:p,agentSpawned:f}),{success:!0,message:`Successfully approved request ${o}: ${d.title}${p?" \u2014 creating session notified":f?" \u2014 work agent spawned":""}`,data:{operationType:"approve-and-work",previousStatus:"pending_approval",newStatus:"open",notifiedSession:p,agentSpawned:f}}}catch(e){const t=(0,s.getErrorMessage)(e)
|
|
926
1004
|
return n.error("approve-and-work-request action failed",{requestId:o,error:t}),{success:!1,message:"Failed to approve request: "+t,error:t}}}
|
|
927
1005
|
const s=t(8824),r=t(8148)},7960(e,exports){"use strict"
|
|
928
1006
|
function t(){const e=process.argv[1]||""
|
|
@@ -937,17 +1015,17 @@ const m=new Date,y=`${(m.getHours()+"").padStart(2,"0")}${(m.getMinutes()+"").pa
|
|
|
937
1015
|
let w
|
|
938
1016
|
if(o.ALCHEMY_SESSION_TAG=`mcp-${y}-${v}`,s(l,"Spawn env setup",{agent:t,initialBranch:h||"none",sessionId:d||"new",attachMode:!!g}),w=g&&d&&c?["run","--attach",g,"--session",d,"--agent",t,c]:d&&c?["run","--session",d,"--agent",t,c]:c?["run","--agent",t,c]:["run","--agent",t],u){const e=w.findIndex((e=>e===c))
|
|
939
1017
|
e>0?w.splice(e,0,"--model",u):w.push("--model",u)}s(l,"Spawning opencode",{args:w.map((e=>e===c?`[prompt:${e.length}chars]`:e)),model:u||"not specified",promptLength:c?.length,promptPreview:c?.slice(0,100),cwd:l,hasSessionId:!!d})
|
|
940
|
-
const
|
|
1018
|
+
const I=(0,i.join)(l||process.cwd(),".alchemy_lab"),k=(0,i.join)(I,"spawn-stderr.log")
|
|
941
1019
|
let S
|
|
942
|
-
try{S=(0,n.openSync)(
|
|
943
|
-
return
|
|
1020
|
+
try{S=(0,n.openSync)(k,"a")}catch{}const C=(0,a.resolveRunCommand)(),j=(0,r.spawn)(C.command,[...C.prependArgs,...w],{detached:!0,stdio:["ignore","ignore",S??"ignore"],cwd:l||process.cwd(),env:o})
|
|
1021
|
+
return j.unref(),p&&p(j.pid),{success:!0,pid:j.pid}}catch(e){return{success:!1,error:(0,o.getErrorMessage)(e)}}}
|
|
944
1022
|
const r=t(5317),o=t(8824),n=t(9896),i=t(6928),a=t(9068)},8148(e,exports,t){"use strict"
|
|
945
1023
|
function s(e,t){try{const t=(0,o.join)(e,c,"opencode.json"),s=(0,r.readFileSync)(t,"utf8"),n=JSON.parse(s)
|
|
946
1024
|
if(n.port)return"http://127.0.0.1:"+n.port}catch(e){t.debug("Could not read opencode.json for server URL",{error:(0,i.getErrorMessage)(e)})}return null}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getOpenCodeServerUrl=s,exports.spawnActionAgent=async function(e){const{agent:t,prompt:r,cwd:o,projectPath:c,title:l,requestId:d,parentSessionId:u,allowParallel:h,apiClient:g,logger:p,trackChildPid:f}=e
|
|
947
1025
|
let m,y,v,w=null
|
|
948
1026
|
try{const s=new a.SessionSpawnService({apiClient:g,logger:p,projectId:e.projectId,getDefaultModel:e.getDefaultModel||(()=>{})}),o=await s.prepareSession({agent:t,prompt:r,projectPath:c,requestId:d,parentSessionId:u,title:l,allowParallel:h})
|
|
949
|
-
w=o.sessionId,m=o.model,y=o.serverUrl,v=o.initialBranch}catch(e){p.warn("Session preparation failed \u2014 spawning without pre-created session",{error:(0,i.getErrorMessage)(e)}),y=s(c,p)||void 0}const
|
|
950
|
-
return
|
|
1027
|
+
w=o.sessionId,m=o.model,y=o.serverUrl,v=o.initialBranch}catch(e){p.warn("Session preparation failed \u2014 spawning without pre-created session",{error:(0,i.getErrorMessage)(e)}),y=s(c,p)||void 0}const I=(0,n.spawnDetachedSubagent)({agent:t,prompt:r,cwd:o,sessionId:w||void 0,serverUrl:y,model:m,initialBranch:v,allowParallel:h??!1,onSpawn(e){e&&f&&f(e)}})
|
|
1028
|
+
return I.success?(p.info("Spawned action agent",{agent:t,sessionId:w,pid:I.pid,requestId:d}),{success:!0,sessionId:w,pid:I.pid}):{success:!1,sessionId:w,error:I.error||"Spawn failed"}}
|
|
951
1029
|
const r=t(9896),o=t(6928),n=t(8035),i=t(8824),a=t(1292),c=".alchemy_lab"},8524(e,exports,t){"use strict"
|
|
952
1030
|
var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
953
1031
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.CLIBridgeWebSocket=void 0,exports.setCLIBridge=function(e){n=e},exports.getCLIBridge=function(){return n}
|
|
@@ -1012,34 +1090,34 @@ return` ${o.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${r}`}: ${s}`})).join("\n")
|
|
|
1012
1090
|
let c=`---\ndescription: "${e.description||e.name}"\nmode: "${"utility"===e.mode?"primary":e.mode||"primary"}"\nmodel: "${i}"\n`
|
|
1013
1091
|
if(e.color&&(c+=`color: "${e.color}"\n`),c+=`tools:\n${a}\n`,e.opencodePermission&&Object.keys(e.opencodePermission).length>0){c+="permission:\n"
|
|
1014
1092
|
for(const[t,s]of Object.entries(e.opencodePermission))if("object"==typeof s&&null!==s){c+=` ${t}:\n`
|
|
1015
|
-
for(const[e,t]of Object.entries(s))c+=` ${e}: ${t}\n`}else c+=` ${t}: ${s}\n`}return void 0!==e.temperature&&(c+=`temperature: ${e.temperature}\n`),e.name.endsWith("-thinking")&&(c+="thinking:\n type: enabled\n budgetTokens: 10000\n"),"journal"===e.name&&(c+=`mcp:\n ${t}:\n env:\n ALCHEMY_MODE: "journal"\n`),c+="---\n\n",n&&(c+=n.trim()+"\n\n"),e.prompt&&(c+=e.prompt),e.promptAddition&&(c+="\n\n"+e.promptAddition),c}async configureOpenCode(e,r,n="node",i,a){try{const s=await Promise.resolve().then(t.t.bind(t,1943,23)),c=await Promise.resolve().then(t.t.bind(t,6928,23)),l=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),d="alchemy",u=c.join(e,"mcp-direct.js")
|
|
1093
|
+
for(const[e,t]of Object.entries(s))c+=` ${e}: ${t}\n`}else c+=` ${t}: ${s}\n`}return void 0!==e.temperature&&(c+=`temperature: ${e.temperature}\n`),(e.name.endsWith("-thinking")||e.thinkingModeDefault)&&(c+="thinking:\n type: enabled\n budgetTokens: 10000\n"),"journal"===e.name&&(c+=`mcp:\n ${t}:\n env:\n ALCHEMY_MODE: "journal"\n`),c+="---\n\n",n&&(c+=n.trim()+"\n\n"),e.prompt&&(c+=e.prompt),e.promptAddition&&(c+="\n\n"+e.promptAddition),c}async configureOpenCode(e,r,n="node",i,a){try{const s=await Promise.resolve().then(t.t.bind(t,1943,23)),c=await Promise.resolve().then(t.t.bind(t,6928,23)),l=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),d="alchemy",u=c.join(e,"mcp-direct.js")
|
|
1016
1094
|
this.logger.info("Configuring OpenCode",{isDevMode:l,serverName:d,scriptPath:u,projectPath:r})
|
|
1017
1095
|
const h=c.join(r,"opencode.json")
|
|
1018
1096
|
let g=null,p=null,f=null,m=null,y=!1,v=!1
|
|
1019
1097
|
const w=c.join(r,".alchemy_lab","config.json")
|
|
1020
1098
|
if(!i){try{const e=await s.readFile(w,"utf-8"),t=JSON.parse(e)
|
|
1021
1099
|
t?.openCodeTools?(g=t.openCodeTools,this.logger.info("Found tool settings in project config",{projectConfigTools:g})):v=!0,t?.openCodeModel&&(p=t.openCodeModel)}catch(e){this.logger.debug("No project config found, will create with defaults"),v=!0}try{const e=await s.readFile(h,"utf-8"),t=JSON.parse(e)
|
|
1022
|
-
t?.mode?.alchemy?.tools&&(f=t.mode.alchemy.tools,this.logger.info("Found existing tool settings in opencode.json",{existingTools:f})),t?.model&&(m=t.model),t?.mode?.journal&&(y=!0)}catch(e){this.logger.info("No existing opencode.json found, will use defaults")}}const
|
|
1023
|
-
i?.openCodeModel?
|
|
1100
|
+
t?.mode?.alchemy?.tools&&(f=t.mode.alchemy.tools,this.logger.info("Found existing tool settings in opencode.json",{existingTools:f})),t?.model&&(m=t.model),t?.mode?.journal&&(y=!0)}catch(e){this.logger.info("No existing opencode.json found, will use defaults")}}const I=i?.openCodeTools||g||f||{glob:!1,grep:!1,ls:!1,list:!1,view:!1,read:!1,write:!1,edit:!1,patch:!1,diagnostics:!0,bash:!0===i?.enableBash,fetch:!1,sourcegraph:!0,agent:!1!==a,todoread:!1,todowrite:!1},k={tools:I},S=i?.openCodeModel||p||m||"anthropic/claude-opus-4-5"
|
|
1101
|
+
i?.openCodeModel?k.model=i.openCodeModel:p?k.model=p:m&&(k.model=m)
|
|
1024
1102
|
const C=c.join(r,".opencode","agent")
|
|
1025
1103
|
await s.mkdir(C,{recursive:!0})
|
|
1026
|
-
const
|
|
1027
|
-
let E
|
|
1028
|
-
if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),T=await this.fetchProjectFromRemote(i.projectId),
|
|
1029
|
-
for(const e of
|
|
1030
|
-
const t=this.generateAgentMarkdown(e,d,b
|
|
1104
|
+
const j=S||"anthropic/claude-opus-4-5-20251101"
|
|
1105
|
+
let b,E=null,T=null
|
|
1106
|
+
if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),T=await this.fetchProjectFromRemote(i.projectId),b=T?.metadata?.globalSystemPrompt,b&&this.logger.info("Using global system prompt from project metadata",{projectId:i.projectId,promptLength:b.length}),E=await this.fetchAgentsFromRemote(i.projectId),E&&E.length>0){this.logger.info("Using agents from remote database",{count:E.length})
|
|
1107
|
+
for(const e of E){if(!e.enabled)continue
|
|
1108
|
+
const t=this.generateAgentMarkdown(e,d,j,b),r=c.join(C,e.name+".md")
|
|
1031
1109
|
await s.writeFile(r,t,"utf8"),this.logger.info(`Created ${e.name} agent file from remote config`,{agentPath:r,mode:e.mode})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
|
|
1032
1110
|
else this.logger.warn("No projectId provided - agents must be configured via database")
|
|
1033
1111
|
const A={$schema:"https://opencode.ai/config.json",model:S,mcp:{[d]:{type:"local",command:[n,u,r],enabled:!0}}}
|
|
1034
|
-
if(l&&process.env.REMOTE_SERVER_URL&&(A.mcp[d].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL}),
|
|
1035
|
-
for(const e of
|
|
1112
|
+
if(l&&process.env.REMOTE_SERVER_URL&&(A.mcp[d].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL}),E&&E.length>0){A.agent={}
|
|
1113
|
+
for(const e of E){if(!e.enabled)continue
|
|
1036
1114
|
const t={}
|
|
1037
1115
|
for(const[s,r]of Object.entries(e.tools||{})){const e=r,n=s.replace(/-/g,"_")
|
|
1038
1116
|
t[o.OPENCODE_BUILTIN_TOOLS.has(s)?s:`${d}_${n}`]=e}const s=T?.metadata?.bashToolAgents||{}
|
|
1039
1117
|
e.name in s&&(t.bash=!!s[e.name]),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name)&&void 0===t.task&&(t.task=!1)
|
|
1040
1118
|
let r=""
|
|
1041
|
-
|
|
1042
|
-
const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${
|
|
1119
|
+
b&&(r+=b.trim()+"\n\n"),e.prompt&&(r+=e.prompt),e.promptAddition&&(r+="\n\n"+e.promptAddition),A.agent[e.name]={tools:t,...r?{prompt:r}:{},...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}A.agent.general={...A.agent.general,disable:!0},A.agent.explore={...A.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[d+"_search"]:!0,[d+"_read_files"]:!0,[d+"_view_asset"]:!0,[d+"_query_history"]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
|
|
1120
|
+
const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${b?b.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`
|
|
1043
1121
|
await s.writeFile(c.join(C,"explore.md"),e,"utf8"),this.logger.info("Created explore agent override with research prompt"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(A.agent).length})}const P=[]
|
|
1044
1122
|
P.push("file://"+c.join(r,".opencode","plugin","inject-session.js")),P.push("file://"+c.join(r,".opencode","plugin","event-hooks.js")),P.push("file://"+c.join(r,".opencode","plugin","system-logger.js")),P.length>0&&(A.plugin=P),await s.writeFile(h,JSON.stringify(A,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:h,plugins:P})
|
|
1045
1123
|
try{const e=await Promise.resolve().then(t.t.bind(t,857,23)),r=c.join(e.homedir(),".alchemy_lab","opencode","config")
|
|
@@ -1049,8 +1127,9 @@ let n={}
|
|
|
1049
1127
|
try{n=JSON.parse(await s.readFile(o,"utf-8"))}catch{}const i=(n.plugin||[]).filter((e=>!e.includes(".opencode/plugin/")))
|
|
1050
1128
|
n.plugin=[...i,...P],n.$schema=n.$schema||"https://opencode.ai/config.json",await s.writeFile(o,JSON.stringify(n,null,2),"utf8"),this.logger.info("Updated global alchemy opencode config",{globalConfigPath:o,plugins:n.plugin})}catch(e){this.logger.warn("Failed to write global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(v&&!i)try{let e={}
|
|
1051
1129
|
try{const t=await s.readFile(w,"utf-8")
|
|
1052
|
-
e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=
|
|
1053
|
-
o.synced.length>0&&this.logger.info("Synced plugins during OpenCode config",{synced:o.synced}),o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors})}catch(e){this.logger.warn("Plugin sync failed during OpenCode config",{error:e instanceof Error?e.message:"Unknown error"})}return{success:!0,configPath:h}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,s.getErrorMessage)(e)}),{success:!1,error:(0,s.getErrorMessage)(e)}}}}exports.MCPClientConfigurer=o,o.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","fetch","agent","task","question","todoread","todowrite"])},
|
|
1130
|
+
e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=I,await s.writeFile(w,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:w}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPlugins:s}=await t.e(939).then(t.t.bind(t,9939,23)),o=await s(r,l??!1,this.logger,e)
|
|
1131
|
+
o.synced.length>0&&this.logger.info("Synced plugins during OpenCode config",{synced:o.synced}),o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors})}catch(e){this.logger.warn("Plugin sync failed during OpenCode config",{error:e instanceof Error?e.message:"Unknown error"})}return{success:!0,configPath:h}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,s.getErrorMessage)(e)}),{success:!1,error:(0,s.getErrorMessage)(e)}}}}exports.MCPClientConfigurer=o,o.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","fetch","agent","task","question","todoread","todowrite"])},9023(module){"use strict"
|
|
1132
|
+
module.exports=require("util")},9067(e,exports,t){"use strict"
|
|
1054
1133
|
var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
1055
1134
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.ExternalAuthFlow=exports.PollingStatus=void 0
|
|
1056
1135
|
const r=s(t(6982)),o=t(8824)
|
|
@@ -1158,15 +1237,15 @@ setTimeout((()=>{clearInterval(r),s(!1)}),t)}))}async function a(t){if(process.e
|
|
|
1158
1237
|
try{const{createConfigManager:s}=await e.e(60).then(e.t.bind(e,9060,23)),r=s(t)
|
|
1159
1238
|
return(await r.getInstallationConfig()).remoteServerUrl||"https://api.alchemylabcode.com"}catch{return"https://api.alchemylabcode.com"}}async function c(e,t,s,r,o="cli-init"){try{const n=(0,h.basename)(s)||"Local Project",i=await fetch(e+"/api/projects",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer "+t,"X-Client-Type":"alchemy-local-service","X-Service-Secret":process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"},body:JSON.stringify({name:n,description:"Project for "+n,status:"active",metadata:{basePath:s,createdVia:o}})})
|
|
1160
1239
|
if(i.ok){const e=await i.json()
|
|
1161
|
-
return e.data?.id||e.id}r.info(`Project registration failed (HTTP ${i.status})`)}catch(e){r.info("Project registration failed",{error:(0,
|
|
1162
|
-
const l=e(3444),d=e(9896),u=e(1943),h=e(6928),g=e(5317),p=e(857),f=e(1253),m=e(8984),y=e(2366),v=e(3962),w=e(1187),
|
|
1240
|
+
return e.data?.id||e.id}r.info(`Project registration failed (HTTP ${i.status})`)}catch(e){r.info("Project registration failed",{error:(0,I.getErrorMessage)(e)})}}Object.defineProperty(i,"__esModule",{value:!0})
|
|
1241
|
+
const l=e(3444),d=e(9896),u=e(1943),h=e(6928),g=e(5317),p=e(857),f=e(1253),m=e(8984),y=e(2366),v=e(3962),w=e(1187),I=e(8824),k=e(6800),S=e(4139);(async function(){23===parseInt(process.version.replace("v","").split(".")[0],10)&&(console.error(`\n\u274c Node.js ${process.version} is not supported.`),console.error(" AlchemyLab requires Node.js 22 or 24+ (Node 23 has a broken sqlite API)."),console.error("\n Switch with: nvm use 22 or nvm use 24\n"),process.exit(1))
|
|
1163
1242
|
const i=process.argv.slice(2)
|
|
1164
1243
|
let C
|
|
1165
|
-
const
|
|
1166
|
-
const
|
|
1167
|
-
if(0===
|
|
1244
|
+
const j=i.indexOf("--port");-1!==j&&i[j+1]&&(C=parseInt(i[j+1],10),isNaN(C)&&(console.error("\u274c Invalid port number"),process.exit(1)))
|
|
1245
|
+
const b=i.filter(((e,t)=>"--port"!==e&&(-1===j||t!==j+1)))
|
|
1246
|
+
if(0===b.length)return void await async function(s={}){const i=process.cwd(),l=(0,h.join)(i,S.ALCHEMY_DIR_NAME)
|
|
1168
1247
|
await(0,u.mkdir)(l,{recursive:!0})
|
|
1169
|
-
const C=(0,h.join)(l,"server.log"),{createLogger:
|
|
1248
|
+
const C=(0,h.join)(l,"server.log"),{createLogger:j}=await Promise.resolve().then(e.t.bind(e,2782,23)),b=j({serviceName:"alchemy-server",console:!1,logToFile:!0,logFilePath:C,baseDir:i}),E=await a(b),T=new f.TokenManager(b,i,{remoteServerUrl:E,allowInteractiveAuth:!1})
|
|
1170
1249
|
await T.initialize()
|
|
1171
1250
|
let A=await T.getToken()
|
|
1172
1251
|
if(!A?.token){console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
|
|
@@ -1177,12 +1256,12 @@ console.log("\u{1f4c1} Initializing AlchemyLab project..."),console.log("")
|
|
|
1177
1256
|
const n=(0,h.join)(e,S.ALCHEMY_DIR_NAME)
|
|
1178
1257
|
let i
|
|
1179
1258
|
await(0,u.mkdir)(n,{recursive:!0}),s?(console.log(`\u{1f4cb} Registering project "${(0,h.basename)(e)||"Local Project"}"...`),i=await c(t,s,e,o),console.log(i?"\u2705 Project registered: "+i:"\u26a0\ufe0f Project registration failed (will retry next run)")):console.log("\u26a0\ufe0f No auth token \u2014 skipping project registration")
|
|
1180
|
-
const a=(0,h.join)(n,"project.json");(0,d.writeFileSync)(a,JSON.stringify({id:i||null,name:(0,h.basename)(e)||"Local Project",status:"active",registeredRemotely:!!i,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:n}},null,2),"utf-8"),console.log("")}(i,
|
|
1259
|
+
const a=(0,h.join)(n,"project.json");(0,d.writeFileSync)(a,JSON.stringify({id:i||null,name:(0,h.basename)(e)||"Local Project",status:"active",registeredRemotely:!!i,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:n}},null,2),"utf-8"),console.log("")}(i,E,P,b)
|
|
1181
1260
|
const M=(0,h.join)(i,S.ALCHEMY_DIR_NAME,"project.json")
|
|
1182
1261
|
let _
|
|
1183
1262
|
try{const e=JSON.parse((0,d.readFileSync)(M,"utf-8"))
|
|
1184
|
-
if(_=e.id||void 0,!1===e.registeredRemotely&&P){const t=await c(
|
|
1185
|
-
t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(M,JSON.stringify(e,null,2),"utf-8"),_=t,console.log("\u2705 Project registered: "+t))}}catch{
|
|
1263
|
+
if(_=e.id||void 0,!1===e.registeredRemotely&&P){const t=await c(E,P,i,b,"cli-init-retry")
|
|
1264
|
+
t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(M,JSON.stringify(e,null,2),"utf-8"),_=t,console.log("\u2705 Project registered: "+t))}}catch{b.warn("Could not read project.json")}await async function(t){try{return void(0,g.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{}let s="latest"
|
|
1186
1265
|
try{const e=(0,h.join)(t,S.ALCHEMY_DIR_NAME,"config.json")
|
|
1187
1266
|
if((0,d.existsSync)(e)){const t=JSON.parse((0,d.readFileSync)(e,"utf-8"))
|
|
1188
1267
|
t.opencodeVersion&&(s=t.opencodeVersion)}}catch{}console.log(""),console.log("\u{1f4e6} OpenCode not found in PATH. Installing...")
|
|
@@ -1194,29 +1273,29 @@ console.log(" Downloading from: "+t)
|
|
|
1194
1273
|
const r=(0,h.join)((0,p.homedir)(),".local","bin")
|
|
1195
1274
|
await(0,u.mkdir)(r,{recursive:!0})
|
|
1196
1275
|
const o=(0,h.join)(r,"opencode")
|
|
1197
|
-
return(0,g.execFileSync)("curl",["-fsSL","-o",o,t],{stdio:"inherit",timeout:6e4}),(0,g.execFileSync)("chmod",["+x",o],{stdio:"pipe"}),(process.env.PATH||"").split(":").includes(r)||(console.log(` \u26a0\ufe0f Add ${r} to your PATH:`),console.log(` export PATH="${r}:$PATH"`),console.log(""),process.env.PATH=`${r}:${process.env.PATH}`),void console.log("\u2705 OpenCode installed successfully")}}catch(e){console.log("\u26a0\ufe0f Binary download failed: "+(0,
|
|
1276
|
+
return(0,g.execFileSync)("curl",["-fsSL","-o",o,t],{stdio:"inherit",timeout:6e4}),(0,g.execFileSync)("chmod",["+x",o],{stdio:"pipe"}),(process.env.PATH||"").split(":").includes(r)||(console.log(` \u26a0\ufe0f Add ${r} to your PATH:`),console.log(` export PATH="${r}:$PATH"`),console.log(""),process.env.PATH=`${r}:${process.env.PATH}`),void console.log("\u2705 OpenCode installed successfully")}}catch(e){console.log("\u26a0\ufe0f Binary download failed: "+(0,I.getErrorMessage)(e))}console.log(""),console.log("\u26a0\ufe0f Could not auto-install OpenCode."),console.log(""),console.log(" Install manually:"),console.log(" go install github.com/sst/opencode@latest"),console.log(" Or download from:"),console.log(" https://github.com/sst/opencode/releases"),console.log("")}(i)
|
|
1198
1277
|
const O=t()
|
|
1199
1278
|
console.log(["\n .oo 8 8 o 8 \n .P 8 8 8 8 8 \n .P 8 8 .oPYo. 8oPYo. .oPYo. ooYoYo. o o 8 .oPYo. 8oPYo. \n oPooo8 8 8 ' 8 8 8oooo8 8' 8 8 8 8 8 .oooo8 8 8 \n .P 8 8 8 . 8 8 8. 8 8 8 8 8 8 8 8 8 8 \n.P 8 8 `YooP' 8 8 `Yooo' 8 8 8 `YooP8 8oooo `YooP8 `YooP' \n..:::::....:.....:..:::..:.....:..:..:..:....8 ......:.....::.....:","::::::::::::::::::::::::::::::::::::::::::ooP'.::::::::::::::::::: v"+O,"::::::::::::::::::::::::::::::::::::::::::...::::::::::::::::::::::\n"].join("\n")),console.log("\u{1f4c1} Project: "+i)
|
|
1200
|
-
const x=s.port||await(0,
|
|
1201
|
-
try{const t=(0,v.getInstallContext)(),s=new m.MCPClientConfigurer(E,
|
|
1202
|
-
if(
|
|
1203
|
-
try{const e=await fetch(
|
|
1279
|
+
const x=s.port||await(0,k.findAvailablePort)(3456),$=new y.AlchemyActionServer(b,{projectPath:i,remoteServerUrl:E,port:x,authToken:P})
|
|
1280
|
+
try{const t=(0,v.getInstallContext)(),s=new m.MCPClientConfigurer(b,E,P),r=new w.UnifiedLauncher(b,t,s),[,a,c]=await Promise.all([$.start(),(async()=>{if(process.env.FRONTEND_URL)return process.env.FRONTEND_URL
|
|
1281
|
+
if(E.includes("api.alchemylabcode.com"))return"https://alchemylabcode.com"
|
|
1282
|
+
try{const e=await fetch(E+"/health")
|
|
1204
1283
|
if(e.ok){const t=await e.json()
|
|
1205
|
-
if(t?.data?.frontendUrl)return t.data.frontendUrl}}catch{}return
|
|
1284
|
+
if(t?.data?.frontendUrl)return t.data.frontendUrl}}catch{}return E.replace(/:\d+/,":3000")})(),r.launch({projectPath:i,settings:_?{projectId:_}:void 0}).catch((e=>(b.warn("Failed to launch OpenCode serve",{error:(0,I.getErrorMessage)(e)}),{success:!1,error:(0,I.getErrorMessage)(e)})))])
|
|
1206
1285
|
if(console.log(""),console.log(" Action server .... port "+$.getPort()),c.success&&"port"in c&&c.port){"pid"in c&&c.pid&&$.trackChildPid(c.pid),$.setOpenCodePort(c.port),console.log(" OpenCode serve ... port "+c.port)
|
|
1207
|
-
const e=(0,h.join)(i,S.ALCHEMY_DIR_NAME,"opencode.json");(0,d.writeFileSync)(e,JSON.stringify({port:c.port,pid:"pid"in c?c.pid:void 0,startedAt:(new Date).toISOString()},null,2)),
|
|
1286
|
+
const e=(0,h.join)(i,S.ALCHEMY_DIR_NAME,"opencode.json");(0,d.writeFileSync)(e,JSON.stringify({port:c.port,pid:"pid"in c?c.pid:void 0,startedAt:(new Date).toISOString()},null,2)),b.info("Wrote OpenCode config",{path:e,port:c.port})}else c.success?console.log(" OpenCode serve ... started"):console.log(" OpenCode serve ... failed (run manually: opencode serve)")
|
|
1208
1287
|
console.log(""),console.log(` \u2192 Alchemy UI: \x1b]8;;${a}\x07${a}\x1b]8;;\x07`),console.log("")
|
|
1209
1288
|
let l=!1,u=null
|
|
1210
1289
|
const g=async()=>{l||(l=!0,u&&clearInterval(u),console.log(""),console.log("Shutting down..."),await $.stop(),process.exit(0))}
|
|
1211
1290
|
process.on("SIGTERM",g),process.on("SIGINT",g)
|
|
1212
|
-
const{InteractiveShell:p}=await e.e(994).then(e.t.bind(e,5994,23)),f=new p({projectPath:i,logger:
|
|
1291
|
+
const{InteractiveShell:p}=await e.e(994).then(e.t.bind(e,5994,23)),f=new p({projectPath:i,logger:b,remoteServerUrl:E,authToken:P,projectId:_})
|
|
1213
1292
|
u=setInterval((async()=>{try{if(await T.hasValidToken())return
|
|
1214
1293
|
console.log(""),console.log("\u26a0\ufe0f Auth token expired. Opening browser to re-authenticate...")
|
|
1215
1294
|
const e=await T.startAuthenticationFlow()
|
|
1216
1295
|
if(console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(T)){const e=await T.getToken()
|
|
1217
|
-
e?.token&&(P=e.token,$.updateAuthToken(P)),console.log("\u2705 Re-authenticated successfully.")}else console.log("\u274c Re-authentication timed out. Some features may not work until you restart.")}catch(e){
|
|
1218
|
-
const
|
|
1219
|
-
switch("--help"!==
|
|
1296
|
+
e?.token&&(P=e.token,$.updateAuthToken(P)),console.log("\u2705 Re-authenticated successfully.")}else console.log("\u274c Re-authentication timed out. Some features may not work until you restart.")}catch(e){b.warn("Token watchdog check failed",{error:(0,I.getErrorMessage)(e)})}}),3e5),await f.start(g)}catch(e){b.error("Failed to start server",{error:(0,I.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,I.getErrorMessage)(e)),process.exit(1)}}({port:C})
|
|
1297
|
+
const E=b[0].toLowerCase()
|
|
1298
|
+
switch("--help"!==E&&"-h"!==E||(s(),process.exit(0)),"--version"!==E&&"-v"!==E||(console.log("alchemy v"+t()),process.exit(0)),E){case"serve":await async function(e={}){const s=process.cwd()
|
|
1220
1299
|
r(s)||(console.log("\u{1f4c1} Current directory is not an AlchemyLab project."),console.log(""),console.log("\u{1f4a1} Run `alchemy` to initialize this project."),console.log(""),process.exit(1))
|
|
1221
1300
|
const o=(0,l.createEnvLogger)({serviceName:"alchemy-server",console:!0,baseDir:s}),n=t()
|
|
1222
1301
|
console.log(`\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551 AlchemyLab Server v${n.padEnd(10)} \u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n`),console.log("\u{1f4c1} Project: "+s)
|
|
@@ -1224,13 +1303,13 @@ const i=await a(o),c=new f.TokenManager(o,s,{remoteServerUrl:i,allowInteractiveA
|
|
|
1224
1303
|
await c.initialize()
|
|
1225
1304
|
const d=await c.getToken(),u=d?.token
|
|
1226
1305
|
u||console.log("\u26a0\ufe0f No auth token. Some features may be limited. Run `alchemy` to authenticate.")
|
|
1227
|
-
const h=e.port||await(0,
|
|
1306
|
+
const h=e.port||await(0,k.findAvailablePort)(3456),g=new y.AlchemyActionServer(o,{projectPath:s,remoteServerUrl:i,port:h,authToken:u})
|
|
1228
1307
|
try{await g.start(),console.log(""),console.log("\u2705 Action server running!"),console.log(` Health: http://127.0.0.1:${g.getPort()}/health`),console.log(""),console.log("Press Ctrl+C to stop the server."),console.log("")
|
|
1229
1308
|
let e=!1
|
|
1230
1309
|
const t=async()=>{e||(e=!0,console.log(""),console.log("Shutting down action server..."),await g.stop(),process.exit(0))}
|
|
1231
|
-
process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){o.error("Failed to start server",{error:(0,
|
|
1310
|
+
process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){o.error("Failed to start server",{error:(0,I.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,I.getErrorMessage)(e)),process.exit(1)}}({port:C})
|
|
1232
1311
|
break
|
|
1233
1312
|
case"logout":await async function(){const e=process.cwd(),t=(0,l.createEnvLogger)({serviceName:"alchemy-cli",console:!1,baseDir:e}),s=await a(t),r=new f.TokenManager(t,e,{remoteServerUrl:s,allowInteractiveAuth:!1})
|
|
1234
1313
|
await r.initialize(),await r.hasValidToken()?await r.removeToken()?console.log("Logged out successfully."):(console.error("Failed to remove auth token."),process.exit(1)):console.log("Not currently logged in.")}()
|
|
1235
1314
|
break
|
|
1236
|
-
default:console.error("\u274c Unknown command: "+
|
|
1315
|
+
default:console.error("\u274c Unknown command: "+E),console.log(""),s(),process.exit(1)}})().catch((e=>{console.error("\u{1f4a5} Unexpected error:",e),process.exit(1)}))})(),module.exports=i})()
|