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 +3 -3
- package/dist/cli.js +121 -102
- package/dist/http-server.js +663 -707
- package/dist/index.js +559 -603
- package/dist/mcp-direct.js +974 -1018
- package/package.json +10 -2
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:["
|
|
12
|
-
if(null===m
|
|
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===
|
|
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,
|
|
40
|
-
try{(0,s.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{return{success:!1,error:"OpenCode not found in PATH."}}
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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,
|
|
140
|
-
if(!e?.id)return void
|
|
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
|
|
143
|
-
if(!
|
|
144
|
-
const
|
|
145
|
-
this.logger.debug("Fetched project for global prompt",{projectId:e.id,hasProject:!!
|
|
146
|
-
const o
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
await c.default.promises.mkdir(
|
|
150
|
-
let
|
|
151
|
-
for(const e of
|
|
152
|
-
const t="alchemy",s=this.configurer.generateAgentMarkdown(e,t,"anthropic/claude-sonnet-4-20250514",
|
|
153
|
-
await c.default.promises.writeFile(r,s,"utf8"),
|
|
154
|
-
if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),
|
|
155
|
-
if(
|
|
156
|
-
c.default.writeFileSync(e,JSON.stringify(
|
|
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
|
|
159
|
-
if(
|
|
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{
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
try{
|
|
166
|
-
|
|
167
|
-
if(!
|
|
168
|
-
if(!/^req\d+$/.test(
|
|
169
|
-
this.logger.info("Generating journal for request",{requestId:
|
|
170
|
-
const
|
|
171
|
-
|
|
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
|
|
216
|
-
if(
|
|
217
|
-
if(!e.ok||!e.body)return
|
|
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
|
|
220
|
-
try{for(;;){const{done:e,value:s}=await
|
|
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{
|
|
223
|
-
return this.proxyAbortControllers.set(s,
|
|
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
|
-
|
|
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),
|
|
611
|
-
if((0,r.existsSync)(
|
|
612
|
-
await e.raw(["reset","--hard",k]),m.info("Reset test worktree to latest branch HEAD",{worktreePath:
|
|
613
|
-
try{await
|
|
614
|
-
try{if(!(await
|
|
615
|
-
await
|
|
616
|
-
m.info("Test worktree already exists, using as-is",{worktreePath:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
710
|
-
const
|
|
711
|
-
let
|
|
712
|
-
if(n.ALCHEMY_SESSION_TAG=`mcp-${
|
|
713
|
-
e>0?
|
|
714
|
-
const
|
|
715
|
-
let
|
|
716
|
-
try{
|
|
717
|
-
return
|
|
718
|
-
return
|
|
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
|
|
795
|
-
let
|
|
796
|
-
if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),T=await this.fetchProjectFromRemote(i.projectId),
|
|
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
|
|
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
|
-
|
|
810
|
-
const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\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
|
-
|
|
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}}}},
|
|
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
|
|
910
|
-
if(0===
|
|
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)),
|
|
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,
|
|
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(
|
|
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{
|
|
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
|
|
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"+
|
|
943
|
-
const
|
|
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(
|
|
947
|
-
else{try{const e=await fetch(
|
|
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=
|
|
950
|
-
try{const e=(0,v.getInstallContext)(),t=new m.MCPClientConfigurer(E,
|
|
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)),
|
|
953
|
-
console.log(" OpenCode serve ... started")}}catch(e){
|
|
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:
|
|
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){
|
|
963
|
-
const b
|
|
964
|
-
switch("--help"!==
|
|
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: "+
|
|
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})()
|