alchemylab 1.4.3 → 1.4.5
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/cli.js +130 -103
- package/dist/http-server.js +73 -65
- package/dist/index.js +635 -627
- package/dist/mcp-direct.js +73 -65
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
(()=>{function e(t){var r=n[t]
|
|
3
3
|
if(void 0!==r)return r.exports
|
|
4
4
|
var module=n[t]={exports:{}}
|
|
5
|
-
return o[t].call(module.exports,module,module.exports,e),module.exports}var t,r,s,o={
|
|
6
|
-
module.exports=require("alchemylab-opencode/package.json")},270(e,exports){"use strict"
|
|
5
|
+
return o[t].call(module.exports,module,module.exports,e),module.exports}var t,r,s,o={270(e,exports){"use strict"
|
|
7
6
|
Object.defineProperty(exports,"__esModule",{value:!0})},355(e,exports,t){"use strict"
|
|
8
7
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.registerHooksRoutes=function(e,t){e.get("/api/event-hooks/by-event/:eventType",((e,s)=>async function(e,t,s){const{apiClient:o,logger:n,getProjectId:i}=s
|
|
9
8
|
try{const{eventType:r}=e.params,{sessionId:s}=e.query,a=i()
|
|
@@ -27,10 +26,10 @@ const{sessionId:s,hookId:n}=e.params,a=await o.post(`/api/projects/${r}/hook-ite
|
|
|
27
26
|
t.json(a)}catch(e){n.debug("Failed to increment hook iteration",{error:(0,r.getErrorMessage)(e)}),t.json({success:!0,data:{iterationCount:0}})}}(e,s,t)))}
|
|
28
27
|
const r=t(8824)},441(e,exports){"use strict"
|
|
29
28
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.BAD_REQUEST=exports.INTERNAL_ERROR=exports.NOT_FOUND=exports.FORBIDDEN=exports.UNAUTHORIZED=exports.VALIDATION_ERROR=exports.MCP_PROTOCOL_ERROR=exports.REQUEST_VALIDATION_FAILED=exports.REQUEST_ID_INVALID_FORMAT=exports.REQUEST_NOT_FOUND=exports.CONNECTION_ERROR=exports.SERVICE_UNAVAILABLE=exports.CONNECTION_TIMEOUT=exports.SERVER_ERROR=exports.NETWORK_ERROR=exports.TASK_VALIDATION_FAILED=exports.TASK_ID_INVALID_FORMAT=exports.TASK_NOT_FOUND=void 0,exports.TASK_NOT_FOUND="TASK_NOT_FOUND",exports.TASK_ID_INVALID_FORMAT="TASK_ID_INVALID_FORMAT",exports.TASK_VALIDATION_FAILED="TASK_VALIDATION_FAILED",exports.NETWORK_ERROR="NETWORK_ERROR",exports.SERVER_ERROR="SERVER_ERROR",exports.CONNECTION_TIMEOUT="CONNECTION_TIMEOUT",exports.SERVICE_UNAVAILABLE="SERVICE_UNAVAILABLE",exports.CONNECTION_ERROR="CONNECTION_ERROR",exports.REQUEST_NOT_FOUND="REQUEST_NOT_FOUND",exports.REQUEST_ID_INVALID_FORMAT="REQUEST_ID_INVALID_FORMAT",exports.REQUEST_VALIDATION_FAILED="REQUEST_VALIDATION_FAILED",exports.MCP_PROTOCOL_ERROR="MCP_PROTOCOL_ERROR",exports.VALIDATION_ERROR="VALIDATION_ERROR",exports.UNAUTHORIZED="UNAUTHORIZED",exports.FORBIDDEN="FORBIDDEN",exports.NOT_FOUND="NOT_FOUND",exports.INTERNAL_ERROR="INTERNAL_ERROR",exports.BAD_REQUEST="BAD_REQUEST"},489(e,exports,t){"use strict"
|
|
30
|
-
function r(e,t){return(0,h.join)(e,p,m,"pool-"+t)}function s(){const e=
|
|
29
|
+
function r(e,t){return(0,h.join)(e,p,m,"pool-"+t)}function s(){const e=S.shift()
|
|
31
30
|
e&&setImmediate(e)}async function o(e,t){const o=Date.now()
|
|
32
31
|
!function(e){let t=!1
|
|
33
|
-
for(const r of E.values())r.busy&&r.busySince&&e-r.busySince>
|
|
32
|
+
for(const r of E.values())r.busy&&r.busySince&&e-r.busySince>I&&(r.busy=!1,r.busySince=void 0,t=!0)
|
|
34
33
|
t&&s()}(o)
|
|
35
34
|
for(let s=0;t>s;s++){const t=r(e,s)
|
|
36
35
|
if(!(E.get(t)||{busy:!1}).busy)return E.set(t,{busy:!0,busySince:o}),{path:t,index:s,storedFingerprint:await a(t)}}return null}function n(e){const t=E.get(e)
|
|
@@ -39,16 +38,16 @@ return await async function r(s){const o=s?(0,h.join)(e,s):e
|
|
|
39
38
|
let n
|
|
40
39
|
try{n=await d.promises.readdir(o,{withFileTypes:!0})}catch{return}for(const e of n){const o=s?`${s}/${e.name}`:e.name
|
|
41
40
|
!s&&w.has(e.name)||!s&&v.has(e.name)||(e.isDirectory()?await r(o):(e.isFile()||e.isSymbolicLink())&&t.push(o))}}(""),t.sort(),t}async function l(e){try{const t=(await d.promises.readFile((0,h.join)(e,y),"utf-8")).split("\n").map((e=>e.trim())).filter(Boolean)
|
|
42
|
-
return new Set(t)}catch{return null}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getSlotPath=r,exports.acquireSlot=async function(e,t,r=
|
|
41
|
+
return new Set(t)}catch{return null}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getSlotPath=r,exports.acquireSlot=async function(e,t,r=b){return await o(e,t)||new Promise(((i,a)=>{let c=!1
|
|
43
42
|
const l=Date.now(),d=setTimeout((()=>{if(c)return
|
|
44
43
|
c=!0
|
|
45
|
-
const e=
|
|
46
|
-
0>e||
|
|
44
|
+
const e=S.indexOf(u)
|
|
45
|
+
0>e||S.splice(e,1),a(Error(`All ${t} pool slot(s) busy after waiting ${Math.round((Date.now()-l)/1e3)}s`))}),r)
|
|
47
46
|
d.unref()
|
|
48
47
|
const u=async()=>{if(c)return
|
|
49
48
|
const r=await o(e,t)
|
|
50
|
-
c?r&&n(r.path):r?(c=!0,clearTimeout(d),i(r)):(
|
|
51
|
-
|
|
49
|
+
c?r&&n(r.path):r?(c=!0,clearTimeout(d),i(r)):(S.unshift(u),s())}
|
|
50
|
+
S.push(u)}))},exports.releaseSlot=n,exports.scheduleSlotRelease=i,exports.watchPidAndRelease=function(e,t,r,s=5e3){if(i(t,r),!e||0>=e)return
|
|
52
51
|
const o=setInterval((()=>{try{process.kill(e,0)}catch{n(t),clearInterval(o)}}),s)
|
|
53
52
|
o.unref()},exports.readSlotFingerprint=a,exports.writeSlotFingerprint=async function(e,t){try{await d.promises.writeFile((0,h.join)(e,f),t,"utf-8")}catch(e){console.warn("worktree-pool: writeSlotFingerprint failed (non-fatal):",(0,g.getErrorMessage)(e))}},exports.computeFingerprint=async function(e,t){if(!t||0===t.length)return""
|
|
54
53
|
const r=(0,u.createHash)("sha256")
|
|
@@ -58,8 +57,8 @@ await d.promises.writeFile((0,h.join)(e,y),t.join("\n")+"\n","utf-8")}catch(e){c
|
|
|
58
57
|
if(!t)return{removed:0}
|
|
59
58
|
const r=await c(e)
|
|
60
59
|
let s=0
|
|
61
|
-
for(const o of r)if(!t.has(o))try{await d.promises.rm((0,h.join)(e,o),{force:!0}),s++}catch{}return{removed:s}},exports._resetForTests=function(){E.clear(),
|
|
62
|
-
const d=t(9896),u=t(6982),h=t(6928),g=t(8824),p=".alchemy_lab",m="test-worktrees",f=".fingerprint.sha256",y=".alchemy-warm-snapshot.txt",v=new Set([f,y]),w=new Set([".git"]),
|
|
60
|
+
for(const o of r)if(!t.has(o))try{await d.promises.rm((0,h.join)(e,o),{force:!0}),s++}catch{}return{removed:s}},exports._resetForTests=function(){E.clear(),S.length=0}
|
|
61
|
+
const d=t(9896),u=t(6982),h=t(6928),g=t(8824),p=".alchemy_lab",m="test-worktrees",f=".fingerprint.sha256",y=".alchemy-warm-snapshot.txt",v=new Set([f,y]),w=new Set([".git"]),I=12e5,b=6e5,E=new Map,S=[]},833(e,exports,t){"use strict"
|
|
63
62
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
64
63
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.getOpenCodeUrlFromConfig=function(e){const t=o.default.join(e,".alchemy_lab","opencode.json")
|
|
65
64
|
try{if(!s.default.existsSync(t))return null
|
|
@@ -204,7 +203,7 @@ const y=await f.json()
|
|
|
204
203
|
if(!y.id)throw Error("Session created in OpenCode but no ID returned")
|
|
205
204
|
const v=y.id
|
|
206
205
|
u.info("Pre-created OpenCode session",{sessionId:v,title:p,agent:t})
|
|
207
|
-
const w=this.registerSession({sessionId:v,parentSessionId:a,projectId:h,agent:t,title:p,specialization:c,specializationHeadline:l,callerAgentType:d,requestId:i}),
|
|
206
|
+
const w=this.registerSession({sessionId:v,parentSessionId:a,projectId:h,agent:t,title:p,specialization:c,specializationHeadline:l,callerAgentType:d,requestId:i}),I=this.resolveModel(t,n),b=i?(0,r.ensureRequestWorktree)(n,i,u).catch((e=>{u.warn("Pre-spawn worktree creation failed (non-fatal)",{requestId:i,error:(0,o.getErrorMessage)(e)})})):Promise.resolve(),[,E]=await Promise.all([w,I,b])
|
|
208
207
|
return{sessionId:v,model:E,serverUrl:g,initialBranch:i?"requests/"+i:void 0}}async registerSession(e){const{sessionId:t,parentSessionId:r,projectId:s,agent:n,title:i,specialization:a,specializationHeadline:c,callerAgentType:l,requestId:d}=e,{apiClient:u,logger:h}=this.deps
|
|
209
208
|
try{let e,g
|
|
210
209
|
if(r)try{const t=await u.post(`/api/projects/${s}/cli-sessions`,{externalSessionId:r,providerId:"opencode",status:"active",agentType:l||void 0}),o=t?.data||t
|
|
@@ -241,8 +240,8 @@ if(!m)return void this.logger.info("Quest tick: no persistent alchemist session
|
|
|
241
240
|
const f=(t.currentIteration||0)+1
|
|
242
241
|
let y=!1
|
|
243
242
|
try{y=await this.bumpQuestMetadata(e,f)}catch(e){return void this.logger.warn("Quest tick: metadata PATCH failed; skipping prompt",{questId:a,error:e+""})}if(!y)return void this.logger.info("Quest tick: status changed during tick; skipping prompt",{questId:a})
|
|
244
|
-
const v=this.buildTickPrompt(e,f,d,h,p+1),w={logger:this.logger,projectId:this.projectId,projectPath:this.projectPath,apiClient:this.apiClient,cachedDefaultModel:this.getDefaultModel()??null},
|
|
245
|
-
try{await(0,s.executeSameSession)(v,m,w,
|
|
243
|
+
const v=this.buildTickPrompt(e,f,d,h,p+1),w={logger:this.logger,projectId:this.projectId,projectPath:this.projectPath,apiClient:this.apiClient,cachedDefaultModel:this.getDefaultModel()??null},I={alchemy:{kind:"quest-tick",payload:{questId:a,iteration:f,criteria:t.criteria||"",openCount:d,taskOnlyCount:h,maxInFlight:n,ticksToday:p+1,maxPerDay:i,minIterations:t.minIterations??o.QUEST_DEFAULTS.minIterations}}}
|
|
244
|
+
try{await(0,s.executeSameSession)(v,m,w,I),this.logger.info("Quest tick fired",{questId:a,iteration:f,alchemistSessionId:m})}catch(e){this.logger.warn("Quest tick: prompt send failed",{questId:a,error:e+""})}}invalidateCache(){this.lastQuestsFetch=0}invalidateAlchemistCache(){this.cachedAlchemistSessionId=null,this.cachedAlchemistAt=0}async fetchOpenQuests(){const e=Date.now()
|
|
246
245
|
if(18e5>e-this.lastQuestsFetch)return this.cachedQuests
|
|
247
246
|
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/plans?type=Quest&limit=100`),r=t?.data||t||[]
|
|
248
247
|
return this.cachedQuests=r.filter((e=>e&&(e.metadata?.status||o.QUEST_STATUS_OPEN)===o.QUEST_STATUS_OPEN)),this.lastQuestsFetch=e,this.cachedQuests}catch(t){throw this.lastQuestsFetch=e,t}}async fetchQuestRequests(e){const t=await this.apiClient.get(`/api/projects/${this.projectId}/requests?questDocumentId=${encodeURIComponent(e)}&limit=1000`)
|
|
@@ -270,7 +269,7 @@ let f=!1
|
|
|
270
269
|
try{await m("create report",(()=>d.post(`/api/projects/${u}/session-messages/reports`,{fromSessionId:n,toSessionId:p,status:"success",summary:"Plan complete. Created requests: "+(a||[]).join(", "),details:c||"Plan complete"}))),f=!0}catch(e){l.error("Failed to create report after retry",{error:(0,r.getErrorMessage)(e)})}let y=!1
|
|
271
270
|
const v=(0,o.getOpenCodeServerUrl)(h,l)
|
|
272
271
|
if(v){const e=`The planner has finished creating the plan.\n\nCreated requests: ${(a||[]).join(", ")}\n\nPlease begin working on these requests.`
|
|
273
|
-
try{const r=await m("notify parent",(()=>fetch(`${v}/session/${p}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:e,metadata:(0,s.partMeta)("
|
|
272
|
+
try{const r=await m("notify parent",(()=>fetch(`${v}/session/${p}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:e,metadata:(0,s.partMeta)("plan-complete",{plannerSessionId:n,summary:c,requestIds:a})}],...t.cachedDefaultModel?(()=>{const[e,...r]=t.cachedDefaultModel.split("/")
|
|
274
273
|
return{model:{providerID:e,modelID:r.join("/")}}})():{}})})))
|
|
275
274
|
r.ok?(y=!0,l.info("Notified parent session to start building",{parentSessionId:p})):l.error("Parent session prompt returned non-OK",{status:r.status,parentSessionId:p})}catch(e){l.error("Could not notify parent session after retry",{error:(0,r.getErrorMessage)(e)})}}else l.error("Could not resolve OpenCode server URL \u2014 parent prompt skipped")
|
|
276
275
|
if(f&&y){const e=g?.id
|
|
@@ -298,8 +297,8 @@ Object.defineProperty(exports,"__esModule",{value:!0}),exports.partMeta=function
|
|
|
298
297
|
return{...e,_meta:{...s,alchemy:{...o,...r,kind:t}}}},exports.readAlchemyMeta=function(e){return e?._meta?.alchemy}},2366(e,exports,t){"use strict"
|
|
299
298
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
300
299
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
|
|
301
|
-
const s=r(t(7252)),o=r(t(8577)),n=t(9896),i=t(5959),a=t(2640),c=t(4192),l=t(1152),d=r(t(6928)),u=r(t(9896)),h=r(t(857)),g=t(5607),p=t(8984),m=t(8524),f=t(8824),y=t(2096),v=t(7210),w=t(7227),
|
|
302
|
-
class
|
|
300
|
+
const s=r(t(7252)),o=r(t(8577)),n=t(9896),i=t(5959),a=t(2640),c=t(4192),l=t(1152),d=r(t(6928)),u=r(t(9896)),h=r(t(857)),g=t(5607),p=t(8984),m=t(8524),f=t(8824),y=t(2096),v=t(7210),w=t(7227),I=t(5419),b=t(3962),E=t(2928),S=t(6563),C=t(1401),k=t(1292),T=t(8148),_=t(9068),j=t(8463),A=t(489),P=t(3807),M=t(7478),O=t(5317),R=t(355),D=t(833)
|
|
301
|
+
class x{updateAuthToken(e){this.options.authToken=e,this.apiClient.authToken=e,this.configurer.authToken=e}constructor(e,t){this.directReportDedup=new Map,this.activeMcpCalls=new Map,this.pendingAgentResync=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.isPolling=!1,this.isProcessingAction=!1,this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.droppedEventCount=0,this.firstDropAt=0,this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.logger=e,this.options=t,this.port=t.port||3456,this.startTime=new Date
|
|
303
302
|
const r=(0,l.createMetricsCollector)(!1)
|
|
304
303
|
this.apiClient=new c.BaseApiClient(t.remoteServerUrl,e,r,t.authToken,{timeout:35e3}),this.configurer=new p.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.cliBridge=new m.CLIBridgeWebSocket(e),(0,m.setCLIBridge)(this.cliBridge),this.hookExecutor=new v.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,s.default)()
|
|
305
304
|
return e.use(((e,t,r)=>{t.setHeader("Access-Control-Allow-Private-Network","true"),r()})),e.use((0,o.default)()),e.use(s.default.json({limit:"10mb"})),e.get("/health",this.handleHealth.bind(this)),e.get("/api/commands/running",(async(e,t)=>{try{const e=16384,r=await Promise.all((0,i.listEntries)().map((async t=>{let r,s="",o=0,i=t.finishedAt??t.startedAt
|
|
@@ -309,7 +308,7 @@ const c=Math.max(0,o-e),l=Buffer.alloc(o-c)
|
|
|
309
308
|
await r.read(l,0,l.length,c),s=l.toString("utf-8").split("\n").slice(-50).join("\n")}catch{}finally{await(r?.close().catch((()=>{})))}return{...t,tail:s,sizeBytes:o,modifiedAt:i}})))
|
|
310
309
|
t.json({success:!0,data:r})}catch(e){t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}})),e.post("/api/commands/start",(async(e,t)=>{try{const{id:r,name:s,command:o,cwd:n,sessionId:a,startedAt:c,logPath:l}=e.body??{}
|
|
311
310
|
if(!(r&&s&&o&&n&&c&&l))return void t.status(400).json({success:!1,error:"missing required fields"});(0,i.addStart)({id:r,name:s,command:o,cwd:n,sessionId:a,startedAt:c,logPath:l}),t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}})),e.post("/api/commands/end",(async(e,t)=>{try{const{id:r,exitCode:s,timedOut:o,finishedAt:n}=e.body??{}
|
|
312
|
-
if(!r||"number"!=typeof s||!n)return void t.status(400).json({success:!1,error:"missing required fields"});(0,i.markEnd)({id:r,exitCode:s,timedOut:!!o,finishedAt:n}),t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}})),e.get("/api/auth-status",this.handleAuthStatus.bind(this)),e.get("/status",this.handleStatus.bind(this)),e.get("/opencode",this.handleOpenCodeConfig.bind(this)),e.post("/sync-agents",this.handleSyncAgents.bind(this)),e.post("/api/generate-journal",this.handleGenerateJournal.bind(this)),(0,
|
|
311
|
+
if(!r||"number"!=typeof s||!n)return void t.status(400).json({success:!1,error:"missing required fields"});(0,i.markEnd)({id:r,exitCode:s,timedOut:!!o,finishedAt:n}),t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}})),e.get("/api/auth-status",this.handleAuthStatus.bind(this)),e.get("/status",this.handleStatus.bind(this)),e.get("/opencode",this.handleOpenCodeConfig.bind(this)),e.post("/sync-agents",this.handleSyncAgents.bind(this)),e.post("/api/generate-journal",this.handleGenerateJournal.bind(this)),(0,R.registerHooksRoutes)(e,{apiClient:this.apiClient,logger:this.logger,getProjectId:()=>this.projectId}),e.get("/api/project-settings",((e,t)=>{t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null}})})),e.post("/api/sync-settings",(async(e,t)=>{await this.syncProjectSettings(),t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null}})})),e.get("/api/agents/by-name/:agentName",this.handleGetAgentByName.bind(this)),e.get("/api/sessions/:sessionId/agent-type",this.handleGetSessionAgentType.bind(this)),e.get("/api/sessions/:sessionId/parent",this.handleGetSessionParent.bind(this)),e.get("/api/sessions/:sessionId/ancestors",(async(e,t)=>{try{const{sessionId:r}=e.params,s=this.projectId
|
|
313
312
|
if(!s)return void t.status(400).json({success:!1,error:"No project"})
|
|
314
313
|
const o=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}/ancestors`),n=(o?.data||o||[]).map((e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title})))
|
|
315
314
|
t.json({success:!0,ancestors:n})}catch(e){t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}})),e.get("/api/registry/projects",(async(e,r)=>{try{const{listLive:e}=await Promise.resolve().then(t.bind(t,2640))
|
|
@@ -322,11 +321,11 @@ if(!r||"before"!==i&&"after"!==i)return void t.status(400).json({success:!1,erro
|
|
|
322
321
|
const a=(0,w.parseToolEvents)({toolName:r,sessionId:s||"",input:o,output:n,timing:i})
|
|
323
322
|
let c=0
|
|
324
323
|
for(const e of a)c+=(await this.hookExecutor.processEvent({eventType:e.eventType,sessionId:s||"",variables:e.variables,timing:e.timing})).triggered
|
|
325
|
-
this.logger.info("tool-event received",{toolName:r,timing:i,sessionId:s||"",events:a.length,triggered:c}),t.json({success:!0,data:{events:a.length,triggered:c}})}catch(e){this.logger.error("tool-event processing failed",{error:(0,f.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}})),e.post("/api/direct-report",this.handleDirectReport.bind(this)),e.post("/api/cli-bridge/command",this.handleCLIBridgeCommand.bind(this)),e.get("/api/cli-bridge/status",this.handleCLIBridgeStatus.bind(this)),e.post("/api/spawn-session",this.handleSpawnSession.bind(this)),e.post("/api/setup-tests",this.handleSetupTests.bind(this)),e.post("/api/setup-commands",this.handleSetupCommands.bind(this)),e.post("/api/run-tests-now",this.handleRunTestsNow.bind(this)),e.get("/api/branch-head",(async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0,s=("string"==typeof e.query.branch?e.query.branch:void 0)||(r?(0,
|
|
324
|
+
this.logger.info("tool-event received",{toolName:r,timing:i,sessionId:s||"",events:a.length,triggered:c}),t.json({success:!0,data:{events:a.length,triggered:c}})}catch(e){this.logger.error("tool-event processing failed",{error:(0,f.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}})),e.post("/api/direct-report",this.handleDirectReport.bind(this)),e.post("/api/cli-bridge/command",this.handleCLIBridgeCommand.bind(this)),e.get("/api/cli-bridge/status",this.handleCLIBridgeStatus.bind(this)),e.post("/api/spawn-session",this.handleSpawnSession.bind(this)),e.post("/api/spawn-planner",this.handleSpawnPlanner.bind(this)),e.post("/api/setup-tests",this.handleSetupTests.bind(this)),e.post("/api/setup-commands",this.handleSetupCommands.bind(this)),e.post("/api/run-tests-now",this.handleRunTestsNow.bind(this)),e.get("/api/branch-head",(async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0,s=("string"==typeof e.query.branch?e.query.branch:void 0)||(r?(0,M.generateRequestBranchName)(r,""):void 0)
|
|
326
325
|
if(!s)return void t.status(400).json({success:!1,error:"requestId or branch required"})
|
|
327
|
-
const o=(await(0,
|
|
326
|
+
const o=(await(0,P.simpleGit)(this.options.projectPath).revparse([s])).trim()
|
|
328
327
|
t.json({success:!0,data:{branch:s,sha:o}})}catch(e){this.logger.debug("branch-head lookup failed",{error:(0,f.getErrorMessage)(e)}),t.json({success:!0,data:{branch:null,sha:null}})}})),e.post("/api/release-pool-slot",(async(e,t)=>{try{const{worktreePath:r}=e.body||{}
|
|
329
|
-
if(!r||"string"!=typeof r)return void t.status(400).json({success:!1,error:"worktreePath required"});(0,
|
|
328
|
+
if(!r||"string"!=typeof r)return void t.status(400).json({success:!1,error:"worktreePath required"});(0,A.releaseSlot)(r),this.logger.info("Released pool slot via report-back",{worktreePath:r}),t.json({success:!0})}catch(e){this.logger.warn("release-pool-slot failed (non-fatal)",{error:(0,f.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}})),e.get("/api/timer-hooks/status",((e,t)=>{const r=this.timerHookScheduler?.getStatus()??{running:!1,trackedHooks:0,inFlight:[],hooks:[]}
|
|
330
329
|
t.json({success:!0,data:r})})),e.post("/api/timer-hooks/:hookId/pause",((e,t)=>{if(!this.timerHookScheduler)return void t.status(503).json({success:!1,error:"Scheduler not running"})
|
|
331
330
|
const r=this.timerHookScheduler.pauseHook(e.params.hookId)
|
|
332
331
|
t.json({success:r})})),e.post("/api/timer-hooks/:hookId/resume",((e,t)=>{if(!this.timerHookScheduler)return void t.status(503).json({success:!1,error:"Scheduler not running"})
|
|
@@ -335,7 +334,7 @@ t.json({success:r})})),e.post("/api/timer-hooks/:hookId/stop",((e,t)=>{if(!this.
|
|
|
335
334
|
const r=this.timerHookScheduler.stopHook(e.params.hookId)
|
|
336
335
|
t.json({success:r})})),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.get("/api/version-warnings",((e,t)=>{t.json({success:!0,data:this.versionWarnings})})),e.post("/api/self-update",(async(e,r)=>{try{const{execSync:e}=await Promise.resolve().then(t.t.bind(t,5317,23)),s=e("npm root -g",{encoding:"utf8"}).trim()
|
|
337
336
|
try{u.default.accessSync(s,u.default.constants.W_OK)}catch{return void r.json({success:!1,error:"Permission denied. Run manually: sudo npm install -g alchemylab@latest"})}const{exec:o}=await Promise.resolve().then(t.t.bind(t,5317,23))
|
|
338
|
-
r.json({success:!0,message:"Update started. The CLI will restart when complete."}),o("npm install -g alchemylab@latest",{timeout:12e4},((e,t,r)=>{e?this.logger.error("Self-update failed",{error:e.message,stderr:r}):(this.logger.info("Self-update complete, shutting down for restart",{stdout:t.trim()}),setTimeout((()=>{console.log("\n\x1b[32mUpdate complete. Please restart alchemylab.\x1b[0m\n"),process.exit(0)}),1e3))}))}catch(e){r.json({success:!1,error:(0,f.getErrorMessage)(e)})}})),e.use("/api/opencode",((e,t)=>this.handleOpenCodeProxy(e,t))),e}handleHealth(e,t){t.json({status:"healthy",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),timestamp:(new Date).toISOString()})}async handleAuthStatus(e,t){const r=this.options.tokenManager
|
|
337
|
+
r.json({success:!0,message:"Update started. The CLI will restart when complete."}),o("npm install -g alchemylab@latest",{timeout:12e4},((e,t,r)=>{e?this.logger.error("Self-update failed",{error:e.message,stderr:r}):(this.logger.info("Self-update complete, shutting down for restart",{stdout:t.trim()}),setTimeout((()=>{console.log("\n\x1b[32mUpdate complete. Please restart alchemylab.\x1b[0m\n"),process.exit(0)}),1e3))}))}catch(e){r.json({success:!1,error:(0,f.getErrorMessage)(e)})}})),e.use("/api/opencode",((e,t)=>this.handleOpenCodeProxy(e,t))),e}handleHealth(e,t){t.json({status:"healthy",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),projectId:this.projectId,projectPath:this.options.projectPath,timestamp:(new Date).toISOString()})}async handleAuthStatus(e,t){const r=this.options.tokenManager
|
|
339
338
|
if(r)try{const e=await r.hasValidToken(),s=r.getAuthStatus(),o=e?await r.getToken():null
|
|
340
339
|
t.json({authenticated:e,isPendingAuth:s.isPendingAuth,authUrl:e?void 0:s.authUrl,expiresAt:o?.expiresAt??null})}catch(e){this.logger.warn("auth-status check failed",{error:(0,f.getErrorMessage)(e)}),t.json({authenticated:!1,isPendingAuth:!1})}else t.json({authenticated:!0,isPendingAuth:!1})}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 r=d.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
341
340
|
try{if(!u.default.existsSync(r))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
|
|
@@ -377,7 +376,7 @@ e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3
|
|
|
377
376
|
if(!s)return void r.status(400).json({success:!1,error:"requestId is required"})
|
|
378
377
|
if(!/^req\d+$/.test(s))return void r.status(400).json({success:!1,error:"Invalid requestId format"})
|
|
379
378
|
this.logger.info("Generating journal for request",{requestId:s})
|
|
380
|
-
const o=`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.`,{resolveRunCommand:n}=await Promise.resolve().then(t.bind(t,9068)),i=n(),a=(0,
|
|
379
|
+
const o=`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.`,{resolveRunCommand:n}=await Promise.resolve().then(t.bind(t,9068)),i=n(),a=(0,O.spawn)(i.command,[...i.prependArgs,"run","--agent","journal",o],{detached:!0,stdio:"ignore",cwd:this.options.projectPath,env:{...process.env,...(0,I.getOpencodeSpawnEnv)(this.options.projectPath,(0,b.isDevMode)())}})
|
|
381
380
|
a.pid&&this.trackChildPid(a.pid),a.unref(),r.json({success:!0,message:"Journal generation started for "+s})}catch(e){this.logger.error("Failed to generate journal",{error:(0,f.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}}async handleDirectReport(e,t){try{const r=this.projectId
|
|
382
381
|
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
383
382
|
const{sessionId:s,requestId:o,summary:n,status:i}=e.body
|
|
@@ -425,7 +424,14 @@ if(!t||t===r)throw e
|
|
|
425
424
|
d=`${t}/${s}${o}`,this.logger.info("OpenCode proxy: retrying with fresh port",{oldUrl:r,newUrl:t}),l=await fetch(d,c)}if(204===l.status||"0"===l.headers.get("content-length"))return void t.status(l.status).end()
|
|
426
425
|
const u=l.headers.get("content-type")||""
|
|
427
426
|
let h
|
|
428
|
-
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(r){this.openCodePort=null,this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,f.getErrorMessage)(r),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,f.getErrorMessage)(r)})}}async
|
|
427
|
+
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(r){this.openCodePort=null,this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,f.getErrorMessage)(r),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,f.getErrorMessage)(r)})}}async handleSpawnPlanner(e,r){try{const{agent:s,planId:o,prompt:n,title:i,parentSessionId:a}=e.body||{}
|
|
428
|
+
if(!o||!n)return void r.status(400).json({success:!1,error:"planId and prompt are required"})
|
|
429
|
+
const c=new Set(["planner","auto-planner","quest-planner"])
|
|
430
|
+
if(s&&!c.has(s))return void r.status(400).json({success:!1,error:"agent must be one of: "+[...c].join(", ")})
|
|
431
|
+
if(!this.projectId)return void r.status(400).json({success:!1,error:"No project configured"})
|
|
432
|
+
const{spawnPlannerWithDoc:l}=await Promise.resolve().then(t.bind(t,8649)),d=await l({agent:s||"planner",planId:o,prompt:n,title:i||"Planner",parentSessionId:a,apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e)})
|
|
433
|
+
if(!d.success||!d.sessionId)return void r.status(500).json({success:!1,error:d.error||"spawn failed"})
|
|
434
|
+
r.json({success:!0,data:{sessionId:d.sessionId,planId:d.planId,pid:d.pid}})}catch(e){this.logger.error("Spawn planner failed",{error:(0,f.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}}async handleSpawnSession(e,t){try{const{agent:r,prompt:s,requestId:o,parentSessionId:n,title:i,specialization:a,specializationHeadline:c,allowParallel:l,callerAgentType:d}=e.body
|
|
429
435
|
if(!r||!s)return void t.status(400).json({success:!1,error:"agent and prompt are required"})
|
|
430
436
|
if(!this.projectId)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
431
437
|
const u=this.projectId,h=new k.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:u,getDefaultModel:()=>this.cachedDefaultModel||void 0}),g=await h.prepareSession({agent:r,prompt:s,projectPath:this.options.projectPath,requestId:o,parentSessionId:n,title:i,specialization:a,specializationHeadline:c,allowParallel:l,callerAgentType:d})
|
|
@@ -437,24 +443,24 @@ if(!l.success)return void r.status(500).json({success:!1,error:l.error||l.messag
|
|
|
437
443
|
r.json({success:!0,data:l.data})}catch(e){this.logger.error("Run tests now failed",{error:(0,f.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}}async handleSetupTests(e,t){try{const{requestId:r,parentSessionId:s,branchName:o}=e.body||{}
|
|
438
444
|
if(!this.projectId)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
439
445
|
const n=this.projectId,i=this.options.projectPath
|
|
440
|
-
await(0,
|
|
441
|
-
const a=await(0,
|
|
442
|
-
try{await(0,
|
|
443
|
-
return this.logger.error("Failed to create setup worktree",{branch:a,error:r}),void t.status(500).json({success:!1,error:"Failed to create setup worktree: "+r})}const u=(0,
|
|
446
|
+
await(0,j.sweepStaleSetupWorktrees)(i,this.logger)
|
|
447
|
+
const a=await(0,j.resolveSetupBranch)(this.apiClient,n,o,r,this.logger,i),c=(0,j.generateSetupId)(),l=d.default.join((0,j.getSetupWorktreesBase)(i),c)
|
|
448
|
+
try{await(0,j.createSetupWorktree)(i,l,a,this.logger)}catch(e){const r=(0,f.getErrorMessage)(e)
|
|
449
|
+
return this.logger.error("Failed to create setup worktree",{branch:a,error:r}),void t.status(500).json({success:!1,error:"Failed to create setup worktree: "+r})}const u=(0,j.buildSetupPrompt)(i,r),h=await(0,T.spawnActionAgent)({agent:"test-architect",prompt:u,cwd:l,projectPath:i,projectId:n,title:r?`[${r}] Test setup`:"Test setup",requestId:r,parentSessionId:s,allowParallel:!0,apiClient:this.apiClient,logger:this.logger,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0})
|
|
444
450
|
if(!h.success)return void t.status(500).json({success:!1,error:h.error||"Spawn failed"})
|
|
445
451
|
t.json({success:!0,data:{sessionId:h.sessionId,worktreePath:l,branch:a}})}catch(e){this.logger.error("Setup tests failed",{error:(0,f.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}}async handleSetupCommands(e,t){try{const{requestId:r,parentSessionId:s,branchName:o,context:n}=e.body||{}
|
|
446
452
|
if(!this.projectId)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
447
453
|
const i=this.projectId,a=this.options.projectPath
|
|
448
|
-
await(0,
|
|
449
|
-
const c=await(0,
|
|
450
|
-
try{await(0,
|
|
451
|
-
return this.logger.error("Failed to create setup worktree (commands)",{branch:c,error:r}),void t.status(500).json({success:!1,error:"Failed to create setup worktree: "+r})}const h=(0,
|
|
454
|
+
await(0,j.sweepStaleSetupWorktrees)(a,this.logger)
|
|
455
|
+
const c=await(0,j.resolveSetupBranch)(this.apiClient,i,o,r,this.logger,a),l=(0,j.generateSetupId)(),u=d.default.join((0,j.getSetupWorktreesBase)(a),l)
|
|
456
|
+
try{await(0,j.createSetupWorktree)(a,u,c,this.logger)}catch(e){const r=(0,f.getErrorMessage)(e)
|
|
457
|
+
return this.logger.error("Failed to create setup worktree (commands)",{branch:c,error:r}),void t.status(500).json({success:!1,error:"Failed to create setup worktree: "+r})}const h=(0,j.buildCommandSetupPrompt)(a,r,n),g=await(0,T.spawnActionAgent)({agent:"command-architect",prompt:h,cwd:u,projectPath:a,projectId:i,title:r?`[${r}] Commands setup`:"Commands setup",requestId:r,parentSessionId:s,allowParallel:!0,apiClient:this.apiClient,logger:this.logger,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0})
|
|
452
458
|
if(!g.success)return void t.status(500).json({success:!1,error:g.error||"Spawn failed"})
|
|
453
459
|
t.json({success:!0,data:{sessionId:g.sessionId,worktreePath:u,branch:c}})}catch(e){this.logger.error("Setup commands failed",{error:(0,f.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}}async handleGetAgentByName(e,t){try{const r=this.projectId
|
|
454
460
|
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
455
461
|
const{agentName:s}=e.params,o=await this.apiClient.get(`/api/projects/${r}/agents`),n=(o?.data||o||[]).find((e=>e.name===s))
|
|
456
462
|
if(!n)return void t.status(404).json({success:!1,error:`Agent '${s}' not found`})
|
|
457
|
-
t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,f.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}}readProjectConfig(){if(this.cachedProjectConfig&&Date.now()-this.cachedProjectConfig.timestamp<
|
|
463
|
+
t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,f.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}}readProjectConfig(){if(this.cachedProjectConfig&&Date.now()-this.cachedProjectConfig.timestamp<x.PROJECT_CONFIG_TTL)return this.cachedProjectConfig.config
|
|
458
464
|
const e=d.default.join(this.options.projectPath,".alchemy_lab","project.json")
|
|
459
465
|
try{if(!u.default.existsSync(e))return this.cachedProjectConfig={config:null,timestamp:Date.now()},null
|
|
460
466
|
const t=u.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
@@ -467,7 +473,7 @@ if(!o||!n)return
|
|
|
467
473
|
const i=`${o}/${n}`
|
|
468
474
|
this.updateDefaultModel(i),this.logger.info("Seeded defaultModel from OpenCode zen default",{defaultModel:i}),this.apiClient.post("/api/user/preferences",{defaultModel:i}).catch((e=>this.logger.debug("Failed to seed user preference",{error:(0,f.getErrorMessage)(e)})))}getOpenCodeUrl(){if(this.openCodePort)return"http://127.0.0.1:"+this.openCodePort
|
|
469
475
|
if(this.openCodeUrlCache&&3e4>Date.now()-this.openCodeUrlCache.timestamp)return this.openCodeUrlCache.url
|
|
470
|
-
const e=(0,
|
|
476
|
+
const e=(0,D.getOpenCodeUrlFromConfig)(this.options.projectPath)
|
|
471
477
|
return e&&(this.openCodeUrlCache={url:e,timestamp:Date.now()}),e}async handleSessionCommand(e,t){const r=this.getOpenCodeUrl()
|
|
472
478
|
if(!r)return{success:!1,error:"OpenCode server not available. Is 'opencode serve' running?"}
|
|
473
479
|
try{switch(e){case"list-sessions":{const[e,s]=await Promise.all([fetch(r+"/session"),fetch(r+"/session/status")])
|
|
@@ -502,17 +508,16 @@ r?.agentType&&(a=r.agentType)}catch{}return{success:!0,data:{id:e,title:i||"(unt
|
|
|
502
508
|
if(!e?.id)throw Error("No project configuration found")
|
|
503
509
|
this.projectId=e.id,this.hookExecutor.setProjectId(this.projectId),this.timerHookScheduler?.setProjectId(this.projectId)
|
|
504
510
|
try{const e=d.default.resolve(__dirname,"../../package.json"),t=JSON.parse(u.default.readFileSync(e,"utf8"))
|
|
505
|
-
this.clientVersion=t.version}catch{}try{const e=
|
|
506
|
-
this.opencodeVersion=e.version}catch{try{const e=process.env.ALCHEMY_OPENCODE_DIR
|
|
511
|
+
this.clientVersion=t.version}catch{}try{const e=(0,_.findOpencodeDir)()
|
|
507
512
|
if(e){const t=JSON.parse(u.default.readFileSync(d.default.join(e,"package.json"),"utf8"))
|
|
508
|
-
this.opencodeVersion=t.version}}catch{}
|
|
513
|
+
this.opencodeVersion=t.version}}catch{}const t={projectId:this.projectId,projectPath:this.options.projectPath,serverPort:this.port,hostname:h.default.hostname(),platform:process.platform,clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}
|
|
509
514
|
this.logger.info("Registering with remote",{projectId:this.projectId})
|
|
510
|
-
const
|
|
511
|
-
if(
|
|
512
|
-
const e=
|
|
515
|
+
const r=await this.apiClient.post("/api/servers/register",t)
|
|
516
|
+
if(r.success&&r.data?.serverId){this.serverId=r.data.serverId,this.logger.info("Registered successfully",{serverId:this.serverId})
|
|
517
|
+
const e=r.data.versionWarnings
|
|
513
518
|
if(e&&e.length>0){const t=e.some((e=>"required"===e.severity))
|
|
514
519
|
for(const t of e)"required"===t.severity?(this.logger.error("[UPDATE REQUIRED] "+t.message),console.error("\n\x1b[31m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 UPDATE REQUIRED \u2551"),console.error("\x1b[31m\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\x1b[0m"),console.error(`\x1b[31m${t.message}\x1b[0m\n`)):(this.logger.warn("[UPDATE RECOMMENDED] "+t.message),console.warn(`\n\x1b[33m[UPDATE RECOMMENDED] ${t.message}\x1b[0m\n`))
|
|
515
|
-
this.versionWarnings=e,t&&(this._pendingUpdateBlock=!0)}}else this.logger.warn("Registration failed, continuing without remote connection",{error:
|
|
520
|
+
this.versionWarnings=e,t&&(this._pendingUpdateBlock=!0)}}else this.logger.warn("Registration failed, continuing without remote connection",{error:r.error?.message||"Unknown error"})}async applyVersionWarnings(e){if(e.map((e=>`${e.component}:${e.severity}`)).join(",")!==this.versionWarnings.map((e=>`${e.component}:${e.severity}`)).join(",")){for(const t of e)"required"===t.severity?(this.logger.error("[UPDATE REQUIRED] "+t.message),console.error(`\n\x1b[31m[UPDATE REQUIRED] ${t.message}\x1b[0m\n`)):(this.logger.warn("[UPDATE RECOMMENDED] "+t.message),console.warn(`\n\x1b[33m[UPDATE RECOMMENDED] ${t.message}\x1b[0m\n`))
|
|
516
521
|
this.versionWarnings=e,!e.some((e=>"required"===e.severity))&&!this._pendingUpdateBlock||this.updateBlocked||await this.enterUpdateMode()}}startHeartbeat(){this.serverId&&(setInterval((()=>{if(this.projectId)try{(0,a.touchHeartbeat)(this.projectId)}catch{}}),3e4),this.heartbeatInterval=setInterval((async()=>{try{const e=await this.apiClient.post(`/api/servers/${this.serverId}/heartbeat`,{timestamp:(new Date).toISOString(),clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}),t=e?.data?.versionWarnings??[]
|
|
517
522
|
await this.applyVersionWarnings(t)}catch(e){this.logger.warn("Heartbeat failed",{error:(0,f.getErrorMessage)(e)})}}),3e5))}startSSEEventForwarder(){this.logger.info("Starting SSE event forwarder"),this.connectToOpenCodeSSE(),this.sseForwarderInterval=setInterval((async()=>{await this.flushSSEEvents()}),2e3)}stopSSEEventForwarder(){this.sseForwarderInterval&&(clearInterval(this.sseForwarderInterval),this.sseForwarderInterval=void 0),this.sseAbortController&&(this.sseAbortController.abort(),this.sseAbortController=void 0),this.flushSSEEvents().catch((e=>this.logger.warn("Failed to flush remaining SSE events",{error:(0,f.getErrorMessage)(e)})))}async connectToOpenCodeSSE(){const e=this.getOpenCodeUrl()
|
|
518
523
|
if(!e)return this.logger.debug("SSE forwarder: OpenCode URL unavailable, will retry"),void setTimeout((()=>this.connectToOpenCodeSSE()),5e3)
|
|
@@ -599,7 +604,7 @@ if(s.ok){const e=await s.json(),t=e?.data?.settings||e?.settings
|
|
|
599
604
|
void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel}))}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,f.getErrorMessage)(e)})}this.startPolling(),this.projectId&&this.startSSEEventForwarder(),this.projectId&&this.startStuckSessionWatchdog(),this.projectId&&this.startTimerHookScheduler(),this.projectId&&this.startQuestTickScheduler(),this.startMcpStaleCleanup()}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,f.getErrorMessage)(t)})}}this.trackedChildPids.clear()
|
|
600
605
|
try{const e=d.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
601
606
|
if(u.default.existsSync(e)){const t=u.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
602
|
-
if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,f.getErrorMessage)(e)})}}}}catch{}}startTimerHookScheduler(){this.timerHookScheduler=new
|
|
607
|
+
if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,f.getErrorMessage)(e)})}}}}catch{}}startTimerHookScheduler(){this.timerHookScheduler=new S.TimerHookScheduler({hookExecutor:this.hookExecutor,apiClient:this.apiClient,logger:this.logger,getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath}),this.timerHookScheduler.start()}startQuestTickScheduler(){this.questTickScheduler=new C.QuestTickScheduler({apiClient:this.apiClient,logger:this.logger,getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath}),this.questTickScheduler.start()}startStuckSessionWatchdog(){this.stuckSessionWatchdog=new E.StuckSessionWatchdog({getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,logger:this.logger,onStuckDetected:e=>this.handleStuckSessionDetected(e),onStuckResolved:e=>this.handleStuckSessionResolved(e)}),this.stuckSessionWatchdog.start()}handleStuckSessionDetected(e){this.sseEventBuffer.push({type:"session.stuck",data:{type:"session.stuck",properties:{sessionId:e.sessionId,messageId:e.messageId,detectedAt:e.detectedAt,sessionTitle:e.sessionTitle,retryCount:e.retryCount}}}),this.logger.warn("Stuck session detected",{sessionId:e.sessionId,title:e.sessionTitle})}handleStuckSessionResolved(e){this.sseEventBuffer.push({type:"session.unstuck",data:{type:"session.unstuck",properties:{sessionId:e}}})}handleGetStuckSessions(e,t){const r=this.stuckSessionWatchdog?.getStuckSessions()??[]
|
|
603
608
|
t.json({success:!0,data:r})}handleTestInjectStuckSession(e,t){if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"Watchdog not running"})
|
|
604
609
|
const{sessionId:r,title:s}=e.body||{},o=this.stuckSessionWatchdog.injectTestStuck(r,s)
|
|
605
610
|
this.logger.info("Test-injected stuck session",{sessionId:o.sessionId}),t.json({success:!0,data:o})}async handleRetryStuckSession(e,t){const{sessionId:r}=e.params
|
|
@@ -613,7 +618,7 @@ for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortCo
|
|
|
613
618
|
try{const e=d.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
|
|
614
619
|
u.default.existsSync(e)&&u.default.unlinkSync(e)}catch{}if(this.projectId)try{const{removeEntry:e}=await Promise.resolve().then(t.bind(t,2640))
|
|
615
620
|
e(this.projectId)}catch{}if(this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval),this.heartbeatInterval&&clearInterval(this.heartbeatInterval),this.serverId)try{await this.apiClient.post(`/api/servers/${this.serverId}/disconnect`)}catch{}this.cliBridge.close(),await new Promise((e=>{if(this.server){const t=setTimeout((()=>{this.logger.warn("Graceful shutdown timed out after 10s, forcing close"),e()}),1e4)
|
|
616
|
-
this.server.close((()=>{clearTimeout(t),e()}))}else e()})),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=
|
|
621
|
+
this.server.close((()=>{clearTimeout(t),e()}))}else e()})),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=x,x.PROJECT_CONFIG_TTL=3e4},2424(e,exports,t){"use strict"
|
|
617
622
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.ensureRequestWorktree=async function(e,t,i){const a="requests/"+t,c=(0,s.join)(e,n.ALCHEMY_DIR_NAME,"worktrees"),l=(0,s.join)(c,t)
|
|
618
623
|
if((0,r.existsSync)(l))return void i.info("Worktree already exists on disk, skipping pre-spawn creation",{requestId:t,worktreePath:l})
|
|
619
624
|
const d=(0,o.simpleGit)(e);(0,r.mkdirSync)(c,{recursive:!0})
|
|
@@ -718,7 +723,7 @@ break
|
|
|
718
723
|
case"parent_session":case"ancestor_chain":await(0,o.executeInjectPrompt)(e,e.promptTemplate,i,a,this.ctx)}else await(0,o.executeBackground)(e,t,i,a.requestId,this.ctx)
|
|
719
724
|
const c=(0,s.determineEventToEmit)(e,a)
|
|
720
725
|
c&&await this.processCustomEvent(c,a,r+1)}}async notifyHookTrigger(e){try{await this.ctx.apiClient.post(`/api/projects/${this.ctx.projectId}/hook-triggers`,{hookId:e.id,hookName:e.name,eventType:e.eventType,action:e.action,target:e.target,agent:e.agent})}catch{}}}},3416(e,exports){"use strict"
|
|
721
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.KIND_META=exports.QUEST_STATUS_CLOSED=exports.QUEST_STATUS_OPEN=exports.QUEST_DEFAULTS=exports.PLAN_KINDS=void 0,exports.PLAN_KINDS=["plan","quest"],exports.QUEST_DEFAULTS={tickIntervalSeconds:1800,maxInFlight:1,maxPerDay:24,minIterations:3},exports.QUEST_STATUS_OPEN="open",exports.QUEST_STATUS_CLOSED="closed",exports.KIND_META={plan:{docType:"Plan",filenamePrefix:"PLAN-",label:"plan",idLabel:"Plan ID",headingCreated:"Plan Created",errorVerb:"create plan",alchemyKind:"plan-create",canBeActivePlan:!0},quest:{docType:"Quest",filenamePrefix:"QUEST-",label:"quest",idLabel:"Quest ID",headingCreated:"Quest Created",errorVerb:"create quest",alchemyKind:"quest-create",canBeActivePlan:!1}}},3444(e,exports,t){"use strict"
|
|
726
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.KIND_META=exports.QUEST_STATUS_DRAFT=exports.QUEST_STATUS_CLOSED=exports.QUEST_STATUS_OPEN=exports.QUEST_DEFAULTS=exports.PLAN_KINDS=void 0,exports.PLAN_KINDS=["plan","quest"],exports.QUEST_DEFAULTS={tickIntervalSeconds:1800,maxInFlight:1,maxPerDay:24,minIterations:3},exports.QUEST_STATUS_OPEN="open",exports.QUEST_STATUS_CLOSED="closed",exports.QUEST_STATUS_DRAFT="draft",exports.KIND_META={plan:{docType:"Plan",filenamePrefix:"PLAN-",label:"plan",idLabel:"Plan ID",headingCreated:"Plan Created",errorVerb:"create plan",alchemyKind:"plan-create",canBeActivePlan:!0},quest:{docType:"Quest",filenamePrefix:"QUEST-",label:"quest",idLabel:"Quest ID",headingCreated:"Quest Created",errorVerb:"create quest",alchemyKind:"quest-create",canBeActivePlan:!1}}},3444(e,exports,t){"use strict"
|
|
722
727
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
723
728
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.LogLevel=exports.defaultLogger=exports.isDebugToolsEnabled=exports.getDebugLevelFromEnv=exports.isDebugLoggingEnabled=exports.createEnvLogger=exports.createLogger=void 0
|
|
724
729
|
const s=t(2782)
|
|
@@ -877,7 +882,7 @@ if(w){try{const e=await fetch(`${y}/session/${w}/prompt_async`,{method:"POST",he
|
|
|
877
882
|
return{model:{providerID:e,modelID:r.join("/")}}})():{}}),signal:AbortSignal.timeout(1e4)})
|
|
878
883
|
if(!e.ok)return{success:!1,message:"prompt_async failed: "+e.status,error:"prompt_failed"}}catch(e){return{success:!1,message:"Failed to inject prompt: "+(e instanceof Error?e.message:e+""),error:"prompt_failed"}}n.info("Injected prompt into existing session",{sessionId:w})}else{const e=await(0,o.spawnActionAgent)({agent:p,prompt:f,cwd:i,projectPath:i,projectId:a,title:m||"prompt-and-wait",apiClient:c,logger:n,trackChildPid:l,getDefaultModel(){return t.cachedDefaultModel||void 0}})
|
|
879
884
|
if(!e.success||!e.sessionId)return{success:!1,message:"Failed to spawn session: "+e.error,error:"spawn_failed"}
|
|
880
|
-
w=e.sessionId,n.info("Spawned session for prompt-and-wait",{sessionId:w})}const
|
|
885
|
+
w=e.sessionId,n.info("Spawned session for prompt-and-wait",{sessionId:w})}const I=await async function(e,t,o){const n=Date.now()+1e3*o
|
|
881
886
|
let i=!1
|
|
882
887
|
const a=Date.now()+15e3
|
|
883
888
|
for(;Date.now()<a&&Date.now()<n;){const o=await r(e,t)
|
|
@@ -885,10 +890,10 @@ if("busy"===o||"running"===o){i=!0
|
|
|
885
890
|
break}await s(500)}for(;Date.now()<n;){const o=await r(e,t)
|
|
886
891
|
if("error"===o)return{status:"error"}
|
|
887
892
|
if(("idle"===o||"completed"===o)&&i)return{status:"completed"}
|
|
888
|
-
"busy"!==o&&"running"!==o||(i=!0),await s(2e3)}return{status:"timeout"}}(y,w,g),
|
|
889
|
-
return r.ok?(await r.json()).map((e=>({role:e.info?.role||"unknown",text:(e.parts||[]).filter((e=>"text"===e.type)).map((e=>e.text)).join("\n"),toolCalls:(e.parts||[]).filter((e=>"tool_use"===e.type)).map((e=>({name:e.name,id:e.id}))),timestamp:e.info?.time?.created}))):[]}catch{return[]}}(y,w),
|
|
893
|
+
"busy"!==o&&"running"!==o||(i=!0),await s(2e3)}return{status:"timeout"}}(y,w,g),b=Date.now()-v,E=await async function(e,t){try{const r=await fetch(`${e}/session/${t}/message?limit=200`,{signal:AbortSignal.timeout(1e4)})
|
|
894
|
+
return r.ok?(await r.json()).map((e=>({role:e.info?.role||"unknown",text:(e.parts||[]).filter((e=>"text"===e.type)).map((e=>e.text)).join("\n"),toolCalls:(e.parts||[]).filter((e=>"tool_use"===e.type)).map((e=>({name:e.name,id:e.id}))),timestamp:e.info?.time?.created}))):[]}catch{return[]}}(y,w),S=function(e){return e.reduce(((e,t)=>e+(t.toolCalls?.length||0)),0)}(E),C=function(e){for(let t=e.length-1;t>=0;t--)if("assistant"===e[t].role&&e[t].text)return e[t].text
|
|
890
895
|
return""}(E)
|
|
891
|
-
return{success:!0,message:"Prompt completed",data:{sessionId:w,status:
|
|
896
|
+
return{success:!0,message:"Prompt completed",data:{sessionId:w,status:I.status,messages:E,toolCallCount:S,elapsedMs:b,finalResponse:C}}}
|
|
892
897
|
const o=t(8148)},4769(e,exports,t){"use strict"
|
|
893
898
|
function r(e){return(0,o.getOpenCodeServerUrl)(e.projectPath,e.logger)}async function s(e,t,s,o){const n=r(s)
|
|
894
899
|
if(!n||!t)return!1
|
|
@@ -1021,31 +1026,31 @@ if(!d)return{success:!1,message:"requestId is required",error:"Missing requestId
|
|
|
1021
1026
|
m.info("Starting run-tests action",{requestId:d,testProcessIds:u})
|
|
1022
1027
|
try{const l=await f.get(`/api/projects/${y}/requests/${d}`)
|
|
1023
1028
|
if(!l.success||!l.data)return{success:!1,message:`Request ${d} not found`,error:"Request not found"}
|
|
1024
|
-
const g=l.data,
|
|
1025
|
-
let
|
|
1026
|
-
if(u?.length)
|
|
1029
|
+
const g=l.data,I=g.branchName||"requests/"+d
|
|
1030
|
+
let b
|
|
1031
|
+
if(u?.length)b=(await Promise.all(u.map((e=>f.get(`/api/projects/${y}/test-processes/${e}`))))).filter((e=>e.success&&e.data)).map((e=>e.data))
|
|
1027
1032
|
else{const e=await f.get(`/api/projects/${y}/test-processes/active`)
|
|
1028
|
-
|
|
1029
|
-
const E=Math.max(1,Math.min(8,
|
|
1030
|
-
let
|
|
1031
|
-
try{
|
|
1033
|
+
b=e.success&&e.data?Array.isArray(e.data)?e.data:[e.data]:[]}if(0===b.length)return{success:!1,message:"No test processes configured for this project. Set up testing first.",error:"No test processes"}
|
|
1034
|
+
const E=Math.max(1,Math.min(8,b[0]?.config?.poolSize??2)),S=b[0]?.config?.fingerprint||[]
|
|
1035
|
+
let C
|
|
1036
|
+
try{C=await(0,c.acquireSlot)(v,E)}catch(e){return{success:!1,message:"Could not acquire test worktree slot: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}}const k=C.path,T=(0,n.simpleGit)(v)
|
|
1032
1037
|
try{if((0,s.existsSync)(k))try{const e=(0,n.simpleGit)(k)
|
|
1033
|
-
await e.raw(["reset","--hard",
|
|
1038
|
+
await e.raw(["reset","--hard",I]),m.info("Reset pool slot to latest branch HEAD",{worktreePath:k,branchName:I,slotIndex:C.index})}catch(e){m.warn("Could not reset slot, recreating",{error:(0,i.getErrorMessage)(e)})
|
|
1034
1039
|
try{await T.raw(["worktree","remove","--force",k])}catch{}}if(!(0,s.existsSync)(k)){(0,s.mkdirSync)((0,o.join)(v,".alchemy_lab","test-worktrees"),{recursive:!0})
|
|
1035
|
-
try{if(!(await T.branch()).all.includes(
|
|
1036
|
-
await T.raw(["worktree","add","--detach",k,
|
|
1037
|
-
m.info("Pool slot worktree already exists, using as-is",{worktreePath:k})}}}catch(e){throw(0,c.releaseSlot)(k),e}const _=await(0,c.computeFingerprint)(k,
|
|
1038
|
-
j||m.info("Pool slot fingerprint matches; skipping setup",{worktreePath:k,slotIndex:
|
|
1040
|
+
try{if(!(await T.branch()).all.includes(I))return(0,c.releaseSlot)(k),{success:!1,message:`Branch ${I} not found`,error:"Branch not found"}
|
|
1041
|
+
await T.raw(["worktree","add","--detach",k,I]),m.info("Created pool slot worktree (detached)",{worktreePath:k,branchName:I,slotIndex:C.index})}catch(e){if(!(e+"").includes("already exists"))return(0,c.releaseSlot)(k),{success:!1,message:"Failed to create pool slot worktree: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}
|
|
1042
|
+
m.info("Pool slot worktree already exists, using as-is",{worktreePath:k})}}}catch(e){throw(0,c.releaseSlot)(k),e}const _=await(0,c.computeFingerprint)(k,S),j=0===S.length||_!==C.storedFingerprint,A=0===S.length?null:_===C.storedFingerprint
|
|
1043
|
+
j||m.info("Pool slot fingerprint matches; skipping setup",{worktreePath:k,slotIndex:C.index,fingerprintFiles:S})
|
|
1039
1044
|
const{execSync:P}=await Promise.resolve().then(t.t.bind(t,5317,23))
|
|
1040
|
-
if(j){for(const e of
|
|
1041
|
-
if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length,slotIndex:
|
|
1042
|
-
for(const r of t)try{m.info("Setup command",{command:r,worktreePath:k}),P(r,{cwd:k,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:r,processName:e.name,exitCode:t.status,stderr:(t.stderr||"").slice(-500)})}}}
|
|
1043
|
-
e>0&&m.info("Pruned drift from warm slot",{worktreePath:k,slotIndex:
|
|
1045
|
+
if(j){for(const e of b){const t=e.config?.setup||[]
|
|
1046
|
+
if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length,slotIndex:C.index})
|
|
1047
|
+
for(const r of t)try{m.info("Setup command",{command:r,worktreePath:k}),P(r,{cwd:k,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:r,processName:e.name,exitCode:t.status,stderr:(t.stderr||"").slice(-500)})}}}S.length>0&&await(0,c.writeSlotFingerprint)(k,_),await(0,c.writeWarmSnapshot)(k)}else{const{removed:e}=await(0,c.restoreToWarmSnapshot)(k)
|
|
1048
|
+
e>0&&m.info("Pruned drift from warm slot",{worktreePath:k,slotIndex:C.index,removed:e})}let M
|
|
1044
1049
|
try{const e=(0,n.simpleGit)(k)
|
|
1045
1050
|
M=(await e.revparse(["HEAD"])).trim()}catch(e){m.warn("Failed to capture worktree HEAD; commit_sha will be NULL on this run",{worktreePath:k,error:(0,i.getErrorMessage)(e)})}const O=[],R=[],D=[]
|
|
1046
1051
|
let x
|
|
1047
1052
|
const $={worktreePath:k,commitSha:M,fingerprintHit:A}
|
|
1048
|
-
for(const process of
|
|
1053
|
+
for(const process of b){let t
|
|
1049
1054
|
const s=e.testRunIds?.[O.length]
|
|
1050
1055
|
try{s?(await f.patch(`/api/projects/${y}/test-runs/${s}`,$),t=(await f.get(`/api/projects/${y}/test-runs/${s}`)).data,O.push(t.id)):(t=(await f.post(`/api/projects/${y}/test-runs`,{requestId:d,testProcessId:process.id,parentSessionId:h,...$})).data,O.push(t.id))}catch(e){m.error("Failed to get/create test run record",{requestId:d,processId:process.id,preCreatedId:s,error:(0,i.getErrorMessage)(e)})
|
|
1051
1056
|
continue}const o=r(g,process,0,y,p,e.commandNames),n=p?"tester":"auto-tester"
|
|
@@ -1053,7 +1058,7 @@ try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:o,cwd:k,projectPath:v,pr
|
|
|
1053
1058
|
if(!e.success)throw Error(e.error||"Spawn failed")
|
|
1054
1059
|
R.push(t.id),e.sessionId&&D.push(e.sessionId),e.pid&&(x=e.pid),e.sessionId&&(f.patch(`/api/projects/${y}/test-runs/${t.id}`,{testerSessionId:e.sessionId}).catch((e=>m.warn("Failed to set testerSessionId on test run",{error:(0,i.getErrorMessage)(e)}))),f.post(`/api/projects/${y}/session-test-process-associations`,{opencodeSessionId:e.sessionId,testProcessId:process.id}).catch((t=>m.warn("Failed to create session\u2194test-process association",{sessionId:e.sessionId,testProcessId:process.id,error:(0,i.getErrorMessage)(t)})))),m.info("Spawned test-runner agent",{testRunId:t.id,processName:process.name,worktreePath:k,pid:e.pid,sessionId:e.sessionId})}catch(e){m.error("Failed to spawn test-runner agent",{testRunId:t.id,error:(0,i.getErrorMessage)(e)})
|
|
1055
1060
|
try{await f.patch(`/api/projects/${y}/test-runs/${t.id}`,{status:"error",results:{steps:[],summary:"Failed to spawn test agent: "+(0,i.getErrorMessage)(e)}})}catch{}}}if(0===R.length)return(0,c.releaseSlot)(k),{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}
|
|
1056
|
-
for(const e of D);return(0,c.watchPidAndRelease)(x,k,12e5),{success:!0,message:`Started ${R.length} test run(s) for request ${d}`,data:{testRunIds:O,agentsSpawned:R.length,processCount:
|
|
1061
|
+
for(const e of D);return(0,c.watchPidAndRelease)(x,k,12e5),{success:!0,message:`Started ${R.length} test run(s) for request ${d}`,data:{testRunIds:O,agentsSpawned:R.length,processCount:b.length,worktreePath:k,spawnedSessionIds:D}}}catch(e){const t=(0,i.getErrorMessage)(e)
|
|
1057
1062
|
return m.error("run-tests action failed",{requestId:d,error:t}),{success:!1,message:"Failed to run tests: "+t,error:t}}}
|
|
1058
1063
|
const s=t(9896),o=t(6928),n=t(3807),i=t(8824),a=t(8148),c=t(489)},6563(e,exports,t){"use strict"
|
|
1059
1064
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.TimerHookScheduler=void 0
|
|
@@ -1139,15 +1144,20 @@ const o=s(t(9278))},6895(module){"use strict"
|
|
|
1139
1144
|
module.exports=require("chalk")},6928(module){"use strict"
|
|
1140
1145
|
module.exports=require("path")},6982(module){"use strict"
|
|
1141
1146
|
module.exports=require("crypto")},7078(e,exports,t){"use strict"
|
|
1142
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleSpawnAutoPlannerAction=async function(e,t){const{requestId:o,parentSessionId:
|
|
1143
|
-
if(!o)return{success:!1,message:"requestId is required",error:"Missing
|
|
1144
|
-
|
|
1145
|
-
try{
|
|
1146
|
-
if(
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
const
|
|
1147
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleSpawnAutoPlannerAction=async function(e,t){const{requestId:o,prompt:n,title:i,parentSessionId:a,originMessageCode:c}=e,{logger:l,apiClient:d,projectId:u,projectPath:h,trackChildPid:g}=t
|
|
1148
|
+
if(!o&&!n)return{success:!1,message:"Either requestId or prompt is required",error:"Missing payload"}
|
|
1149
|
+
l.info("Starting spawn-auto-planner action",{requestId:o,hasPrompt:!!n})
|
|
1150
|
+
try{let e,t,p
|
|
1151
|
+
if(o){const r=await d.get(`/api/projects/${u}/requests/${o}`)
|
|
1152
|
+
if(!r.success||!r.data)return{success:!1,message:`Request ${o} not found`,error:"Request not found"}
|
|
1153
|
+
const s=r.data,n=Array.isArray(s.acceptanceCriteria)?s.acceptanceCriteria.map((e=>e.description||e)).join("\n- "):""
|
|
1154
|
+
e=`Create a plan for request ${o}: ${s.title}\n\nDescription: ${s.description||"No description provided."}\n\n${n?"Acceptance Criteria:\n- "+n:""}\n\nResearch the codebase and create a comprehensive implementation plan.`,t=`[${o}] Plan: ${s.title}`,p=s.title||"New Plan for "+o}else e=n,t=i||"Auto-planner",p=i||"Auto-planner Draft"
|
|
1155
|
+
const m=await(0,s.spawnPlannerWithDoc)({agent:"auto-planner",planName:p,prompt:e,title:t,requestId:o,parentSessionId:a,apiClient:d,logger:l,projectId:u,projectPath:h,trackChildPid:g})
|
|
1156
|
+
if(!m.success||!m.sessionId)return{success:!1,message:"Failed to spawn auto-planner: "+m.error,error:m.error||"Spawn failed"}
|
|
1157
|
+
const f=m.planId
|
|
1158
|
+
return c&&d.patch(`/api/projects/${u}/session-messages/${c}/response`,{partial:{plannerSessionId:m.sessionId,planId:f}}).catch((e=>l.warn("Failed to record plannerSessionId on origin message",{originMessageCode:c,error:(0,r.getErrorMessage)(e)}))),{success:!0,message:o?"Auto-planner spawned for request "+o:"Auto-planner spawned: "+(i||m.sessionId),data:{sessionId:m.sessionId,pid:m.pid}}}catch(e){const t=(0,r.getErrorMessage)(e)
|
|
1159
|
+
return l.error("spawn-auto-planner action failed",{requestId:o,error:t}),{success:!1,message:"Failed to spawn auto-planner: "+t,error:t}}}
|
|
1160
|
+
const r=t(8824),s=t(8649)},7163(e,exports){"use strict"
|
|
1151
1161
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.debugTokenAccess=function(){},exports.compareTokens=function(){},exports.debugTokenStorage=function(){},exports.disableTokenDebugging=function(){},exports.enableTokenDebugging=function(){},new WeakMap},7210(e,exports,t){"use strict"
|
|
1152
1162
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.tryMarkMessageProcessed=exports.createToolCallHash=exports.isDuplicateToolCall=exports.determineEventToEmit=exports.interpolatePrompt=exports.evaluateCondition=exports.TimerConditionEvaluator=exports.TimerHookScheduler=exports.HookCache=exports.HookExecutor=void 0
|
|
1153
1163
|
var r=t(3077)
|
|
@@ -1163,7 +1173,7 @@ Object.defineProperty(exports,"evaluateCondition",{enumerable:!0,get(){return i.
|
|
|
1163
1173
|
function t(e){if("string"!=typeof e)return e
|
|
1164
1174
|
try{return JSON.parse(e)}catch{return e}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseToolEvents=function(e){const{toolName:o,sessionId:n,input:i,output:a,timing:c}=e
|
|
1165
1175
|
if("before"===c)return[{eventType:"tool_executed",timing:"before",variables:{tool:o,toolName:o,input:JSON.stringify(i??{}).substring(0,1e3),sessionId:n||""}}]
|
|
1166
|
-
const l=[],d=a||"",u={output:d,toolName:o,sessionId:n||""},h=o.includes("request_operations")||o.includes("complete_request"),g=o.includes("task_operations")||o.includes("complete_task"),p=o.includes("write_file"),m=o.includes("replace_in_files"),f=o.includes("quick_write")||o.includes("quick_replace")||o.includes("quick_manage"),y=o.includes("read_files"),v=o.includes("search"),w=o.includes("manage_files"),
|
|
1176
|
+
const l=[],d=a||"",u={output:d,toolName:o,sessionId:n||""},h=o.includes("request_operations")||o.includes("complete_request"),g=o.includes("task_operations")||o.includes("complete_task"),p=o.includes("write_file"),m=o.includes("replace_in_files"),f=o.includes("quick_write")||o.includes("quick_replace")||o.includes("quick_manage"),y=o.includes("read_files"),v=o.includes("search"),w=o.includes("manage_files"),I=o.includes("document_operations"),b=o.includes("query_history")
|
|
1167
1177
|
if(h&&d){const e=i?.operation,t=function(e){for(const t of r){const r=e.match(t)
|
|
1168
1178
|
if(r)return r[1]}return null}(d)
|
|
1169
1179
|
t&&("merge"===e||"finalize"===e?l.push({eventType:"request_completed",timing:"after",variables:{...u,requestId:t}}):"complete"===e&&l.push({eventType:"request_ready_for_merge",timing:"after",variables:{...u,requestId:t}}))}else if(g&&d){const e=function(e){const t=e.match(s)
|
|
@@ -1174,7 +1184,7 @@ l.push({eventType:"replace_in_files",timing:"after",variables:{...u,paths:r.join
|
|
|
1174
1184
|
t&&(e.requestId=t[1]),l.push({eventType:"quick_edit",timing:"after",variables:e}),e.requestId&&l.push({eventType:"request_completed",timing:"after",variables:{...u,requestId:e.requestId}})}else if(y){const e=t(i?.paths)||[],r=(Array.isArray(e)?e:[]).map((e=>e.path||e.documentId)).filter(Boolean)
|
|
1175
1185
|
l.push({eventType:"read_files",timing:"after",variables:{...u,paths:r.join(", "),fileCount:r.length+""}})}else if(v)l.push({eventType:"search",timing:"after",variables:{...u,query:i?.needle||"",directory:i?.directory||"",fileExtension:i?.options?.fileExtension||"*"}})
|
|
1176
1186
|
else if(w){const e=t(i?.operations)||[],r=(Array.isArray(e)?e:[]).map((e=>e.operationType)).filter(Boolean)
|
|
1177
|
-
l.push({eventType:"manage_files",timing:"after",variables:{...u,operationTypes:[...new Set(r)].join(", ")}})}else
|
|
1187
|
+
l.push({eventType:"manage_files",timing:"after",variables:{...u,operationTypes:[...new Set(r)].join(", ")}})}else I?l.push({eventType:"document_operations",timing:"after",variables:u}):b&&l.push({eventType:"query_history",timing:"after",variables:u})
|
|
1178
1188
|
return l.push({eventType:"tool_executed",timing:"after",variables:{tool:o,toolName:o,output:d,sessionId:n||""}}),l}
|
|
1179
1189
|
const r=[/Request completed:\s*(req\d+)/i,/Request (req\d+) completed/i,/completed.*?(req\d{6})/i,/Request finalized:\s*(req\d+)/i,/Request (req\d+) finalized/i,/finalized.*?(req\d{6})/i,/Request (req\d+) merged/i,/merged.*?(req\d{6})/i],s=/Task\s+(tas\d+).*(?:completed|marked as completed)/i},7252(module){"use strict"
|
|
1180
1190
|
module.exports=require("express")},7388(e,exports,t){"use strict"
|
|
@@ -1378,15 +1388,15 @@ let y
|
|
|
1378
1388
|
if(p&&(o.ALCHEMY_INITIAL_BRANCH=p),r(u,"Spawn env setup",{agent:t,initialBranch:p||"none",sessionId:h||"new",attachMode:!!m}),y=m&&h&&d?["run","--attach",m,"--session",h,"--agent",t,d]:h&&d?["run","--session",h,"--agent",t,d]:d?["run","--agent",t,d]:["run","--agent",t],g){const e=y.findIndex((e=>e===d))
|
|
1379
1389
|
e>0?y.splice(e,0,"--model",g):y.push("--model",g)}r(u,"Spawning opencode",{args:y.map((e=>e===d?`[prompt:${e.length}chars]`:e)),model:g||"not specified",promptLength:d?.length,promptPreview:d?.slice(0,100),cwd:u,hasSessionId:!!h})
|
|
1380
1390
|
const v=(0,i.join)(u||process.cwd(),".alchemy_lab"),w=(0,i.join)(v,"spawn-stderr.log")
|
|
1381
|
-
let
|
|
1382
|
-
try{
|
|
1391
|
+
let I
|
|
1392
|
+
try{I=(0,n.openSync)(w,"a")}catch{}const b=(0,a.resolveRunCommand)(),E=(0,s.spawn)(b.command,[...b.prependArgs,...y],{detached:!0,stdio:["ignore","ignore",I??"ignore"],cwd:u||process.cwd(),env:o})
|
|
1383
1393
|
return E.unref(),f&&f(E.pid),{success:!0,pid:E.pid}}catch(e){return{success:!1,error:(0,o.getErrorMessage)(e)}}}
|
|
1384
1394
|
const s=t(5317),o=t(8824),n=t(9896),i=t(6928),a=t(9068),c=t(5419),l=t(3962)},8148(e,exports,t){"use strict"
|
|
1385
1395
|
function r(e){return(0,n.getOpenCodeUrlFromConfig)(e)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getOpenCodeServerUrl=r,exports.spawnActionAgent=async function(e){const{agent:t,prompt:n,cwd:a,projectPath:c,title:l,requestId:d,parentSessionId:u,allowParallel:h,apiClient:g,logger:p,trackChildPid:m}=e
|
|
1386
1396
|
let f,y,v,w=null
|
|
1387
1397
|
try{const r=new i.SessionSpawnService({apiClient:g,logger:p,projectId:e.projectId,getDefaultModel:e.getDefaultModel||(()=>{})}),s=await r.prepareSession({agent:t,prompt:n,projectPath:c,requestId:d,parentSessionId:u,title:l,allowParallel:h})
|
|
1388
|
-
w=s.sessionId,f=s.model,y=s.serverUrl,v=s.initialBranch}catch(e){p.warn("Session preparation failed \u2014 spawning without pre-created session",{error:(0,o.getErrorMessage)(e)}),y=r(c)||void 0}const
|
|
1389
|
-
return
|
|
1398
|
+
w=s.sessionId,f=s.model,y=s.serverUrl,v=s.initialBranch}catch(e){p.warn("Session preparation failed \u2014 spawning without pre-created session",{error:(0,o.getErrorMessage)(e)}),y=r(c)||void 0}const I=(0,s.spawnDetachedSubagent)({agent:t,prompt:n,cwd:a,sessionId:w||void 0,serverUrl:y,model:f,initialBranch:v,allowParallel:h??!1,onSpawn(e){e&&m&&m(e)}})
|
|
1399
|
+
return I.success?(p.info("Spawned action agent",{agent:t,sessionId:w,pid:I.pid,requestId:d}),{success:!0,sessionId:w,pid:I.pid}):{success:!1,sessionId:w,error:I.error||"Spawn failed"}}
|
|
1390
1400
|
const s=t(8035),o=t(8824),n=t(833),i=t(1292)},8463(e,exports,t){"use strict"
|
|
1391
1401
|
function r(e){return(0,o.join)(e,c,l)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getSetupWorktreesBase=r,exports.sweepStaleSetupWorktrees=async function(e,t){const i=r(e)
|
|
1392
1402
|
if(!(0,s.existsSync)(i))return
|
|
@@ -1428,7 +1438,24 @@ exports.ERROR_TYPE_PROPERTIES={[i.TASK_NOT_FOUND]:{statusCode:404,retryable:!1,d
|
|
|
1428
1438
|
class a extends Error{constructor(e,t="INTERNAL_ERROR",r=500,s){super(e),this.name=this.constructor.name,this.code=t,this.statusCode=r,this.details=s||{},s&&"code"in s&&"string"==typeof s.code&&(this.code=s.code)
|
|
1429
1439
|
const o=exports.ERROR_TYPE_PROPERTIES[this.code]
|
|
1430
1440
|
this.retryable=o?o.retryable:!s||!("retryable"in s)||!!s.retryable,Error.captureStackTrace(this,this.constructor)}toJSON(){return{name:this.name,message:this.message,code:this.code,statusCode:this.statusCode,details:this.details,retryable:this.retryable,stack:this.stack}}get metadata(){return this.details}isRetryable(){return this.retryable}}class c extends a{constructor(e="Resource not found",t){super(e,i.NOT_FOUND,404,t),this.resourceType="unknown",t&&("taskId"in t?(this.code=i.TASK_NOT_FOUND,this.resourceType="task"):"requestId"in t&&(this.code=i.REQUEST_NOT_FOUND,this.resourceType="request"))}}class l extends a{constructor(e="Invalid input",t){super(e,i.VALIDATION_ERROR,400,t)}}class d extends a{constructor(e="Authentication required",t){super(e,i.UNAUTHORIZED,401,t)}}class u extends a{constructor(e="Not authorized",t){super(e,i.FORBIDDEN,403,t)}}class h extends a{constructor(e="Database error",t){super(e,"DATABASE_ERROR",500,t)}}class g extends a{constructor(e="Service unavailable",t){if(super(e,i.SERVICE_UNAVAILABLE,503,t),t&&"structuredError"in t){const e=t.structuredError
|
|
1431
|
-
e&&e.error&&"retryable"in e.error&&(this.details.retryable=!!e.error.retryable)}}}class p extends a{constructor(e="File operation error",t){super(e,"FILE_OPERATION_ERROR",500,t)}}class m extends a{constructor(e="MCP protocol error",t){super(e,i.MCP_PROTOCOL_ERROR,500,t)}}exports.AppError=a,exports.NotFoundError=c,exports.ValidationError=l,exports.AuthenticationError=d,exports.AuthorizationError=u,exports.DatabaseError=h,exports.ServiceError=g,exports.FileOperationError=p,exports.McpProtocolError=m,exports.UnauthorizedError=d},
|
|
1441
|
+
e&&e.error&&"retryable"in e.error&&(this.details.retryable=!!e.error.retryable)}}}class p extends a{constructor(e="File operation error",t){super(e,"FILE_OPERATION_ERROR",500,t)}}class m extends a{constructor(e="MCP protocol error",t){super(e,i.MCP_PROTOCOL_ERROR,500,t)}}exports.AppError=a,exports.NotFoundError=c,exports.ValidationError=l,exports.AuthenticationError=d,exports.AuthorizationError=u,exports.DatabaseError=h,exports.ServiceError=g,exports.FileOperationError=p,exports.McpProtocolError=m,exports.UnauthorizedError=d},8649(e,exports,t){"use strict"
|
|
1442
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.spawnPlannerWithDoc=async function(e){const{agent:t,prompt:o,title:n,parentSessionId:i,requestId:a,apiClient:c,logger:l,projectId:d,projectPath:u,trackChildPid:h}=e
|
|
1443
|
+
let g=e.planId
|
|
1444
|
+
if(!g){const r=`PLAN-${(e.planName||n).replace(/\.md$/i,"")}.md`
|
|
1445
|
+
try{const e=await c.post(`/api/projects/${d}/documents`,{filename:r,content:" ",type:"Plan"})
|
|
1446
|
+
g=e?.data?.id,l.info("spawnPlannerWithDoc: pre-created plan document",{planId:g,filename:r,agent:t})}catch(e){l.warn("spawnPlannerWithDoc: failed to pre-create plan document; planner will fall back to plan_write create",{error:(0,s.getErrorMessage)(e),agent:t})}}const p="quest-planner"===t?"quest":"plan",m=g?`[Session Context]\n- ${p}Id: ${g} \u2014 this is YOUR associated ${p} document. It already exists with a placeholder filename.\n- Use plan_write (update / append / replace) with planId "${g}" to fill it in.${"quest-planner"===t?" For quests, plan_write update also accepts criteria / tickIntervalSeconds / maxInFlight / maxPerDay / minIterations / status.":""}${"quest-planner"===t?"":'\n- On your FIRST plan_write call, include a "name" field with a short descriptive title so the document gets a meaningful filename.'}\n- Do NOT call plan_write "create" \u2014 always modify the document above.\n- Do NOT call plan_read "get-active" \u2014 your associated ${p} is given here, not via the active-${p} slot.\n\n${o}`:o,f=await(0,r.spawnActionAgent)({agent:t,prompt:m,cwd:u,projectPath:u,projectId:d,title:n,requestId:a,parentSessionId:i,apiClient:c,logger:l,trackChildPid:h})
|
|
1447
|
+
if(!f.success||!f.sessionId)return{success:!1,error:f.error||"spawnActionAgent returned no sessionId"}
|
|
1448
|
+
l.info("spawnPlannerWithDoc: spawn succeeded",{agent:t,sessionId:f.sessionId,pid:f.pid,planId:g})
|
|
1449
|
+
const y=(0,r.getOpenCodeServerUrl)(u)
|
|
1450
|
+
if(y&&async function(e){const{serverUrl:t,sessionId:r,logger:s}=e,o=t.replace(/\/$/,""),n=Date.now()+3e4
|
|
1451
|
+
let i=0
|
|
1452
|
+
for(;Date.now()<n;){i++
|
|
1453
|
+
try{const e=await fetch(`${o}/session/${r}/message`,{signal:AbortSignal.timeout(5e3)})
|
|
1454
|
+
if(e.ok){const t=(await e.json()).find((e=>"user"===e.info?.role)),n=t?.parts.find((e=>"text"===e.type))
|
|
1455
|
+
if(t&&n){const e={...n,metadata:{...n.metadata||{},alchemy:{kind:"agent-context"}}},a=`${o}/session/${r}/message/${t.info.id}/part/${n.id}`,c=await fetch(a,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(5e3)})
|
|
1456
|
+
if(c.ok)return void s.info("Tagged planner agent-context message",{sessionId:r,attempt:i})
|
|
1457
|
+
s.warn("PATCH planner agent-context failed",{sessionId:r,status:c.status})}}}catch{}await new Promise((e=>setTimeout(e,500)))}s.warn("Timed out tagging planner agent-context; spawn prompt will appear as user message",{sessionId:r,attempts:i})}({serverUrl:y,sessionId:f.sessionId,logger:l}),g)try{await c.post(`/api/projects/${d}/session-plan-associations`,{opencodeSessionId:f.sessionId,documentId:g})}catch(e){l.warn("spawnPlannerWithDoc: failed to create session-plan association",{error:(0,s.getErrorMessage)(e),sessionId:f.sessionId,planId:g})}return{success:!0,sessionId:f.sessionId,planId:g,pid:f.pid}}
|
|
1458
|
+
const r=t(8148),s=t(8824)},8740(e,exports){"use strict"
|
|
1432
1459
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.HookCache=void 0,exports.HookCache=class{constructor(e,t,r){this.cache=new Map,this.logger=e,this.projectId=t,this.apiClient=r}setProjectId(e){this.projectId=e}getCacheKey(e,t){return t?`${e}:session:${t}`:e+":all"}async getHooksForEvent(e,t,r){const s=this.getCacheKey(e,t)
|
|
1433
1460
|
if(!r){const e=this.cache.get(s)
|
|
1434
1461
|
if(e&&6e4>Date.now()-e.time)return e.hooks.filter((e=>e.enabled))}try{const r=t?"?sessionId="+encodeURIComponent(t):"",o=await this.apiClient.get(`/api/projects/${this.projectId}/event-hooks/by-event/${e}${r}`),n=(o?.data||o||[]).map((e=>({id:e.id||e.hookId,name:e.name||e.hookName,eventType:e.eventType||e.hookEventType,agent:e.agent||e.hookAgent,promptTemplate:e.promptTemplate||e.hookPromptTemplate,condition:e.condition||e.hookCondition||{},enabled:!1!==e.enabled,action:e.action||e.hookAction||void 0,target:e.target||e.hookTarget||void 0,timing:e.timing||e.hookTiming||"after",maxIterations:e.maxIterations||e.hookMaxIterations||void 0,emitEvent:e.emitEvent||e.hookEmitEvent||void 0,emitEventOnCondition:e.emitEventOnCondition||e.hookEmitEventOnCondition||void 0,intervalSeconds:e.intervalSeconds||e.hookIntervalSeconds||void 0,timerCondition:e.timerCondition||e.hookTimerCondition||void 0,timerExpiresAfter:e.timerExpiresAfter||e.hookTimerExpiresAfter||void 0,stopAfterInactiveMinutes:e.stopAfterInactiveMinutes??e.hookStopAfterInactiveMinutes??10})))
|
|
@@ -1518,15 +1545,15 @@ let g=null,p=null,m=null,f=null,y=!1,v=!1
|
|
|
1518
1545
|
const w=c.join(s,".alchemy_lab","config.json")
|
|
1519
1546
|
if(!i){try{const e=await r.readFile(w,"utf-8"),t=JSON.parse(e)
|
|
1520
1547
|
t?.openCodeTools?(g=t.openCodeTools,this.logger.info("Found tool settings in project config",{projectConfigTools:g})):v=!0,t?.openCodeModel&&(p=t.openCodeModel)}catch(e){this.logger.debug("No project config found, will create with defaults"),v=!0}try{const e=await r.readFile(h,"utf-8"),t=JSON.parse(e)
|
|
1521
|
-
t?.mode?.alchemy?.tools&&(m=t.mode.alchemy.tools,this.logger.info("Found existing tool settings in opencode.json",{existingTools:m})),t?.model&&(f=t.model),t?.mode?.journal&&(y=!0)}catch(e){this.logger.info("No existing opencode.json found, will use defaults")}}const
|
|
1522
|
-
i?.openCodeModel?
|
|
1523
|
-
const
|
|
1524
|
-
await r.mkdir(
|
|
1525
|
-
const
|
|
1548
|
+
t?.mode?.alchemy?.tools&&(m=t.mode.alchemy.tools,this.logger.info("Found existing tool settings in opencode.json",{existingTools:m})),t?.model&&(f=t.model),t?.mode?.journal&&(y=!0)}catch(e){this.logger.info("No existing opencode.json found, will use defaults")}}const I=i?.openCodeTools||g||m||{glob:!1,grep:!1,ls:!1,list:!1,view:!1,read:!1,write:!1,edit:!1,patch:!1,diagnostics:!0,bash:!0===i?.enableBash,fetch:!1,sourcegraph:!0,agent:!1!==a,todoread:!1,todowrite:!1},b={tools:I},E=i?.openCodeModel||p||f||"anthropic/claude-opus-4-5"
|
|
1549
|
+
i?.openCodeModel?b.model=i.openCodeModel:p?b.model=p:f&&(b.model=f)
|
|
1550
|
+
const S=c.join(s,".opencode","agent")
|
|
1551
|
+
await r.mkdir(S,{recursive:!0})
|
|
1552
|
+
const C=E||"anthropic/claude-opus-4-5-20251101"
|
|
1526
1553
|
let k,T=null,_=null
|
|
1527
1554
|
if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),_=await this.fetchProjectFromRemote(i.projectId),k=_?.metadata?.globalSystemPrompt,k&&this.logger.info("Using global system prompt from project metadata",{projectId:i.projectId,promptLength:k.length}),T=await this.fetchAgentsFromRemote(i.projectId),T&&T.length>0){this.logger.info("Using agents from remote database",{count:T.length})
|
|
1528
1555
|
for(const e of T){if(!e.enabled)continue
|
|
1529
|
-
const t=this.generateAgentMarkdown(e,d,
|
|
1556
|
+
const t=this.generateAgentMarkdown(e,d,C,k),s=c.join(S,e.name+".md")
|
|
1530
1557
|
await r.writeFile(s,t,"utf8"),this.logger.info(`Created ${e.name} agent file from remote config`,{agentPath:s,mode:e.mode})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
|
|
1531
1558
|
else this.logger.warn("No projectId provided - agents must be configured via database")
|
|
1532
1559
|
const j={$schema:"https://opencode.ai/config.json",model:E,mcp:{[d]:{type:"local",command:[n,u,s],enabled:!0,timeout:6e5}}}
|
|
@@ -1539,14 +1566,14 @@ e.name in r&&(t.bash=!!r[e.name]),t.skill=!1,("subagent"===e.mode||"tester"===e.
|
|
|
1539
1566
|
let s=""
|
|
1540
1567
|
k&&(s+=k.trim()+"\n\n"),e.prompt&&(s+=e.prompt),e.promptAddition&&(s+="\n\n"+e.promptAddition),j.agent[e.name]={tools:t,...s?{prompt:s}:{},...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}j.agent.general={...j.agent.general,disable:!0},j.agent.explore={...j.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}}
|
|
1541
1568
|
const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${k?k.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`
|
|
1542
|
-
await r.writeFile(c.join(
|
|
1569
|
+
await r.writeFile(c.join(S,"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(j.agent).length})}const A=e=>"string"==typeof e&&e.includes(".opencode/plugin/"),P=(await this.fetchPluginManifest())["opencode-claude-auth"],M=j.plugin,O=Array.isArray(M)?M.filter((e=>!A(e))):[]
|
|
1543
1570
|
P&&this.applyAuthPluginPin(O,P),O.length>0?j.plugin=O:delete j.plugin,await r.writeFile(h,JSON.stringify(j,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:h})
|
|
1544
1571
|
try{const e=await Promise.resolve().then(t.t.bind(t,857,23)),s=c.join(e.homedir(),".alchemy_lab","opencode","config"),o=c.join(s,"opencode.json")
|
|
1545
1572
|
let n=null
|
|
1546
1573
|
try{n=JSON.parse(await r.readFile(o,"utf-8"))}catch{}const i=n??{},a=Array.isArray(i.plugin)?i.plugin.slice():[],l=a.filter((e=>!A(e)))
|
|
1547
1574
|
P&&this.applyAuthPluginPin(l,P),a.length===l.length&&!a.some(((e,t)=>e!==l[t]))&&n||(i.plugin=l,await r.mkdir(s,{recursive:!0}),await r.writeFile(o,JSON.stringify(i,null,2),"utf8"),this.logger.info("Updated global opencode config plugin list",{globalConfigPath:o,plugins:l}))}catch(e){this.logger.warn("Failed to update global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(v&&!i)try{let e={}
|
|
1548
1575
|
try{const t=await r.readFile(w,"utf-8")
|
|
1549
|
-
e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=
|
|
1576
|
+
e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=I,await r.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{syncPluginsGlobal:r,cleanupLegacyProjectPlugins:o}=await t.e(939).then(t.t.bind(t,9939,23)),n=await r(l??!1,this.logger,e)
|
|
1550
1577
|
n.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:n.errors}),await o(s,this.logger)}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,r.getErrorMessage)(e)}),{success:!1,error:(0,r.getErrorMessage)(e)}}}}exports.MCPClientConfigurer=o,o.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","fetch","agent","task","question","todoread","todowrite"])},9023(module){"use strict"
|
|
1551
1578
|
module.exports=require("util")},9067(e,exports,t){"use strict"
|
|
1552
1579
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
@@ -1604,7 +1631,7 @@ if(s(parseInt(e.replace("v","").split(".")[0],10)))return"node"}catch{}const e=p
|
|
|
1604
1631
|
if((0,i.existsSync)(t))try{const e=(0,i.readdirSync)(t).filter((e=>s(parseInt(e.replace("v","").split(".")[0],10)))).sort(((e,t)=>{const r=parseInt(e.replace("v","").split(".")[0],10)
|
|
1605
1632
|
return parseInt(t.replace("v","").split(".")[0],10)-r}))
|
|
1606
1633
|
if(e.length>0){const r=(0,n.join)(t,e[0],"bin","node")
|
|
1607
|
-
if((0,i.existsSync)(r))return r}}catch{}throw Error(`AlchemyLab requires Node.js 22 or 24+ to run the OpenCode server (Node 23 is not supported).\nYour current version: ${process.version}\nInstall a compatible version: nvm install 22 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,n.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.resolveServeCommand=function(){const e=r()
|
|
1634
|
+
if((0,i.existsSync)(r))return r}}catch{}throw Error(`AlchemyLab requires Node.js 22 or 24+ to run the OpenCode server (Node 23 is not supported).\nYour current version: ${process.version}\nInstall a compatible version: nvm install 22 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,n.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.findOpencodeDir=r,exports.resolveServeCommand=function(){const e=r()
|
|
1608
1635
|
return e?o((0,n.join)(e,"dist"),"serve.js"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
|
|
1609
1636
|
return e?o((0,n.join)(e,"dist"),"run.js"):{command:"opencode",prependArgs:[]}}
|
|
1610
1637
|
const n=t(6928),i=t(9896),a=t(5317)},9266(e,exports,t){"use strict"
|
|
@@ -1659,15 +1686,15 @@ setTimeout((()=>{clearInterval(s),r(!1)}),t)}))}async function a(t){if(process.e
|
|
|
1659
1686
|
try{const{createConfigManager:r}=await e.e(60).then(e.t.bind(e,9060,23)),s=r(t)
|
|
1660
1687
|
return(await s.getInstallationConfig()).remoteServerUrl||"https://api.alchemylabcode.com"}catch{return"https://api.alchemylabcode.com"}}async function c(e,t,r,s,o="cli-init"){try{const n=(0,h.basename)(r)||"Local Project",i=await fetch(e+"/api/projects",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer "+t,"X-Client-Type":"alchemy-local-service","X-Service-Secret":process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"},body:JSON.stringify({name:n,description:"Project for "+n,status:"active",metadata:{basePath:r,createdVia:o}})})
|
|
1661
1688
|
if(i.ok){const e=await i.json()
|
|
1662
|
-
return e.data?.id||e.id}s.info(`Project registration failed (HTTP ${i.status})`)}catch(e){s.info("Project registration failed",{error:(0,
|
|
1663
|
-
const l=e(3444),d=e(9896),u=e(1943),h=e(6928),g=e(5317),p=e(857),m=e(1253),f=e(8984),y=e(2366),v=e(3962),w=e(1187),
|
|
1689
|
+
return e.data?.id||e.id}s.info(`Project registration failed (HTTP ${i.status})`)}catch(e){s.info("Project registration failed",{error:(0,I.getErrorMessage)(e)})}}Object.defineProperty(i,"__esModule",{value:!0})
|
|
1690
|
+
const l=e(3444),d=e(9896),u=e(1943),h=e(6928),g=e(5317),p=e(857),m=e(1253),f=e(8984),y=e(2366),v=e(3962),w=e(1187),I=e(8824),b=e(6800),E=e(4139);(async function(){23===parseInt(process.version.replace("v","").split(".")[0],10)&&(console.error(`\n\u274c Node.js ${process.version} is not supported.`),console.error(" AlchemyLab requires Node.js 22 or 24+ (Node 23 has a broken sqlite API)."),console.error("\n Switch with: nvm use 22 or nvm use 24\n"),process.exit(1))
|
|
1664
1691
|
const i=process.argv.slice(2)
|
|
1665
|
-
let
|
|
1666
|
-
const
|
|
1667
|
-
const k=i.filter(((e,t)=>"--port"!==e&&(-1===
|
|
1692
|
+
let S
|
|
1693
|
+
const C=i.indexOf("--port");-1!==C&&i[C+1]&&(S=parseInt(i[C+1],10),isNaN(S)&&(console.error("\u274c Invalid port number"),process.exit(1)))
|
|
1694
|
+
const k=i.filter(((e,t)=>"--port"!==e&&(-1===C||t!==C+1)))
|
|
1668
1695
|
if(0===k.length)return void await async function(r={}){const i=process.cwd(),l=(0,h.join)(i,E.ALCHEMY_DIR_NAME)
|
|
1669
1696
|
await(0,u.mkdir)(l,{recursive:!0})
|
|
1670
|
-
const
|
|
1697
|
+
const S=(0,h.join)(l,"server.log"),{createLogger:C}=await Promise.resolve().then(e.t.bind(e,2782,23)),k=C({serviceName:"alchemy-server",console:!1,logToFile:!0,logFilePath:S,baseDir:i}),T=await a(k),_=new m.TokenManager(k,i,{remoteServerUrl:T,allowInteractiveAuth:!1})
|
|
1671
1698
|
await _.initialize()
|
|
1672
1699
|
let j=await _.getToken()
|
|
1673
1700
|
if(!j?.token){console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
|
|
@@ -1695,15 +1722,15 @@ console.log(" Downloading from: "+t)
|
|
|
1695
1722
|
const s=(0,h.join)((0,p.homedir)(),".local","bin")
|
|
1696
1723
|
await(0,u.mkdir)(s,{recursive:!0})
|
|
1697
1724
|
const o=(0,h.join)(s,"opencode")
|
|
1698
|
-
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(s)||(console.log(` \u26a0\ufe0f Add ${s} to your PATH:`),console.log(` export PATH="${s}:$PATH"`),console.log(""),process.env.PATH=`${s}:${process.env.PATH}`),void console.log("\u2705 OpenCode installed successfully")}}catch(e){console.log("\u26a0\ufe0f Binary download failed: "+(0,
|
|
1725
|
+
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(s)||(console.log(` \u26a0\ufe0f Add ${s} to your PATH:`),console.log(` export PATH="${s}:$PATH"`),console.log(""),process.env.PATH=`${s}:${process.env.PATH}`),void console.log("\u2705 OpenCode installed successfully")}}catch(e){console.log("\u26a0\ufe0f Binary download failed: "+(0,I.getErrorMessage)(e))}console.log(""),console.log("\u26a0\ufe0f Could not auto-install OpenCode."),console.log(""),console.log(" Install manually:"),console.log(" go install github.com/sst/opencode@latest"),console.log(" Or download from:"),console.log(" https://github.com/sst/opencode/releases"),console.log("")}(i)
|
|
1699
1726
|
const O=t()
|
|
1700
1727
|
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)
|
|
1701
|
-
const R=r.port||await(0,
|
|
1728
|
+
const R=r.port||await(0,b.findAvailablePort)(3456),D=new y.AlchemyActionServer(k,{projectPath:i,remoteServerUrl:T,port:R,authToken:A,tokenManager:_})
|
|
1702
1729
|
try{const t=(0,v.getInstallContext)(),r=new f.MCPClientConfigurer(k,T,A),s=new w.UnifiedLauncher(k,t,r),[,a,c]=await Promise.all([D.start(),(async()=>{if(process.env.FRONTEND_URL)return process.env.FRONTEND_URL
|
|
1703
1730
|
if(T.includes("api.alchemylabcode.com"))return"https://alchemylabcode.com"
|
|
1704
1731
|
try{const e=await fetch(T+"/health")
|
|
1705
1732
|
if(e.ok){const t=await e.json()
|
|
1706
|
-
if(t?.data?.frontendUrl)return t.data.frontendUrl}}catch{}return T.replace(/:\d+/,":3000")})(),s.launch({projectPath:i,settings:M?{projectId:M}:void 0}).catch((e=>(k.warn("Failed to launch OpenCode serve",{error:(0,
|
|
1733
|
+
if(t?.data?.frontendUrl)return t.data.frontendUrl}}catch{}return T.replace(/:\d+/,":3000")})(),s.launch({projectPath:i,settings:M?{projectId:M}:void 0}).catch((e=>(k.warn("Failed to launch OpenCode serve",{error:(0,I.getErrorMessage)(e)}),{success:!1,error:(0,I.getErrorMessage)(e)})))])
|
|
1707
1734
|
if(console.log(""),console.log(" Action server .... port "+D.getPort()),c.success&&"port"in c&&c.port){"pid"in c&&c.pid&&D.trackChildPid(c.pid),D.setOpenCodePort(c.port),console.log(" OpenCode serve ... port "+c.port)
|
|
1708
1735
|
const e=(0,h.join)(i,E.ALCHEMY_DIR_NAME,"opencode.json");(0,d.writeFileSync)(e,JSON.stringify({port:c.port,pid:"pid"in c?c.pid:void 0,startedAt:(new Date).toISOString()},null,2)),k.info("Wrote OpenCode config",{path:e,port:c.port})}else c.success?console.log(" OpenCode serve ... started"):console.log(" OpenCode serve ... failed (run manually: opencode serve)")
|
|
1709
1736
|
console.log(""),console.log(` \u2192 Alchemy UI: \x1b]8;;${a}\x07${a}\x1b]8;;\x07`),console.log("")
|
|
@@ -1716,7 +1743,7 @@ u=setInterval((async()=>{if(!y)try{if(await _.hasValidToken())return
|
|
|
1716
1743
|
y=!0,console.log(""),console.log("\u26a0\ufe0f Auth token expired. Opening browser to re-authenticate...")
|
|
1717
1744
|
const e=await _.startAuthenticationFlow()
|
|
1718
1745
|
if(console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(_)){const e=await _.getToken()
|
|
1719
|
-
e?.token&&(A=e.token,D.updateAuthToken(A)),console.log("\u2705 Re-authenticated successfully.")}else console.log("\u274c Re-authentication timed out. Some features may not work until you restart.")}catch(e){k.warn("Token watchdog check failed",{error:(0,
|
|
1746
|
+
e?.token&&(A=e.token,D.updateAuthToken(A)),console.log("\u2705 Re-authenticated successfully.")}else console.log("\u274c Re-authentication timed out. Some features may not work until you restart.")}catch(e){k.warn("Token watchdog check failed",{error:(0,I.getErrorMessage)(e)})}finally{y=!1}}),3e5),await m.start(g)}catch(e){k.error("Failed to start server",{error:(0,I.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,I.getErrorMessage)(e)),process.exit(1)}}({port:S})
|
|
1720
1747
|
const T=k[0].toLowerCase()
|
|
1721
1748
|
switch("--help"!==T&&"-h"!==T||(r(),process.exit(0)),"--version"!==T&&"-v"!==T||(console.log("alchemy v"+t()),process.exit(0)),T){case"serve":await async function(e={}){const r=process.cwd()
|
|
1722
1749
|
s(r)||(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))
|
|
@@ -1726,11 +1753,11 @@ const i=await a(o),c=new m.TokenManager(o,r,{remoteServerUrl:i,allowInteractiveA
|
|
|
1726
1753
|
await c.initialize()
|
|
1727
1754
|
const d=await c.getToken(),u=d?.token
|
|
1728
1755
|
u||console.log("\u26a0\ufe0f No auth token. Some features may be limited. Run `alchemy` to authenticate.")
|
|
1729
|
-
const h=e.port||await(0,
|
|
1756
|
+
const h=e.port||await(0,b.findAvailablePort)(3456),g=new y.AlchemyActionServer(o,{projectPath:r,remoteServerUrl:i,port:h,authToken:u,tokenManager:c})
|
|
1730
1757
|
try{await g.start(),console.log(""),console.log("\u2705 Action server running!"),console.log(` Health: http://127.0.0.1:${g.getPort()}/health`),console.log(""),console.log("Press Ctrl+C to stop the server."),console.log("")
|
|
1731
1758
|
let e=!1
|
|
1732
1759
|
const t=async()=>{e||(e=!0,console.log(""),console.log("Shutting down action server..."),await g.stop(),process.exit(0))}
|
|
1733
|
-
process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){o.error("Failed to start server",{error:(0,
|
|
1760
|
+
process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){o.error("Failed to start server",{error:(0,I.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,I.getErrorMessage)(e)),process.exit(1)}}({port:S})
|
|
1734
1761
|
break
|
|
1735
1762
|
case"logout":await async function(){const e=process.cwd(),t=(0,l.createEnvLogger)({serviceName:"alchemy-cli",console:!1,baseDir:e}),r=await a(t),s=new m.TokenManager(t,e,{remoteServerUrl:r,allowInteractiveAuth:!1})
|
|
1736
1763
|
await s.initialize(),await s.hasValidToken()?await s.removeToken()?console.log("Logged out successfully."):(console.error("Failed to remove auth token."),process.exit(1)):console.log("Not currently logged in.")}()
|