alchemylab 1.0.7 → 1.0.10

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/939.js CHANGED
@@ -8,7 +8,7 @@ try{await o.default.promises.mkdir(c,{recursive:!0})}catch(e){return r.error("Fa
8
8
  try{if(!o.default.existsSync(s)){r.warn("Plugin source not found: "+e.sourceName,{sourcePath:s}),l.errors.push(e.destName)
9
9
  continue}const i=await o.default.promises.readFile(s,"utf8").catch((()=>null))
10
10
  if(null===i){r.warn("Could not read plugin source: "+e.sourceName),l.errors.push(e.destName)
11
- continue}const d=e.destName.startsWith("lib/")?i:["const __plugin = (function() {\n const module = { exports: {} };\n const exports = module.exports;\n (function() {",i," })();\n return module.exports.default || module.exports;\n})();\nexport default __plugin;"].join("\n"),p=u.default.createHash("md5").update(d).digest("hex"),m=t(a)
12
- if(null===m||p!==m){const s=n.default.dirname(a)
13
- s!==c&&await o.default.promises.mkdir(s,{recursive:!0}),await o.default.promises.writeFile(a,d,"utf8"),r.info("Synced plugin: "+e.destName,{reason:null===m?"not installed":"updated"}),l.synced.push(e.destName)}else l.skipped.push(e.destName)}catch(s){r.error("Failed to sync plugin: "+e.destName,{error:s instanceof Error?s.message:"Unknown error"}),l.errors.push(e.destName)}}return l}
11
+ continue}const d=e.destName.startsWith("lib/")?i:['import { createRequire as __createRequire } from "node:module";\nconst require = __createRequire(import.meta.url);\nconst __plugin = (function() {\n const module = { exports: {} };\n const exports = module.exports;\n (function() {',i," })();\n return module.exports.default || module.exports;\n})();\nexport default __plugin;"].join("\n"),m=u.default.createHash("md5").update(d).digest("hex"),p=t(a)
12
+ if(null===p||m!==p){const s=n.default.dirname(a)
13
+ s!==c&&await o.default.promises.mkdir(s,{recursive:!0}),await o.default.promises.writeFile(a,d,"utf8"),r.info("Synced plugin: "+e.destName,{reason:null===p?"not installed":"updated"}),l.synced.push(e.destName)}else l.skipped.push(e.destName)}catch(s){r.error("Failed to sync plugin: "+e.destName,{error:s instanceof Error?s.message:"Unknown error"}),l.errors.push(e.destName)}}return l}
14
14
  const o=r(s(9896)),n=r(s(6928)),u=r(s(6982)),a=[{sourceName:"plugins/hooks/plugin.js",destName:"event-hooks.js"},{sourceName:"plugins/hooks/lib/condition-evaluator.js",destName:"lib/condition-evaluator.js"},{sourceName:"plugins/hooks/lib/hook-utils.js",destName:"lib/hook-utils.js"},{sourceName:"plugins/hooks/lib/dedup-utils.js",destName:"lib/dedup-utils.js"},{sourceName:"plugins/inject-session.js",destName:"inject-session.js"},{sourceName:"plugins/system-logger.js",destName:"system-logger.js"}]}}
package/dist/cli.js CHANGED
@@ -36,14 +36,16 @@ const s=t(5317),r=t(8824)
36
36
  exports.UnifiedLauncher=class{constructor(e,t,s){this.logger=e,this.installContext=t,this.configurer=s}async launch(e){const{projectPath:t,settings:s,multiAgent:o}=e,{installPath:n,nodePath:i}=this.installContext
37
37
  this.logger.info("UnifiedLauncher: Starting launch",{projectPath:t,installPath:n})
38
38
  try{const r=await this.configurer.configureOpenCode(n,t,i,s,o)
39
- return r.success?(this.logger.info("UnifiedLauncher: Configuration complete",{configPath:r.configPath}),this.launchBackground(e,r)):(this.logger.error("UnifiedLauncher: Configuration failed",{error:r.error}),{success:!1,error:r.error})}catch(e){return this.logger.error("UnifiedLauncher: Launch failed",{error:(0,r.getErrorMessage)(e)}),{success:!1,error:(0,r.getErrorMessage)(e)}}}async launchBackground(e,t){const{projectPath:r,envVars:o}=e
40
- try{(0,s.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{return{success:!1,error:"OpenCode not found in PATH."}}return this.logger.info("UnifiedLauncher: Launching opencode serve in background",{command:"opencode serve",cwd:r}),new Promise((e=>{const n=(0,s.spawn)("opencode",["serve"],{stdio:["ignore","pipe","pipe"],cwd:r,detached:!0,env:{...process.env,...o}})
41
- let i="",a=!1
42
- const c=s=>{i+=s
43
- const r=i.match(/listening on http:\/\/[^:]+:(\d+)/)
44
- if(r&&!a){const s=parseInt(r[1],10)
45
- a=!0,this.logger.info("UnifiedLauncher: Discovered opencode serve port",{pid:n.pid,port:s}),n.unref(),e({success:!0,pid:n.pid,port:s,configPath:t.configPath})}}
46
- n.stdout?.on("data",(e=>c(e.toString()))),n.stderr?.on("data",(e=>c(e.toString()))),setTimeout((()=>{a||(a=!0,this.logger.warn("UnifiedLauncher: Timeout waiting for opencode serve port",{outputBuffer:i.slice(0,500)}),n.unref(),e({success:!0,pid:n.pid,configPath:t.configPath,error:"Could not determine port from opencode serve output"}))}),1e4),n.on("error",(t=>{a||(a=!0,this.logger.error("UnifiedLauncher: opencode serve failed to start",{error:t.message}),e({success:!1,error:"Failed to start opencode serve: "+t.message}))})),n.on("exit",(t=>{a||(a=!0,this.logger.error("UnifiedLauncher: opencode serve exited early",{code:t,output:i.slice(0,500)}),e({success:!1,error:"opencode serve exited with code "+t}))}))}))}}},1253(e,exports,t){var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
39
+ return r.success?(this.logger.info("UnifiedLauncher: Configuration complete",{configPath:r.configPath}),this.launchBackground(e,r)):(this.logger.error("UnifiedLauncher: Configuration failed",{error:r.error}),{success:!1,error:r.error})}catch(e){return this.logger.error("UnifiedLauncher: Launch failed",{error:(0,r.getErrorMessage)(e)}),{success:!1,error:(0,r.getErrorMessage)(e)}}}async launchBackground(e,r){const{projectPath:o,envVars:n}=e
40
+ try{(0,s.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{return{success:!1,error:"OpenCode not found in PATH."}}this.logger.info("UnifiedLauncher: Launching opencode serve in background",{command:"opencode serve",cwd:o})
41
+ const{resolveServeCommand:i}=await Promise.resolve().then(t.bind(t,9068)),a=i()
42
+ return new Promise((e=>{const t=(0,s.spawn)(a.command,[...a.prependArgs,"--port","0"],{stdio:["ignore","pipe","pipe"],cwd:o,detached:!0,env:{...process.env,...n}})
43
+ let i="",c=!1
44
+ const l=s=>{i+=s
45
+ const o=i.match(/listening on http:\/\/[^:]+:(\d+)/)
46
+ if(o&&!c){const s=parseInt(o[1],10)
47
+ c=!0,this.logger.info("UnifiedLauncher: Discovered opencode serve port",{pid:t.pid,port:s}),t.unref(),e({success:!0,pid:t.pid,port:s,configPath:r.configPath})}}
48
+ t.stdout?.on("data",(e=>l(e.toString()))),t.stderr?.on("data",(e=>l(e.toString()))),setTimeout((()=>{c||(c=!0,this.logger.warn("UnifiedLauncher: Timeout waiting for opencode serve port",{outputBuffer:i.slice(0,500)}),t.unref(),e({success:!0,pid:t.pid,configPath:r.configPath,error:"Could not determine port from opencode serve output"}))}),1e4),t.on("error",(t=>{c||(c=!0,this.logger.error("UnifiedLauncher: opencode serve failed to start",{error:t.message}),e({success:!1,error:"Failed to start opencode serve: "+t.message}))})),t.on("exit",(t=>{c||(c=!0,this.logger.error("UnifiedLauncher: opencode serve exited early",{code:t,output:i.slice(0,500)}),e({success:!1,error:"opencode serve exited with code "+t}))}))}))}}},1253(e,exports,t){var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
47
49
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.TokenManager=void 0
48
50
  const r=s(t(6928)),o=s(t(1943)),n=s(t(6982)),i=s(t(857)),a=t(9067),c=t(8824),l="alchemy-lab"
49
51
  exports.TokenManager=class{constructor(e,t,s={}){this.projectId=null,this.encryptionKey=null,this.pendingAuthCheck=!1,this.tempAuthCode=null,this.CACHE_TTL_MS=6e4,this.TOKEN_CACHE={manualToken:null,encryptedToken:null,hasValidToken:null},this.initPromise=null,this.initialized=!1,this.logger=e.child({component:"token-manager"}),this.remoteServerUrl=s.remoteServerUrl||"https://api.alchemylabcode.com",this.allowInteractiveAuth=!1!==s.allowInteractiveAuth,void 0!==s.cacheTtlMs&&s.cacheTtlMs>0&&(this.CACHE_TTL_MS=s.cacheTtlMs),this.serverInstallDir=this.getModuleDir(),this.tokenDirPath=this.serverInstallDir,this.authFlow=new a.ExternalAuthFlow(e,this.remoteServerUrl),this.logger.info("TokenManager initialized",{workingDir:t,serverInstallDir:this.serverInstallDir,tokenDirPath:this.tokenDirPath,allowInteractiveAuth:this.allowInteractiveAuth,cacheTtlMs:this.CACHE_TTL_MS}),this.generateEncryptionKey()}getModuleDir(){const e=__dirname||process.cwd(),t=i.default.homedir()
@@ -136,39 +138,39 @@ const i=await this.hookExecutor.processEvent({eventType:s,sessionId:r||"",variab
136
138
  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.get("/api/stuck-sessions",this.handleGetStuckSessions.bind(this)),e.post("/api/stuck-sessions/test-inject",this.handleTestInjectStuckSession.bind(this)),e.post("/api/stuck-sessions/:sessionId/retry",this.handleRetryStuckSession.bind(this)),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")
137
139
  try{if(!c.default.existsSync(s))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
138
140
  const e=c.default.readFileSync(s,"utf-8"),r=JSON.parse(e)
139
- 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)})}}async handleSyncAgents(e,t){try{const e=this.readProjectConfig()
140
- if(!e?.id)return void t.status(400).json({success:!1,error:"No project configuration found"})
141
+ 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)})}}async handleSyncAgents(e,s){try{const e=this.readProjectConfig()
142
+ if(!e?.id)return void s.status(400).json({success:!1,error:"No project configuration found"})
141
143
  this.logger.info("Syncing agents from remote",{projectId:e.id})
142
- const s=await this.configurer.fetchAgentsFromRemote(e.id)
143
- if(!s||0===s.length)return void t.json({success:!0,message:"No agents to sync",count:0})
144
- const r=await this.configurer.fetchProjectFromRemote(e.id)
145
- this.logger.debug("Fetched project for global prompt",{projectId:e.id,hasProject:!!r,hasGlobalSystemPrompt:!!r?.metadata?.globalSystemPrompt})
146
- const o=r?.metadata?.globalSystemPrompt
147
- 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")
148
- const n=a.default.join(this.options.projectPath,".opencode","agent")
149
- await c.default.promises.mkdir(n,{recursive:!0})
150
- let i=0
151
- for(const e of s){if(!e.enabled)continue
152
- const t="alchemy",s=this.configurer.generateAgentMarkdown(e,t,"anthropic/claude-sonnet-4-20250514",o),r=a.default.join(n,e.name+".md")
153
- await c.default.promises.writeFile(r,s,"utf8"),i++,this.logger.info("Synced agent file",{name:e.name,path:r,mode:e.mode})}try{const e=a.default.join(this.options.projectPath,"opencode.json")
154
- if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),o=JSON.parse(t),n=r?.metadata?.bashToolAgents||{}
155
- 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])
156
- 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 l=!1
144
+ const r=await this.configurer.fetchAgentsFromRemote(e.id)
145
+ if(!r||0===r.length)return void s.json({success:!0,message:"No agents to sync",count:0})
146
+ const o=await this.configurer.fetchProjectFromRemote(e.id)
147
+ this.logger.debug("Fetched project for global prompt",{projectId:e.id,hasProject:!!o,hasGlobalSystemPrompt:!!o?.metadata?.globalSystemPrompt})
148
+ const n=o?.metadata?.globalSystemPrompt
149
+ n?this.logger.info("Using global system prompt from project metadata",{projectId:e.id,promptLength:n.length}):this.logger.debug("No global system prompt found in project metadata")
150
+ const i=a.default.join(this.options.projectPath,".opencode","agent")
151
+ await c.default.promises.mkdir(i,{recursive:!0})
152
+ let l=0
153
+ for(const e of r){if(!e.enabled)continue
154
+ const t="alchemy",s=this.configurer.generateAgentMarkdown(e,t,"anthropic/claude-sonnet-4-20250514",n),r=a.default.join(i,e.name+".md")
155
+ await c.default.promises.writeFile(r,s,"utf8"),l++,this.logger.info("Synced agent file",{name:e.name,path:r,mode:e.mode})}try{const e=a.default.join(this.options.projectPath,"opencode.json")
156
+ if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),s=JSON.parse(t),n=o?.metadata?.bashToolAgents||{}
157
+ if(s.agent){for(const e of r)e.enabled&&s.agent[e.name]&&e.name in n&&(s.agent[e.name].tools.bash=!!n[e.name])
158
+ c.default.writeFileSync(e,JSON.stringify(s,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 d=!1
157
159
  try{const e=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
158
- if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),s=JSON.parse(t)
159
- if(s.pid)try{process.kill(s.pid,"SIGTERM"),this.logger.info("Killed existing OpenCode process",{pid:s.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 r=(0,y.spawn)("opencode",["serve"],{stdio:["ignore","pipe","pipe"],cwd:this.options.projectPath,detached:!0,env:process.env}),o=new Promise((e=>{let t=""
160
+ if(c.default.existsSync(e)){const s=c.default.readFileSync(e,"utf-8"),r=JSON.parse(s)
161
+ 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{resolveServeCommand:o}=await Promise.resolve().then(t.bind(t,9068)),n=o(),i=(0,y.spawn)(n.command,[...n.prependArgs,"--port","0"],{stdio:["ignore","pipe","pipe"],cwd:this.options.projectPath,detached:!0,env:process.env}),a=new Promise((e=>{let t=""
160
162
  const s=setTimeout((()=>{this.logger.warn("Timeout waiting for OpenCode port, killing process")
161
- try{r.kill("SIGTERM")}catch{}e(null)}),1e4)
162
- r.stdout?.on("data",(o=>{t+=o.toString()
163
- const n=t.match(/listening on http:\/\/[^:]+:(\d+)/)
164
- n&&(clearTimeout(s),r.unref(),e({port:parseInt(n[1],10),pid:r.pid}))})),r.on("error",(()=>{clearTimeout(s)
165
- try{r.kill("SIGTERM")}catch{}e(null)}))})),n=await o
166
- n&&(this.trackChildPid(n.pid),c.default.writeFileSync(e,JSON.stringify({port:n.port,pid:n.pid,startedAt:(new Date).toISOString()},null,2)),this.logger.info("Restarted OpenCode",{port:n.port,pid:n.pid}),this.setOpenCodePort(n.port),l=!0)}}catch(e){this.logger.warn("Could not restart OpenCode",{error:(0,g.getErrorMessage)(e)})}t.json({success:!0,message:`Synced ${i} agent(s) to local files${l?" - OpenCode reloading":""}`,count:i,restarted:l})}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,t){try{const{requestId:s}=e.body
167
- if(!s)return void t.status(400).json({success:!1,error:"requestId is required"})
168
- if(!/^req\d+$/.test(s))return void t.status(400).json({success:!1,error:"Invalid requestId format"})
169
- this.logger.info("Generating journal for request",{requestId:s})
170
- const r=`Generate a journal entry for request ${s}.\n\nSteps:\n1. Use the generate_request_journal tool with requestId: ${s}\n2. Write a concise narrative journal entry based on the returned data covering:\n - What was implemented and why\n - Key decisions and trade-offs made\n - Challenges encountered and solutions\n - Files modified and their purposes\n - How acceptance criteria were met\n3. Save as JOURNAL-${s}.md using document_operations (operation: create, type: Report)\n\nKeep it story-focused and informative.`,o=(0,y.spawn)("opencode",["run","--agent","journal",r],{detached:!0,stdio:"ignore",cwd:this.options.projectPath})
171
- o.pid&&this.trackChildPid(o.pid),o.unref(),t.json({success:!0,message:"Journal generation started for "+s})}catch(e){this.logger.error("Failed to generate journal",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleEventHooksProxy(e,t){try{const{eventType:s}=e.params,{sessionId:r}=e.query,o=this.projectId
163
+ try{i.kill("SIGTERM")}catch{}e(null)}),1e4)
164
+ i.stdout?.on("data",(r=>{t+=r.toString()
165
+ const o=t.match(/listening on http:\/\/[^:]+:(\d+)/)
166
+ o&&(clearTimeout(s),i.unref(),e({port:parseInt(o[1],10),pid:i.pid}))})),i.on("error",(()=>{clearTimeout(s)
167
+ try{i.kill("SIGTERM")}catch{}e(null)}))})),l=await a
168
+ l&&(this.trackChildPid(l.pid),c.default.writeFileSync(e,JSON.stringify({port:l.port,pid:l.pid,startedAt:(new Date).toISOString()},null,2)),this.logger.info("Restarted OpenCode",{port:l.port,pid:l.pid}),this.setOpenCodePort(l.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
169
+ if(!r)return void s.status(400).json({success:!1,error:"requestId is required"})
170
+ if(!/^req\d+$/.test(r))return void s.status(400).json({success:!1,error:"Invalid requestId format"})
171
+ this.logger.info("Generating journal for request",{requestId:r})
172
+ const o=`Generate a journal entry for request ${r}.\n\nSteps:\n1. Use the generate_request_journal tool with requestId: ${r}\n2. Write a concise narrative journal entry based on the returned data covering:\n - What was implemented and why\n - Key decisions and trade-offs made\n - Challenges encountered and solutions\n - Files modified and their purposes\n - How acceptance criteria were met\n3. Save as JOURNAL-${r}.md using document_operations (operation: create, type: Report)\n\nKeep it story-focused and informative.`,{resolveRunCommand:n}=await Promise.resolve().then(t.bind(t,9068)),i=n(),a=(0,y.spawn)(i.command,[...i.prependArgs,"run","--agent","journal",o],{detached:!0,stdio:"ignore",cwd:this.options.projectPath})
173
+ a.pid&&this.trackChildPid(a.pid),a.unref(),s.json({success:!0,message:"Journal generation started for "+r})}catch(e){this.logger.error("Failed to generate journal",{error:(0,g.getErrorMessage)(e)}),s.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleEventHooksProxy(e,t){try{const{eventType:s}=e.params,{sessionId:r}=e.query,o=this.projectId
172
174
  if(!o)return void t.status(400).json({success:!1,error:"Project not registered"})
173
175
  this.logger.info("Proxying event hooks request",{projectId:o,eventType:s,sessionId:r})
174
176
  const n=r?"?sessionId="+encodeURIComponent(r):"",i=await this.apiClient.get(`/api/projects/${o}/event-hooks/by-event/${s}${n}`)
@@ -212,21 +214,25 @@ if(this.logger.info("Agent type bridge lookup",{sessionId:s,bridgeAgentType:e?.d
212
214
  if(r?.id)try{const e=await this.apiClient.get(`/api/projects/${r.id}/cli-sessions/resolve/${encodeURIComponent(s)}`),o=e?.data?.parent?.externalSessionId||""
213
215
  return void t.json({success:!0,parentSessionId:o})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:s,error:(0,g.getErrorMessage)(e)})}t.json({success:!0,parentSessionId:""})}catch(e){this.logger.debug("Failed to get parent session",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleOpenCodeProxy(e,t){try{const s=this.getOpenCodeUrl()
214
216
  if(!s)return void t.status(502).json({error:"OpenCode not running"})
215
- const r=`${s}/${e.path.replace(/^\//,"")}${e.originalUrl.includes("?")?e.originalUrl.substring(e.originalUrl.indexOf("?")):""}`,o=e.headers.accept?.includes("text/event-stream")
216
- if(o){const s=new AbortController,o=()=>{this.proxyAbortControllers.delete(s)},n=(async()=>{try{const e=await fetch(r,{headers:{Accept:"text/event-stream"},signal:s.signal})
217
- if(!e.ok||!e.body)return o(),void t.status(e.status).json({error:"SSE upstream error"})
217
+ const r=e.path.replace(/^\//,""),o=e.originalUrl.includes("?")?e.originalUrl.substring(e.originalUrl.indexOf("?")):"",n=`${s}/${r}${o}`,i=e.headers.accept?.includes("text/event-stream")
218
+ if(i){const s=new AbortController,r=()=>{this.proxyAbortControllers.delete(s)},o=(async()=>{try{const e=await fetch(n,{headers:{Accept:"text/event-stream"},signal:s.signal})
219
+ if(!e.ok||!e.body)return r(),void t.status(e.status).json({error:"SSE upstream error"})
218
220
  t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"}),t.flushHeaders()
219
- const n=e.body.getReader()
220
- try{for(;;){const{done:e,value:s}=await n.read()
221
+ const o=e.body.getReader()
222
+ try{for(;;){const{done:e,value:s}=await o.read()
221
223
  if(e)break
222
- t.write(s),"function"==typeof t.flush&&t.flush()}}catch{}t.end()}catch{t.headersSent||t.status(502).json({error:"SSE upstream error"})}finally{o()}})()
223
- return this.proxyAbortControllers.set(s,n),void e.on("close",(()=>{try{s.abort()}catch{}}))}const n={"Content-Type":e.headers["content-type"]||"application/json"},i={method:e.method,headers:n}
224
- "GET"!==e.method&&"HEAD"!==e.method&&e.body&&(i.body=JSON.stringify(e.body))
225
- const a=await fetch(r,i)
226
- if(204===a.status||"0"===a.headers.get("content-length"))return void t.status(a.status).end()
227
- const c=a.headers.get("content-type")||""
224
+ t.write(s),"function"==typeof t.flush&&t.flush()}}catch{}t.end()}catch{t.headersSent||t.status(502).json({error:"SSE upstream error"})}finally{r()}})()
225
+ return this.proxyAbortControllers.set(s,o),void e.on("close",(()=>{try{s.abort()}catch{}}))}const a={"Content-Type":e.headers["content-type"]||"application/json"},c={method:e.method,headers:a}
228
226
  let l
229
- try{l=c.includes("application/json")?await a.json():await a.text()}catch{return void t.status(a.status).end()}t.status(a.status),c&&t.setHeader("Content-Type",c),"string"==typeof l?t.send(l):t.json(l)}catch(s){this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,g.getErrorMessage)(s),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,g.getErrorMessage)(s)})}}async handleGetAgentByName(e,t){try{const s=this.readProjectConfig()
227
+ "GET"!==e.method&&"HEAD"!==e.method&&e.body&&(c.body=JSON.stringify(e.body))
228
+ let d=n
229
+ try{l=await fetch(n,c)}catch(e){this.openCodePort=null,this.openCodeUrlCache=null
230
+ const t=this.getOpenCodeUrl()
231
+ if(!t||t===s)throw e
232
+ d=`${t}/${r}${o}`,this.logger.info("OpenCode proxy: retrying with fresh port",{oldUrl:s,newUrl:t}),l=await fetch(d,c)}if(204===l.status||"0"===l.headers.get("content-length"))return void t.status(l.status).end()
233
+ const u=l.headers.get("content-type")||""
234
+ let h
235
+ try{h=u.includes("application/json")?await l.json():await l.text()}catch{return void t.status(l.status).end()}t.status(l.status),u&&t.setHeader("Content-Type",u),"string"==typeof h?t.send(h):t.json(h)}catch(s){this.openCodePort=null,this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,g.getErrorMessage)(s),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,g.getErrorMessage)(s)})}}async handleGetAgentByName(e,t){try{const s=this.readProjectConfig()
230
236
  if(!s?.id)return void t.status(400).json({success:!1,error:"No project configured"})
231
237
  const{agentName:r}=e.params,o=await this.apiClient.get(`/api/projects/${s.id}/agents`),n=(o?.data||o||[]).find((e=>e.name===r))
232
238
  if(!n)return void t.status(404).json({success:!1,error:`Agent '${r}' not found`})
@@ -607,24 +613,24 @@ let S
607
613
  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))
608
614
  else{const e=await y.get(`/api/projects/${v}/test-processes/active`)
609
615
  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"}
610
- const I=(0,o.join)(w,c,l),E=(0,o.join)(I,u),b=(0,n.simpleGit)(w)
611
- if((0,r.existsSync)(E))try{const e=(0,n.simpleGit)(E)
612
- await e.raw(["reset","--hard",k]),m.info("Reset test worktree to latest branch HEAD",{worktreePath:E,branchName:k})}catch(e){m.warn("Could not reset test worktree, recreating",{error:(0,i.getErrorMessage)(e)})
613
- try{await b.raw(["worktree","remove","--force",E])}catch{}}if(!(0,r.existsSync)(E)){(0,r.mkdirSync)(I,{recursive:!0})
614
- try{if(!(await b.branch()).all.includes(k))return{success:!1,message:`Branch ${k} not found`,error:"Branch not found"}
615
- await b.raw(["worktree","add","--detach",E,k]),m.info("Created test worktree (detached)",{worktreePath:E,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)}
616
- m.info("Test worktree already exists, using as-is",{worktreePath:E})}}const{execSync:j}=await Promise.resolve().then(t.t.bind(t,5317,23))
616
+ const I=(0,o.join)(w,c,l),b=(0,o.join)(I,u),E=(0,n.simpleGit)(w)
617
+ if((0,r.existsSync)(b))try{const e=(0,n.simpleGit)(b)
618
+ await e.raw(["reset","--hard",k]),m.info("Reset test worktree to latest branch HEAD",{worktreePath:b,branchName:k})}catch(e){m.warn("Could not reset test worktree, recreating",{error:(0,i.getErrorMessage)(e)})
619
+ try{await E.raw(["worktree","remove","--force",b])}catch{}}if(!(0,r.existsSync)(b)){(0,r.mkdirSync)(I,{recursive:!0})
620
+ try{if(!(await E.branch()).all.includes(k))return{success:!1,message:`Branch ${k} not found`,error:"Branch not found"}
621
+ await E.raw(["worktree","add","--detach",b,k]),m.info("Created test worktree (detached)",{worktreePath:b,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)}
622
+ m.info("Test worktree already exists, using as-is",{worktreePath:b})}}const{execSync:j}=await Promise.resolve().then(t.t.bind(t,5317,23))
617
623
  for(const e of S){const t=e.config?.setup||[]
618
624
  if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length})
619
- for(const s of t)try{m.info("Setup command",{command:s,worktreePath:E}),j(s,{cwd:E,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=[],P=[]
625
+ for(const s of t)try{m.info("Setup command",{command:s,worktreePath:b}),j(s,{cwd:b,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=[],P=[]
620
626
  for(const process of S){let t
621
627
  const r=e.testRunIds?.[T.length]
622
- try{r?(await y.patch(`/api/projects/${v}/test-runs/${r}`,{worktreePath:E}),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:E,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)})
628
+ try{r?(await y.patch(`/api/projects/${v}/test-runs/${r}`,{worktreePath:b}),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:b,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)})
623
629
  continue}const o=s(p,process,0,v,f),n=f?"tester":"auto-tester"
624
- try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:o,cwd:E,projectPath:w,projectId:v,title:`[${u}] Test: ${process.name}`,requestId:u,parentSessionId:g,allowParallel:!0,apiClient:y,logger:m,trackChildPid:C})
630
+ try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:o,cwd:b,projectPath:w,projectId:v,title:`[${u}] Test: ${process.name}`,requestId:u,parentSessionId:g,allowParallel:!0,apiClient:y,logger:m,trackChildPid:C})
625
631
  if(!e.success)throw Error(e.error||"Spawn failed")
626
- P.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:E,pid:e.pid,sessionId:e.sessionId})}catch(e){m.error("Failed to spawn test-runner agent",{testRunId:t.id,error:(0,i.getErrorMessage)(e)})
627
- 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===P.length?{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}:{success:!0,message:`Started ${P.length} test run(s) for request ${u}`,data:{testRunIds:T,agentsSpawned:P.length,processCount:S.length,worktreePath:E}}}catch(e){const t=(0,i.getErrorMessage)(e)
632
+ P.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:b,pid:e.pid,sessionId:e.sessionId})}catch(e){m.error("Failed to spawn test-runner agent",{testRunId:t.id,error:(0,i.getErrorMessage)(e)})
633
+ 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===P.length?{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}:{success:!0,message:`Started ${P.length} test run(s) for request ${u}`,data:{testRunIds:T,agentsSpawned:P.length,processCount:S.length,worktreePath:b}}}catch(e){const t=(0,i.getErrorMessage)(e)
628
634
  return m.error("run-tests action failed",{requestId:u,error:t}),{success:!1,message:"Failed to run tests: "+t,error:t}}}
629
635
  const r=t(9896),o=t(6928),n=t(3807),i=t(8824),a=t(8148),c=".alchemy_lab",l="test-worktrees"},6636(e,exports,t){Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRevertRequestAction=async function(e,t){const{requestId:o,reason:n}=e,{logger:i,apiClient:a,projectPath:c,projectId:l}=t
630
636
  if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
@@ -704,19 +710,19 @@ const s=t(8824),r=t(8148)},7960(e,exports){function t(){const e=process.argv[1]|
704
710
  if(e.includes("/dist-dev/")||e.includes("\\dist-dev\\"))return!0
705
711
  if(e.includes("/dist/")||e.includes("\\dist\\"))return!1
706
712
  const t=process.cwd()
707
- return!(!t.includes("/dist-dev")&&!t.includes("\\dist-dev"))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.isDevBuild=t,exports.getBuildMode=function(){return t()?"development":"production"},exports.enableDebugMode=function(){return"true"===process.env.ENABLE_DEBUG_TOOLS&&(process.env.DEBUG_LOGGING="true",!0)}},8035(e,exports,t){function s(e,t,s){try{const r=`[${(new Date).toISOString()}] [detached-subagent] ${t}${s?" "+JSON.stringify(s):""}\n`,o=(0,a.join)(e||process.cwd(),".alchemy_lab","spawn-debug.log");(0,i.appendFileSync)(o,r)}catch{}}function r(e){const{agent:t,prompt:r,cwd:c,parentServerUrl:l,parentSessionId:d,sessionId:u,model:h,initialBranch:g,serverUrl:p,onSpawn:f,onError:m}=e
713
+ return!(!t.includes("/dist-dev")&&!t.includes("\\dist-dev"))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.isDevBuild=t,exports.getBuildMode=function(){return t()?"development":"production"},exports.enableDebugMode=function(){return"true"===process.env.ENABLE_DEBUG_TOOLS&&(process.env.DEBUG_LOGGING="true",!0)}},8035(e,exports,t){function s(e,t,s){try{const r=`[${(new Date).toISOString()}] [detached-subagent] ${t}${s?" "+JSON.stringify(s):""}\n`,o=(0,a.join)(e||process.cwd(),".alchemy_lab","spawn-debug.log");(0,i.appendFileSync)(o,r)}catch{}}function r(e){const{agent:t,prompt:r,cwd:l,parentServerUrl:d,parentSessionId:u,sessionId:h,model:g,initialBranch:p,serverUrl:f,onSpawn:m,onError:y}=e
708
714
  try{const n={...process.env}
709
- l&&(n.OPENCODE_PARENT_SERVER=l),g&&(n.ALCHEMY_INITIAL_BRANCH=g),e.allowParallel&&(n.ALCHEMY_ALLOW_PARALLEL="1")
710
- const d=new Date,m=`${(d.getHours()+"").padStart(2,"0")}${(d.getMinutes()+"").padStart(2,"0")}${(d.getSeconds()+"").padStart(2,"0")}`,y=Math.random().toString(36).substring(2,7)
711
- let v
712
- if(n.ALCHEMY_SESSION_TAG=`mcp-${m}-${y}`,s(c,"Spawn env setup",{agent:t,initialBranch:g||"none",hasParentServer:!!l,sessionId:u||"new",attachMode:!!p}),v=p&&u&&r?["run","--attach",p,"--session",u,"--agent",t,r]:u&&r?["run","--session",u,"--agent",t,r]:r?["run","--agent",t,r]:["run","--agent",t],h){const e=v.findIndex((e=>e===r))
713
- e>0?v.splice(e,0,"--model",h):v.push("--model",h)}s(c,"Spawning opencode",{args:v.map((e=>e===r?`[prompt:${e.length}chars]`:e)),model:h||"not specified",promptLength:r?.length,promptPreview:r?.slice(0,100),cwd:c,hasSessionId:!!u,hasParentServerUrl:!!l})
714
- const w=(0,a.join)(c||process.cwd(),".alchemy_lab"),C=(0,a.join)(w,"spawn-stderr.log")
715
- let k
716
- try{k=(0,i.openSync)(C,"a")}catch{}const S=(0,o.spawn)("opencode",v,{detached:!0,stdio:["ignore","ignore",k??"ignore"],cwd:c||process.cwd(),env:n})
717
- return S.unref(),f&&f(S.pid),{success:!0,pid:S.pid}}catch(e){const t=(0,n.getErrorMessage)(e)
718
- return m&&e instanceof Error&&m(e),{success:!1,error:t}}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.spawnDetachedSubagent=r,exports.spawnJournalSubagent=function(e,t,s,o){return r({agent:"journal",prompt:`Generate a journal entry for request ${e}.\n\nSteps:\n1. Use ${t}__generate_request_journal tool with requestId: ${e}\n2. Write a concise narrative journal entry based on the returned data covering:\n - What was implemented and why\n - Key decisions and trade-offs made\n - Challenges encountered and solutions\n - Files modified and their purposes\n - How acceptance criteria were met\n3. Save as JOURNAL-${e}.md using ${t}__document_operations (operation: create, type: Report)\n\nKeep it story-focused and informative.`,cwd:s,parentServerUrl:o})},exports.spawnWorkSubagent=function(e,t,s){return r({agent:"work",prompt:e,cwd:t,parentServerUrl:s})}
719
- const o=t(5317),n=t(8824),i=t(9896),a=t(6928)},8148(e,exports,t){function s(e,t){try{const t=(0,o.join)(e,c,"opencode.json"),s=(0,r.readFileSync)(t,"utf8"),n=JSON.parse(s)
715
+ d&&(n.OPENCODE_PARENT_SERVER=d),p&&(n.ALCHEMY_INITIAL_BRANCH=p),e.allowParallel&&(n.ALCHEMY_ALLOW_PARALLEL="1")
716
+ const u=new Date,y=`${(u.getHours()+"").padStart(2,"0")}${(u.getMinutes()+"").padStart(2,"0")}${(u.getSeconds()+"").padStart(2,"0")}`,v=Math.random().toString(36).substring(2,7)
717
+ let w
718
+ if(n.ALCHEMY_SESSION_TAG=`mcp-${y}-${v}`,s(l,"Spawn env setup",{agent:t,initialBranch:p||"none",hasParentServer:!!d,sessionId:h||"new",attachMode:!!f}),w=f&&h&&r?["run","--attach",f,"--session",h,"--agent",t,r]:h&&r?["run","--session",h,"--agent",t,r]:r?["run","--agent",t,r]:["run","--agent",t],g){const e=w.findIndex((e=>e===r))
719
+ e>0?w.splice(e,0,"--model",g):w.push("--model",g)}s(l,"Spawning opencode",{args:w.map((e=>e===r?`[prompt:${e.length}chars]`:e)),model:g||"not specified",promptLength:r?.length,promptPreview:r?.slice(0,100),cwd:l,hasSessionId:!!h,hasParentServerUrl:!!d})
720
+ const C=(0,a.join)(l||process.cwd(),".alchemy_lab"),k=(0,a.join)(C,"spawn-stderr.log")
721
+ let S
722
+ try{S=(0,i.openSync)(k,"a")}catch{}const I=(0,c.resolveRunCommand)(),b=(0,o.spawn)(I.command,[...I.prependArgs,...w],{detached:!0,stdio:["ignore","ignore",S??"ignore"],cwd:l||process.cwd(),env:n})
723
+ return b.unref(),m&&m(b.pid),{success:!0,pid:b.pid}}catch(e){const t=(0,n.getErrorMessage)(e)
724
+ return y&&e instanceof Error&&y(e),{success:!1,error:t}}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.spawnDetachedSubagent=r,exports.spawnJournalSubagent=function(e,t,s,o){return r({agent:"journal",prompt:`Generate a journal entry for request ${e}.\n\nSteps:\n1. Use ${t}__generate_request_journal tool with requestId: ${e}\n2. Write a concise narrative journal entry based on the returned data covering:\n - What was implemented and why\n - Key decisions and trade-offs made\n - Challenges encountered and solutions\n - Files modified and their purposes\n - How acceptance criteria were met\n3. Save as JOURNAL-${e}.md using ${t}__document_operations (operation: create, type: Report)\n\nKeep it story-focused and informative.`,cwd:s,parentServerUrl:o})},exports.spawnWorkSubagent=function(e,t,s){return r({agent:"work",prompt:e,cwd:t,parentServerUrl:s})}
725
+ const o=t(5317),n=t(8824),i=t(9896),a=t(6928),c=t(9068)},8148(e,exports,t){function s(e,t){try{const t=(0,o.join)(e,c,"opencode.json"),s=(0,r.readFileSync)(t,"utf8"),n=JSON.parse(s)
720
726
  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,a.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,m=s(c,p)
721
727
  let y,v=null
722
728
  m?v=await async function(e,t,s,r,o,n,i,c){try{const l=await fetch(e+"/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:t,...o?{parentID:o}:{}})})
@@ -791,11 +797,11 @@ t?.mode?.alchemy?.tools&&(f=t.mode.alchemy.tools,this.logger.info("Found existin
791
797
  i?.openCodeModel?k.model=i.openCodeModel:p?k.model=p:m&&(k.model=m)
792
798
  const I=c.join(o,".opencode","agent")
793
799
  await s.mkdir(I,{recursive:!0})
794
- const E=S||"anthropic/claude-opus-4-5-20251101"
795
- let b,j=null,T=null
796
- 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}),j=await this.fetchAgentsFromRemote(i.projectId),j&&j.length>0){this.logger.info("Using agents from remote database",{count:j.length})
800
+ const b=S||"anthropic/claude-opus-4-5-20251101"
801
+ let E,j=null,T=null
802
+ if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),T=await this.fetchProjectFromRemote(i.projectId),E=T?.metadata?.globalSystemPrompt,E&&this.logger.info("Using global system prompt from project metadata",{projectId:i.projectId,promptLength:E.length}),j=await this.fetchAgentsFromRemote(i.projectId),j&&j.length>0){this.logger.info("Using agents from remote database",{count:j.length})
797
803
  for(const e of j){if(!e.enabled)continue
798
- const t=this.generateAgentMarkdown(e,d,E,b),r=c.join(I,e.name+".md")
804
+ const t=this.generateAgentMarkdown(e,d,b,E),r=c.join(I,e.name+".md")
799
805
  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")
800
806
  else this.logger.warn("No projectId provided - agents must be configured via database")
801
807
  const P={$schema:"https://opencode.ai/config.json",model:S,mcp:{[d]:{type:"local",command:[n,u,o],enabled:!0}}}
@@ -806,10 +812,16 @@ for(const[s,o]of Object.entries(e.tools||{})){const e=o,n=s.replace(/-/g,"_")
806
812
  t[r.OPENCODE_BUILTIN_TOOLS.has(s)?s:`${d}_${n}`]=e}const s=T?.metadata?.bashToolAgents||{}
807
813
  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)
808
814
  let o=""
809
- b&&(o+=b.trim()+"\n\n"),e.prompt&&(o+=e.prompt),e.promptAddition&&(o+="\n\n"+e.promptAddition),P.agent[e.name]={tools:t,...o?{prompt:o}:{},...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}P.agent.general={...P.agent.general,disable:!0},P.agent.explore={...P.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}}
810
- 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`
815
+ E&&(o+=E.trim()+"\n\n"),e.prompt&&(o+=e.prompt),e.promptAddition&&(o+="\n\n"+e.promptAddition),P.agent[e.name]={tools:t,...o?{prompt:o}:{},...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}P.agent.general={...P.agent.general,disable:!0},P.agent.explore={...P.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}}
816
+ const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${E?E.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`
811
817
  await s.writeFile(c.join(I,"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(P.agent).length})}const A=[]
812
- if(A.push("file://"+c.join(o,".opencode","plugin","inject-session.js")),A.push("file://"+c.join(o,".opencode","plugin","event-hooks.js")),A.push("file://"+c.join(o,".opencode","plugin","system-logger.js")),A.length>0&&(P.plugin=A),await s.writeFile(h,JSON.stringify(P,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:h,plugins:A}),v&&!i)try{let e={}
818
+ A.push("file://"+c.join(o,".opencode","plugin","inject-session.js")),A.push("file://"+c.join(o,".opencode","plugin","event-hooks.js")),A.push("file://"+c.join(o,".opencode","plugin","system-logger.js")),A.length>0&&(P.plugin=A),await s.writeFile(h,JSON.stringify(P,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:h,plugins:A})
819
+ try{const e=await Promise.resolve().then(t.t.bind(t,857,23)),r=c.join(e.homedir(),".alchemy_lab","opencode","config")
820
+ await s.mkdir(r,{recursive:!0})
821
+ const o=c.join(r,"opencode.json")
822
+ let n={}
823
+ try{n=JSON.parse(await s.readFile(o,"utf-8"))}catch{}const i=(n.plugin||[]).filter((e=>!e.includes(".opencode/plugin/")))
824
+ n.plugin=[...i,...A],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={}
813
825
  try{const t=await s.readFile(w,"utf-8")
814
826
  e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=C,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)),r=await s(o,l??!1,this.logger,e)
815
827
  r.synced.length>0&&this.logger.info("Synced plugins during OpenCode config",{synced:r.synced}),r.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:r.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=r,r.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","fetch","agent","task","question","todoread","todowrite"])},9067(e,exports,t){var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
@@ -858,7 +870,14 @@ if(!o.ok)return void this.logger.debug("Polling response not ok",{status:o.statu
858
870
  const i=await o.json()
859
871
  if(this.logger.debug("Google OAuth polling response",{data:i}),i.success&&i.data&&i.data.authenticated&&i.data.token){this.logger.info("Google OAuth authentication completed")
860
872
  const e=this.parseTokenResponse(i.data)
861
- e&&(this.pollingStatus=n.AUTHENTICATED,this.tokenResult=e,this.stopPolling(),s&&s(e))}else this.logger.debug("Not authenticated yet, continuing to poll",{hasSuccess:!!i.success,hasData:!!i.data,authenticated:i.data?.authenticated})}catch(e){this.logger.error("Error during Google OAuth polling",{error:(0,o.getErrorMessage)(e)}),this.pollingStatus=n.ERROR,this.pollingError=(0,o.getErrorMessage)(e),this.stopPolling(),i&&i(this.pollingError)}}),this.pollingIntervalMs),{authUrl:e,state:t}}}},9266(e,exports,t){var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
873
+ e&&(this.pollingStatus=n.AUTHENTICATED,this.tokenResult=e,this.stopPolling(),s&&s(e))}else this.logger.debug("Not authenticated yet, continuing to poll",{hasSuccess:!!i.success,hasData:!!i.data,authenticated:i.data?.authenticated})}catch(e){this.logger.error("Error during Google OAuth polling",{error:(0,o.getErrorMessage)(e)}),this.pollingStatus=n.ERROR,this.pollingError=(0,o.getErrorMessage)(e),this.stopPolling(),i&&i(this.pollingError)}}),this.pollingIntervalMs),{authUrl:e,state:t}}}},9068(e,exports,t){function s(){const e=process.env.ALCHEMY_OPENCODE_DIR
874
+ if(e)return e
875
+ try{const e=Object(function(){var e=Error("Cannot find module 'alchemylab-opencode/package.json'")
876
+ throw e.code="MODULE_NOT_FOUND",e}())
877
+ return(0,o.dirname)(e)}catch{}}function r(e,t){return{command:"node",prependArgs:["--experimental-strip-types","--experimental-transform-types","--no-warnings","--import",(0,o.join)(e,"node-preload.mjs"),(0,o.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.resolveServeCommand=function(){const e=s()
878
+ return e?r((0,o.join)(e,"packages","opencode","src"),"serve.ts"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=s()
879
+ return e?r((0,o.join)(e,"packages","opencode","src"),"run.ts"):{command:"opencode",prependArgs:[]}}
880
+ const o=t(6928)},9266(e,exports,t){var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
862
881
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleOpenCodeProxyAction=async function(e,t){const{method:s,path:i,body:a,headers:c}=e
863
882
  if(!s||!i)return{success:!1,message:"method and path are required in proxy payload"}
864
883
  const l=function(e){const t=r.default.join(e,".alchemy_lab","opencode.json")
@@ -906,10 +925,10 @@ return e.data?.id||e.id}r.info(`Project registration failed (HTTP ${i.status})`)
906
925
  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),C=e(8824),k=e(6800),S=e(4139);(async function(){const i=process.argv.slice(2)
907
926
  let p
908
927
  const I=i.indexOf("--port");-1!==I&&i[I+1]&&(p=parseInt(i[I+1],10),isNaN(p)&&(console.error("\u274c Invalid port number"),process.exit(1)))
909
- const E=i.filter(((e,t)=>"--port"!==e&&(-1===I||t!==I+1)))
910
- if(0===E.length)return void await async function(s={}){const i=process.cwd(),l=(0,h.join)(i,S.ALCHEMY_DIR_NAME)
928
+ const b=i.filter(((e,t)=>"--port"!==e&&(-1===I||t!==I+1)))
929
+ if(0===b.length)return void await async function(s={}){const i=process.cwd(),l=(0,h.join)(i,S.ALCHEMY_DIR_NAME)
911
930
  await(0,u.mkdir)(l,{recursive:!0})
912
- const p=(0,h.join)(l,"server.log"),{createLogger:I}=await Promise.resolve().then(e.t.bind(e,2782,23)),E=I({serviceName:"alchemy-server",console:!1,logToFile:!0,logFilePath:p,baseDir:i}),b=await a(E),j=new f.TokenManager(E,i,{remoteServerUrl:b,allowInteractiveAuth:!1})
931
+ const p=(0,h.join)(l,"server.log"),{createLogger:I}=await Promise.resolve().then(e.t.bind(e,2782,23)),b=I({serviceName:"alchemy-server",console:!1,logToFile:!0,logFilePath:p,baseDir:i}),E=await a(b),j=new f.TokenManager(b,i,{remoteServerUrl:E,allowInteractiveAuth:!1})
913
932
  await j.initialize()
914
933
  let T=await j.getToken()
915
934
  if(!T?.token){console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
@@ -920,12 +939,12 @@ console.log("\u{1f4c1} Initializing AlchemyLab project..."),console.log("")
920
939
  const n=(0,h.join)(e,S.ALCHEMY_DIR_NAME)
921
940
  let i
922
941
  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")
923
- 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,b,P,E)
942
+ 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)
924
943
  const A=(0,h.join)(i,S.ALCHEMY_DIR_NAME,"project.json")
925
944
  let M
926
945
  try{const e=JSON.parse((0,d.readFileSync)(A,"utf-8"))
927
- if(M=e.id||void 0,!1===e.registeredRemotely&&P){const t=await c(b,P,i,E,"cli-init-retry")
928
- t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(A,JSON.stringify(e,null,2),"utf-8"),M=t,console.log("\u2705 Project registered: "+t))}}catch{E.warn("Could not read project.json")}await async function(t){try{return void(0,g.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{}let s="latest"
946
+ if(M=e.id||void 0,!1===e.registeredRemotely&&P){const t=await c(E,P,i,b,"cli-init-retry")
947
+ t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(A,JSON.stringify(e,null,2),"utf-8"),M=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"
929
948
  try{const e=(0,h.join)(t,S.ALCHEMY_DIR_NAME,"config.json")
930
949
  if((0,d.existsSync)(e)){const t=JSON.parse((0,d.readFileSync)(e,"utf-8"))
931
950
  t.opencodeVersion&&(s=t.opencodeVersion)}}catch{}console.log(""),console.log("\u{1f4e6} OpenCode not found in PATH. Installing...")
@@ -938,30 +957,30 @@ const r=(0,h.join)(process.env.HOME||"/usr/local/bin",".local","bin")
938
957
  await(0,u.mkdir)(r,{recursive:!0})
939
958
  const o=(0,h.join)(r,"opencode")
940
959
  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,C.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)
941
- const _=t()
942
- 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"+_,"::::::::::::::::::::::::::::::::::::::::::...::::::::::::::::::::::\n"].join("\n")),console.log("\u{1f4c1} Project: "+i)
943
- const O=s.port||await(0,k.findAvailablePort)(3456),x=new y.AlchemyActionServer(E,{projectPath:i,remoteServerUrl:b,port:O,authToken:P})
960
+ const O=t()
961
+ 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)
962
+ const _=s.port||await(0,k.findAvailablePort)(3456),x=new y.AlchemyActionServer(b,{projectPath:i,remoteServerUrl:E,port:_,authToken:P})
944
963
  try{await x.start()
945
964
  let t=process.env.FRONTEND_URL
946
- if(!t)if(b.includes("api.alchemylabcode.com"))t="https://alchemylabcode.com"
947
- else{try{const e=await fetch(b+"/health")
965
+ if(!t)if(E.includes("api.alchemylabcode.com"))t="https://alchemylabcode.com"
966
+ else{try{const e=await fetch(E+"/health")
948
967
  if(e.ok){const s=await e.json()
949
- t=s?.data?.frontendUrl}}catch{}t||(t=b.replace(/:\d+/,":3000"))}console.log(""),console.log(" Action server .... port "+x.getPort())
950
- try{const e=(0,v.getInstallContext)(),t=new m.MCPClientConfigurer(E,b,P),s=new w.UnifiedLauncher(E,e,t),r=await s.launch({projectPath:i,settings:M?{projectId:M}:void 0})
968
+ t=s?.data?.frontendUrl}}catch{}t||(t=E.replace(/:\d+/,":3000"))}console.log(""),console.log(" Action server .... port "+x.getPort())
969
+ try{const e=(0,v.getInstallContext)(),t=new m.MCPClientConfigurer(b,E,P),s=new w.UnifiedLauncher(b,e,t),r=await s.launch({projectPath:i,settings:M?{projectId:M}:void 0})
951
970
  if(r.success&&r.port){r.pid&&x.trackChildPid(r.pid),x.setOpenCodePort(r.port),console.log(" OpenCode serve ... port "+r.port)
952
- const e=(0,h.join)(i,S.ALCHEMY_DIR_NAME,"opencode.json");(0,d.writeFileSync)(e,JSON.stringify({port:r.port,pid:r.pid,startedAt:(new Date).toISOString()},null,2)),E.info("Wrote OpenCode config",{path:e,port:r.port})}else{if(!r.success)throw Error(r.error||"Launch failed")
953
- console.log(" OpenCode serve ... started")}}catch(e){E.warn("Failed to launch OpenCode serve",{error:(0,C.getErrorMessage)(e)}),console.log(" OpenCode serve ... failed (run manually: opencode serve)")}console.log(""),console.log(` \u2192 Alchemy UI: \x1b]8;;${t}\x07${t}\x1b]8;;\x07`),console.log("")
971
+ const e=(0,h.join)(i,S.ALCHEMY_DIR_NAME,"opencode.json");(0,d.writeFileSync)(e,JSON.stringify({port:r.port,pid:r.pid,startedAt:(new Date).toISOString()},null,2)),b.info("Wrote OpenCode config",{path:e,port:r.port})}else{if(!r.success)throw Error(r.error||"Launch failed")
972
+ console.log(" OpenCode serve ... started")}}catch(e){b.warn("Failed to launch OpenCode serve",{error:(0,C.getErrorMessage)(e)}),console.log(" OpenCode serve ... failed (run manually: opencode serve)")}console.log(""),console.log(` \u2192 Alchemy UI: \x1b]8;;${t}\x07${t}\x1b]8;;\x07`),console.log("")
954
973
  let s=!1,r=null
955
974
  const a=async()=>{s||(s=!0,r&&clearInterval(r),console.log(""),console.log("Shutting down..."),await x.stop(),process.exit(0))}
956
975
  process.on("SIGTERM",a),process.on("SIGINT",a)
957
- const{InteractiveShell:c}=await e.e(994).then(e.t.bind(e,5994,23)),l=new c({projectPath:i,logger:E,remoteServerUrl:b,authToken:P,projectId:M})
976
+ const{InteractiveShell:c}=await e.e(994).then(e.t.bind(e,5994,23)),l=new c({projectPath:i,logger:b,remoteServerUrl:E,authToken:P,projectId:M})
958
977
  r=setInterval((async()=>{try{if(await j.hasValidToken())return
959
978
  console.log(""),console.log("\u26a0\ufe0f Auth token expired. Opening browser to re-authenticate...")
960
979
  const e=await j.startAuthenticationFlow()
961
980
  if(console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(j)){const e=await j.getToken()
962
- e?.token&&(P=e.token,x.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){E.warn("Token watchdog check failed",{error:(0,C.getErrorMessage)(e)})}}),3e5),await l.start(a)}catch(e){E.error("Failed to start server",{error:(0,C.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,C.getErrorMessage)(e)),process.exit(1)}}({port:p})
963
- const b=E[0].toLowerCase()
964
- switch("--help"!==b&&"-h"!==b||(s(),process.exit(0)),"--version"!==b&&"-v"!==b||(console.log("alchemy v"+t()),process.exit(0)),b){case"serve":await async function(e={}){const s=process.cwd()
981
+ e?.token&&(P=e.token,x.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,C.getErrorMessage)(e)})}}),3e5),await l.start(a)}catch(e){b.error("Failed to start server",{error:(0,C.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,C.getErrorMessage)(e)),process.exit(1)}}({port:p})
982
+ const E=b[0].toLowerCase()
983
+ 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()
965
984
  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))
966
985
  const o=(0,l.createEnvLogger)({serviceName:"alchemy-server",console:!0,baseDir:s}),n=t()
967
986
  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)
@@ -978,4 +997,4 @@ break
978
997
  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})
979
998
  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.")}()
980
999
  break
981
- default:console.error("\u274c Unknown command: "+b),console.log(""),s(),process.exit(1)}})().catch((e=>{console.error("\u{1f4a5} Unexpected error:",e),process.exit(1)}))})(),module.exports=i})()
1000
+ 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})()