alchemylab 1.4.5 → 1.4.6
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 +87 -89
- package/dist/http-server.js +205 -178
- package/dist/index.js +205 -178
- package/dist/mcp-direct.js +205 -178
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ const{sessionId:s,hookId:n}=e.params,a=await o.post(`/api/projects/${r}/hook-ite
|
|
|
26
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)))}
|
|
27
27
|
const r=t(8824)},441(e,exports){"use strict"
|
|
28
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"
|
|
29
|
-
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=C.shift()
|
|
30
30
|
e&&setImmediate(e)}async function o(e,t){const o=Date.now()
|
|
31
31
|
!function(e){let t=!1
|
|
32
32
|
for(const r of E.values())r.busy&&r.busySince&&e-r.busySince>I&&(r.busy=!1,r.busySince=void 0,t=!0)
|
|
@@ -41,13 +41,13 @@ try{n=await d.promises.readdir(o,{withFileTypes:!0})}catch{return}for(const e of
|
|
|
41
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
|
|
42
42
|
const l=Date.now(),d=setTimeout((()=>{if(c)return
|
|
43
43
|
c=!0
|
|
44
|
-
const e=
|
|
45
|
-
0>e||
|
|
44
|
+
const e=C.indexOf(u)
|
|
45
|
+
0>e||C.splice(e,1),a(Error(`All ${t} pool slot(s) busy after waiting ${Math.round((Date.now()-l)/1e3)}s`))}),r)
|
|
46
46
|
d.unref()
|
|
47
47
|
const u=async()=>{if(c)return
|
|
48
48
|
const r=await o(e,t)
|
|
49
|
-
c?r&&n(r.path):r?(c=!0,clearTimeout(d),i(r)):(
|
|
50
|
-
|
|
49
|
+
c?r&&n(r.path):r?(c=!0,clearTimeout(d),i(r)):(C.unshift(u),s())}
|
|
50
|
+
C.push(u)}))},exports.releaseSlot=n,exports.scheduleSlotRelease=i,exports.watchPidAndRelease=function(e,t,r,s=5e3){if(i(t,r),!e||0>=e)return
|
|
51
51
|
const o=setInterval((()=>{try{process.kill(e,0)}catch{n(t),clearInterval(o)}}),s)
|
|
52
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""
|
|
53
53
|
const r=(0,u.createHash)("sha256")
|
|
@@ -57,8 +57,8 @@ await d.promises.writeFile((0,h.join)(e,y),t.join("\n")+"\n","utf-8")}catch(e){c
|
|
|
57
57
|
if(!t)return{removed:0}
|
|
58
58
|
const r=await c(e)
|
|
59
59
|
let s=0
|
|
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(),
|
|
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,
|
|
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(),C.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,C=[]},833(e,exports,t){"use strict"
|
|
62
62
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
63
63
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.getOpenCodeUrlFromConfig=function(e){const t=o.default.join(e,".alchemy_lab","opencode.json")
|
|
64
64
|
try{if(!s.default.existsSync(t))return null
|
|
@@ -297,8 +297,8 @@ Object.defineProperty(exports,"__esModule",{value:!0}),exports.partMeta=function
|
|
|
297
297
|
return{...e,_meta:{...s,alchemy:{...o,...r,kind:t}}}},exports.readAlchemyMeta=function(e){return e?._meta?.alchemy}},2366(e,exports,t){"use strict"
|
|
298
298
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
299
299
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
|
|
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),
|
|
301
|
-
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),C=t(6563),S=t(1401),k=t(1292),T=t(8649),_=t(4306),j=t(9068),A=t(3435),P=t(8463),M=t(489),O=t(3807),R=t(7478),D=t(5317),x=t(355),$=t(833)
|
|
301
|
+
class F{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
|
|
302
302
|
const r=(0,l.createMetricsCollector)(!1)
|
|
303
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)()
|
|
304
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
|
|
@@ -308,7 +308,7 @@ const c=Math.max(0,o-e),l=Buffer.alloc(o-c)
|
|
|
308
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}})))
|
|
309
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??{}
|
|
310
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??{}
|
|
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,
|
|
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,x.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
|
|
312
312
|
if(!s)return void t.status(400).json({success:!1,error:"No project"})
|
|
313
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})))
|
|
314
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))
|
|
@@ -321,11 +321,11 @@ if(!r||"before"!==i&&"after"!==i)return void t.status(400).json({success:!1,erro
|
|
|
321
321
|
const a=(0,w.parseToolEvents)({toolName:r,sessionId:s||"",input:o,output:n,timing:i})
|
|
322
322
|
let c=0
|
|
323
323
|
for(const e of a)c+=(await this.hookExecutor.processEvent({eventType:e.eventType,sessionId:s||"",variables:e.variables,timing:e.timing})).triggered
|
|
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,
|
|
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,R.generateRequestBranchName)(r,""):void 0)
|
|
325
325
|
if(!s)return void t.status(400).json({success:!1,error:"requestId or branch required"})
|
|
326
|
-
const o=(await(0,
|
|
326
|
+
const o=(await(0,O.simpleGit)(this.options.projectPath).revparse([s])).trim()
|
|
327
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||{}
|
|
328
|
-
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,M.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:[]}
|
|
329
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"})
|
|
330
330
|
const r=this.timerHookScheduler.pauseHook(e.params.hookId)
|
|
331
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"})
|
|
@@ -376,7 +376,7 @@ e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3
|
|
|
376
376
|
if(!s)return void r.status(400).json({success:!1,error:"requestId is required"})
|
|
377
377
|
if(!/^req\d+$/.test(s))return void r.status(400).json({success:!1,error:"Invalid requestId format"})
|
|
378
378
|
this.logger.info("Generating journal for request",{requestId:s})
|
|
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,
|
|
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,D.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)())}})
|
|
380
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
|
|
381
381
|
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
382
382
|
const{sessionId:s,requestId:o,summary:n,status:i}=e.body
|
|
@@ -424,43 +424,27 @@ if(!t||t===r)throw e
|
|
|
424
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()
|
|
425
425
|
const u=l.headers.get("content-type")||""
|
|
426
426
|
let h
|
|
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,
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
if(!
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if(!this.projectId)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
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})
|
|
438
|
-
t.json({success:!0,data:g})}catch(e){this.logger.error("Spawn session failed",{error:(0,f.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,f.getErrorMessage)(e)})}}async handleRunTestsNow(e,r){try{const{requestId:s,parentSessionId:o,testProcessIds:n,commandNames:i}=e.body||{}
|
|
439
|
-
if(!this.projectId)return void r.status(400).json({success:!1,error:"No project configured"})
|
|
440
|
-
if(!s)return void r.status(400).json({success:!1,error:"requestId is required"})
|
|
441
|
-
const{handleRunTestsAction:a}=await Promise.resolve().then(t.bind(t,6555)),c={projectId:this.projectId,projectPath:this.options.projectPath,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackedChildPids.add(e),cachedDefaultModel:this.cachedDefaultModel??null},l=await a({requestId:s,parentSessionId:o,testProcessIds:n,commandNames:i,mode:"auto"},c)
|
|
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,t){try{if(!(0,A.validateRequest)(e,t,this.projectId,["planId","prompt"]))return
|
|
428
|
+
const{agent:r,planId:s,prompt:o,title:n,parentSessionId:i}=e.body||{},a=new Set(["planner","auto-planner","quest-planner"])
|
|
429
|
+
if(r&&!a.has(r))return void t.status(400).json({success:!1,error:"agent must be one of: "+[...a].join(", ")})
|
|
430
|
+
const c=await(0,T.spawnPlannerWithDoc)({agent:r||"planner",planId:s,prompt:o,title:n||"Planner",parentSessionId:i,apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e)})
|
|
431
|
+
if(!c.success||!c.sessionId)return void t.status(500).json({success:!1,error:c.error||"spawn failed"})
|
|
432
|
+
t.json({success:!0,data:{sessionId:c.sessionId,planId:c.planId,pid:c.pid}})}catch(e){(0,A.respondWithError)(t,this.logger,"Spawn planner",e)}}async handleSpawnSession(e,t){try{if(!(0,A.validateRequest)(e,t,this.projectId,["agent","prompt"]))return
|
|
433
|
+
const{agent:r,prompt:s,requestId:o,parentSessionId:n,title:i,specialization:a,specializationHeadline:c,allowParallel:l,callerAgentType:d}=e.body,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})
|
|
434
|
+
t.json({success:!0,data:g})}catch(e){(0,A.respondWithError)(t,this.logger,"Spawn session",e)}}async handleRunTestsNow(e,r){try{if(!(0,A.validateRequest)(e,r,this.projectId,["requestId"]))return
|
|
435
|
+
const{requestId:s,parentSessionId:o,testProcessIds:n,commandNames:i}=e.body||{},{handleRunTestsAction:a}=await Promise.resolve().then(t.bind(t,6555)),c={projectId:this.projectId,projectPath:this.options.projectPath,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackedChildPids.add(e),cachedDefaultModel:this.cachedDefaultModel??null},l=await a({requestId:s,parentSessionId:o,testProcessIds:n,commandNames:i,mode:"auto"},c)
|
|
442
436
|
if(!l.success)return void r.status(500).json({success:!1,error:l.error||l.message})
|
|
443
|
-
r.json({success:!0,data:l.data})}catch(e){this.logger
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
if(!h.success)return void t.status(500).json({success:!1,error:h.error||"Spawn failed"})
|
|
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||{}
|
|
452
|
-
if(!this.projectId)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
453
|
-
const i=this.projectId,a=this.options.projectPath
|
|
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})
|
|
458
|
-
if(!g.success)return void t.status(500).json({success:!1,error:g.error||"Spawn failed"})
|
|
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
|
|
437
|
+
r.json({success:!0,data:l.data})}catch(e){(0,A.respondWithError)(r,this.logger,"Run tests now",e)}}async handleSetupTests(e,t){try{if(!(0,A.validateRequest)(e,t,this.projectId))return
|
|
438
|
+
const{requestId:r,parentSessionId:s,branchName:o,context:n}=e.body||{},i=await(0,_.setupAgentInWorktree)({agent:"test-architect",promptBuilder(e,t,r){return(0,P.buildSetupPrompt)(e,t,r)},titleBuilder(e){return e?`[${e}] Test setup`:"Test setup"},worktreeErrorLabel:"Failed to create setup worktree",callerContext:n,requestId:r,parentSessionId:s,branchName:o,apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0})
|
|
439
|
+
if(!i.success)return void t.status(i.statusCode||500).json({success:!1,error:i.error})
|
|
440
|
+
t.json({success:!0,data:{sessionId:i.sessionId,worktreePath:i.worktreePath,branch:i.branch}})}catch(e){(0,A.respondWithError)(t,this.logger,"Setup tests",e)}}async handleSetupCommands(e,t){try{if(!(0,A.validateRequest)(e,t,this.projectId))return
|
|
441
|
+
const{requestId:r,parentSessionId:s,branchName:o,context:n}=e.body||{},i=await(0,_.setupAgentInWorktree)({agent:"command-architect",promptBuilder(e,t,r){return(0,P.buildCommandSetupPrompt)(e,t,r)},titleBuilder(e){return e?`[${e}] Commands setup`:"Commands setup"},worktreeErrorLabel:"Failed to create setup worktree (commands)",callerContext:n,requestId:r,parentSessionId:s,branchName:o,apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0})
|
|
442
|
+
if(!i.success)return void t.status(i.statusCode||500).json({success:!1,error:i.error})
|
|
443
|
+
t.json({success:!0,data:{sessionId:i.sessionId,worktreePath:i.worktreePath,branch:i.branch}})}catch(e){(0,A.respondWithError)(t,this.logger,"Setup commands",e)}}async handleGetAgentByName(e,t){try{const r=this.projectId
|
|
460
444
|
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
461
445
|
const{agentName:s}=e.params,o=await this.apiClient.get(`/api/projects/${r}/agents`),n=(o?.data||o||[]).find((e=>e.name===s))
|
|
462
446
|
if(!n)return void t.status(404).json({success:!1,error:`Agent '${s}' not found`})
|
|
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<
|
|
447
|
+
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<F.PROJECT_CONFIG_TTL)return this.cachedProjectConfig.config
|
|
464
448
|
const e=d.default.join(this.options.projectPath,".alchemy_lab","project.json")
|
|
465
449
|
try{if(!u.default.existsSync(e))return this.cachedProjectConfig={config:null,timestamp:Date.now()},null
|
|
466
450
|
const t=u.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
@@ -473,7 +457,7 @@ if(!o||!n)return
|
|
|
473
457
|
const i=`${o}/${n}`
|
|
474
458
|
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
|
|
475
459
|
if(this.openCodeUrlCache&&3e4>Date.now()-this.openCodeUrlCache.timestamp)return this.openCodeUrlCache.url
|
|
476
|
-
const e=(0
|
|
460
|
+
const e=(0,$.getOpenCodeUrlFromConfig)(this.options.projectPath)
|
|
477
461
|
return e&&(this.openCodeUrlCache={url:e,timestamp:Date.now()}),e}async handleSessionCommand(e,t){const r=this.getOpenCodeUrl()
|
|
478
462
|
if(!r)return{success:!1,error:"OpenCode server not available. Is 'opencode serve' running?"}
|
|
479
463
|
try{switch(e){case"list-sessions":{const[e,s]=await Promise.all([fetch(r+"/session"),fetch(r+"/session/status")])
|
|
@@ -508,7 +492,7 @@ r?.agentType&&(a=r.agentType)}catch{}return{success:!0,data:{id:e,title:i||"(unt
|
|
|
508
492
|
if(!e?.id)throw Error("No project configuration found")
|
|
509
493
|
this.projectId=e.id,this.hookExecutor.setProjectId(this.projectId),this.timerHookScheduler?.setProjectId(this.projectId)
|
|
510
494
|
try{const e=d.default.resolve(__dirname,"../../package.json"),t=JSON.parse(u.default.readFileSync(e,"utf8"))
|
|
511
|
-
this.clientVersion=t.version}catch{}try{const e=(0,
|
|
495
|
+
this.clientVersion=t.version}catch{}try{const e=(0,j.findOpencodeDir)()
|
|
512
496
|
if(e){const t=JSON.parse(u.default.readFileSync(d.default.join(e,"package.json"),"utf8"))
|
|
513
497
|
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}
|
|
514
498
|
this.logger.info("Registering with remote",{projectId:this.projectId})
|
|
@@ -569,9 +553,9 @@ if(void 0!==r&&this.updateDefaultModel(r),void 0!==t&&t!==this.cachedAutoJournal
|
|
|
569
553
|
const e=new y.ProjectConfigService(this.logger)
|
|
570
554
|
await e.saveProjectConfig(this.options.projectPath,t),this.cachedAutoJournal=t}}}catch(e){this.logger.debug("Settings sync failed",{error:(0,f.getErrorMessage)(e)})}}async processAction(e){this.logger.info("Processing action",{actionId:e.id,type:e.type})
|
|
571
555
|
const t=Date.now()
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
await this.apiClient.post(`/api/actions/${e.id}/result`,{success:!
|
|
556
|
+
let r
|
|
557
|
+
try{const s={projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},o=await(0,g.executeAction)(e.type,e.payload||{},s)
|
|
558
|
+
r={success:o.success,result:o.data,error:o.message||o.error,duration:Date.now()-t}}catch(e){r={success:!1,error:(0,f.getErrorMessage)(e),duration:Date.now()-t}}try{await this.apiClient.post(`/api/actions/${e.id}/result`,r),r.success?this.logger.info("Action processed",{actionId:e.id,type:e.type,success:!0,duration:r.duration}):this.logger.error("Action failed",{actionId:e.id,type:e.type,error:r.error})}catch(t){this.logger.error("Failed to POST action result after retries; relying on stale-action sweep",{actionId:e.id,type:e.type,workSucceeded:r.success,postError:(0,f.getErrorMessage)(t)})}}async handleCLIBridgeCommand(e,t){try{const{action:r,params:s,timeout:o}=e.body
|
|
575
559
|
if(!r)return void t.status(400).json({success:!1,error:"action is required"})
|
|
576
560
|
if(["list-notifications","get-notification","respond-to-notification"].includes(r)){const e=await this.handleNotificationCommand(r,s||{})
|
|
577
561
|
return void t.json(e)}if(["list-sessions","get-messages","send-message","create-session","get-session-status"].includes(r)){const e=await this.handleSessionCommand(r,s||{})
|
|
@@ -604,7 +588,7 @@ if(s.ok){const e=await s.json(),t=e?.data?.settings||e?.settings
|
|
|
604
588
|
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()
|
|
605
589
|
try{const e=d.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
606
590
|
if(u.default.existsSync(e)){const t=u.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
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
|
|
591
|
+
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 C.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 S.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()??[]
|
|
608
592
|
t.json({success:!0,data:r})}handleTestInjectStuckSession(e,t){if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"Watchdog not running"})
|
|
609
593
|
const{sessionId:r,title:s}=e.body||{},o=this.stuckSessionWatchdog.injectTestStuck(r,s)
|
|
610
594
|
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
|
|
@@ -618,7 +602,7 @@ for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortCo
|
|
|
618
602
|
try{const e=d.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
|
|
619
603
|
u.default.existsSync(e)&&u.default.unlinkSync(e)}catch{}if(this.projectId)try{const{removeEntry:e}=await Promise.resolve().then(t.bind(t,2640))
|
|
620
604
|
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)
|
|
621
|
-
this.server.close((()=>{clearTimeout(t),e()}))}else e()})),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=
|
|
605
|
+
this.server.close((()=>{clearTimeout(t),e()}))}else e()})),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=F,F.PROJECT_CONFIG_TTL=3e4},2424(e,exports,t){"use strict"
|
|
622
606
|
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)
|
|
623
607
|
if((0,r.existsSync)(l))return void i.info("Worktree already exists on disk, skipping pre-spawn creation",{requestId:t,worktreePath:l})
|
|
624
608
|
const d=(0,o.simpleGit)(e);(0,r.mkdirSync)(c,{recursive:!0})
|
|
@@ -723,7 +707,13 @@ break
|
|
|
723
707
|
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)
|
|
724
708
|
const c=(0,s.determineEventToEmit)(e,a)
|
|
725
709
|
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"
|
|
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}}},
|
|
710
|
+
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}}},3435(e,exports,t){"use strict"
|
|
711
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.validateRequest=function(e,t,r,s=[]){if(!r)return t.status(400).json({success:!1,error:"No project configured"}),!1
|
|
712
|
+
const o=e.body??{},n=s.filter((e=>void 0===o[e]||null===o[e]||""===o[e]))
|
|
713
|
+
if(n.length>0){const e=1===n.length?"is":"are"
|
|
714
|
+
return t.status(400).json({success:!1,error:`${n.join(", ")} ${e} required`}),!1}return!0},exports.respondWithError=function(e,t,s,o){const n=(0,r.getErrorMessage)(o)
|
|
715
|
+
t.error(s+" failed",{error:n}),e.status(500).json({success:!1,error:n})}
|
|
716
|
+
const r=t(8824)},3444(e,exports,t){"use strict"
|
|
727
717
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
728
718
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.LogLevel=exports.defaultLogger=exports.isDebugToolsEnabled=exports.getDebugLevelFromEnv=exports.isDebugLoggingEnabled=exports.createEnvLogger=exports.createLogger=void 0
|
|
729
719
|
const s=t(2782)
|
|
@@ -828,7 +818,15 @@ this.logger.warn(`API request failed, retrying in ${r}ms`,{method:e,url:i,error:
|
|
|
828
818
|
this.failureCount=0,this.circuitState="closed",this.metrics.setGauge("api_failure_count",0),this.metrics.setGauge("circuit_breaker_open",0),this.circuitResetTimeout&&(clearTimeout(this.circuitResetTimeout),this.circuitResetTimeout=null),e&&this.logger.info("Circuit breaker closed after successful request")}openCircuit(){this.circuitState="open",this.metrics.setGauge("circuit_breaker_open",1),this.logger.warn(`Circuit breaker opened after ${this.failureCount} failures`),this.circuitResetTimeout&&clearTimeout(this.circuitResetTimeout)
|
|
829
819
|
const e=this.options.circuitBreakerResetTime||5e3
|
|
830
820
|
this.circuitResetTimeout=setTimeout((()=>{this.circuitState="half-open",this.logger.info("Circuit breaker half-open, next request will probe")}),e)}async get(e,t,r){return this.requestWithRetry("GET",e,void 0,r,t)}async post(e,t,r){return this.requestWithRetry("POST",e,t,r)}async put(e,t,r){return this.requestWithRetry("PUT",e,t,r)}async delete(e,t){return this.requestWithRetry("DELETE",e,void 0,t)}async patch(e,t,r){return this.requestWithRetry("PATCH",e,t,r)}}},4245(module){function e(e){var t=Error("Cannot find module '"+e+"'")
|
|
831
|
-
throw t.code="MODULE_NOT_FOUND",t}e.keys=()=>[],e.resolve=e,e.id=4245,module.exports=e},
|
|
821
|
+
throw t.code="MODULE_NOT_FOUND",t}e.keys=()=>[],e.resolve=e,e.id=4245,module.exports=e},4306(e,exports,t){"use strict"
|
|
822
|
+
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
823
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.setupAgentInWorktree=async function(e){const{projectPath:t,projectId:r,apiClient:a,logger:c,trackChildPid:l}=e
|
|
824
|
+
await(0,o.sweepStaleSetupWorktrees)(t,c)
|
|
825
|
+
const d=await(0,o.resolveSetupBranch)(a,r,e.branchName,e.requestId,c,t),u=(0,o.generateSetupId)(),h=s.default.join((0,o.getSetupWorktreesBase)(t),u)
|
|
826
|
+
try{await(0,o.createSetupWorktree)(t,h,d,c)}catch(t){const r=(0,i.getErrorMessage)(t)
|
|
827
|
+
return c.error(e.worktreeErrorLabel,{branch:d,error:r}),{success:!1,error:"Failed to create setup worktree: "+r,statusCode:500}}const g=e.promptBuilder(t,e.requestId,e.callerContext),p=e.titleBuilder(e.requestId),m=await(0,n.spawnActionAgent)({agent:e.agent,prompt:g,cwd:h,projectPath:t,projectId:r,title:p,requestId:e.requestId,parentSessionId:e.parentSessionId,allowParallel:!0,apiClient:a,logger:c,trackChildPid:l,getDefaultModel:e.getDefaultModel})
|
|
828
|
+
return m.success&&m.sessionId?{success:!0,sessionId:m.sessionId,worktreePath:h,branch:d}:{success:!1,error:m.error||"Spawn failed",statusCode:500}}
|
|
829
|
+
const s=r(t(6928)),o=t(8463),n=t(8148),i=t(8824)},4627(e,exports){"use strict"
|
|
832
830
|
function t(e,t){try{return function(e){const t=r.get(e)
|
|
833
831
|
if(t)return t
|
|
834
832
|
let s=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,"{{GLOBSTAR}}").replace(/\*/g,"[^/]*").replace(/\?/g,".").replace(/\{\{GLOBSTAR\}\}/g,".*")
|
|
@@ -891,9 +889,9 @@ break}await s(500)}for(;Date.now()<n;){const o=await r(e,t)
|
|
|
891
889
|
if("error"===o)return{status:"error"}
|
|
892
890
|
if(("idle"===o||"completed"===o)&&i)return{status:"completed"}
|
|
893
891
|
"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),
|
|
892
|
+
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),C=function(e){return e.reduce(((e,t)=>e+(t.toolCalls?.length||0)),0)}(E),S=function(e){for(let t=e.length-1;t>=0;t--)if("assistant"===e[t].role&&e[t].text)return e[t].text
|
|
895
893
|
return""}(E)
|
|
896
|
-
return{success:!0,message:"Prompt completed",data:{sessionId:w,status:I.status,messages:E,toolCallCount:
|
|
894
|
+
return{success:!0,message:"Prompt completed",data:{sessionId:w,status:I.status,messages:E,toolCallCount:C,elapsedMs:b,finalResponse:S}}}
|
|
897
895
|
const o=t(8148)},4769(e,exports,t){"use strict"
|
|
898
896
|
function r(e){return(0,o.getOpenCodeServerUrl)(e.projectPath,e.logger)}async function s(e,t,s,o){const n=r(s)
|
|
899
897
|
if(!n||!t)return!1
|
|
@@ -1016,12 +1014,12 @@ return"idle"===t||"completed"===t}))
|
|
|
1016
1014
|
return{sessions:s,statusMap:o,running:n,idle:i}}catch(e){return this.logger.debug("Failed to fetch OpenCode session state",{error:e+""}),null}}}},6540(e,exports){"use strict"
|
|
1017
1015
|
var t
|
|
1018
1016
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.LogLevel=void 0,function(e){e.ERROR="error",e.WARN="warn",e.INFO="info",e.DEBUG="debug"}(t||(exports.LogLevel=t={}))},6555(e,exports,t){"use strict"
|
|
1019
|
-
function r(e,process,t,r,s=!1,o){const
|
|
1020
|
-
let
|
|
1021
|
-
for(let e=0;e<
|
|
1022
|
-
|
|
1023
|
-
for(const t of e.acceptanceCriteria)
|
|
1024
|
-
|
|
1017
|
+
function r(e,process,t,r,s=!1,o,n){const i=process.config||{},a=(i.setup,i.commands||[]),c=o&&o.length>0?o.map((e=>a.find((t=>t.name===e)))).filter((e=>e)):a,l=new Set(n||[]),d=l.size>0?c.filter((e=>!l.has(e.name))):c,u=o&&o.length>0&&d.length<a.length||l.size>0,h=l.size>0?a.filter((e=>l.has(e.name))).map((e=>e.name)):[]
|
|
1018
|
+
let g=`You are a test runner agent. Your job is to verify that request ${e.localId||e.id} ("${e.title}") passes all required tests.\n\n## Test Process: ${process.name}\n${process.description?`\n${process.description}\n`:""}\n${process.scope?`**Scope:** Files under \`${process.scope}\`\n`:""}\n## Project ID: ${r}\n${h.length>0?`\n_The coder opted out of these command(s) for this completion: **${h.join(", ")}**. Do NOT run them. They've been excluded from the list below._\n`:""}\n## Test Commands${u?" (partial run \u2014 only the commands listed below)":""}\nExecute these in order:\n`
|
|
1019
|
+
for(let e=0;e<d.length;e++){const t=d[e]
|
|
1020
|
+
g+=`\n### ${e+1}. ${t.name}${!1!==t.required?" (REQUIRED)":" (optional)"}\n- **Command:** \`${t.command}\`\n- **Timeout:** ${t.timeout||300}s\n`}if(g+=s?`\n## Instructions\n\nYou are in interactive mode. Present the test plan to the user and **wait for their direction** before running anything.\n\n1. Greet the user and list the test commands available for this process.\n2. Wait for the user to tell you to proceed (they may want to run specific commands, skip some, or run all).\n3. Use \`execute_test_command\` with processId="${process.id}" and the command name to run each command.\n4. After each command, explain the result and wait for the user before continuing.\n5. When the user is satisfied, call \`report_test_results\` with the overall status.\n\nYou are read-only \u2014 do NOT modify any source code. Your job is to verify, not fix.`:`\n## Instructions\n\nSetup commands have already been executed. Use the \`execute_test_command\` tool to run each test command:\n\n1. For each command above, call \`execute_test_command\` with processId="${process.id}" and the command name.\n2. Check if it passed or failed.\n3. If it fails: try to determine why \u2014 is it a real bug, a flaky test, or an environment issue? Retry once if it looks flaky.\n4. When all commands are done, call \`report_test_results\` with the overall status and per-command results.\n - If all REQUIRED commands passed \u2192 status "passed"\n - If any REQUIRED command failed \u2192 status "failed" with diagnosis of each failure\n\nYou are read-only \u2014 do NOT modify any source code. Your job is to verify, not fix.`,i.verifyAcceptanceCriteria&&e.acceptanceCriteria?.length>0){g+="\n\n## Acceptance Criteria Verification\n\nAfter running all commands, verify each acceptance criterion by reading code changes and running ad-hoc commands as needed. This is **advisory only** \u2014 it does not affect the overall pass/fail status.\n\nCriteria to verify:\n"
|
|
1021
|
+
for(const t of e.acceptanceCriteria)g+=`- ${t}\n`
|
|
1022
|
+
g+='\nFor each criterion, report in the `acceptanceCriteria` array of your results:\n- **criterion**: The criterion text\n- **status**: "verified" (confirmed in code), "unverified" (not found/not implemented), or "unclear" (ambiguous)\n- **evidence**: How you verified it, or why you couldn\'t\n'}return g}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRunTestsAction=async function(e,l){const{requestId:d,testProcessIds:u,parentSessionId:h,mode:g}=e,p="interactive"===g,{logger:m,apiClient:f,projectId:y,projectPath:v,trackChildPid:w}=l
|
|
1025
1023
|
if(!d)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
1026
1024
|
m.info("Starting run-tests action",{requestId:d,testProcessIds:u})
|
|
1027
1025
|
try{const l=await f.get(`/api/projects/${y}/requests/${d}`)
|
|
@@ -1031,21 +1029,21 @@ let b
|
|
|
1031
1029
|
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))
|
|
1032
1030
|
else{const e=await f.get(`/api/projects/${y}/test-processes/active`)
|
|
1033
1031
|
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)),
|
|
1035
|
-
let
|
|
1036
|
-
try{
|
|
1032
|
+
const E=Math.max(1,Math.min(8,b[0]?.config?.poolSize??2)),C=b[0]?.config?.fingerprint||[]
|
|
1033
|
+
let S
|
|
1034
|
+
try{S=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=S.path,T=(0,n.simpleGit)(v)
|
|
1037
1035
|
try{if((0,s.existsSync)(k))try{const e=(0,n.simpleGit)(k)
|
|
1038
|
-
await e.raw(["reset","--hard",I]),m.info("Reset pool slot to latest branch HEAD",{worktreePath:k,branchName:I,slotIndex:
|
|
1036
|
+
await e.raw(["reset","--hard",I]),m.info("Reset pool slot to latest branch HEAD",{worktreePath:k,branchName:I,slotIndex:S.index})}catch(e){m.warn("Could not reset slot, recreating",{error:(0,i.getErrorMessage)(e)})
|
|
1039
1037
|
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})
|
|
1040
1038
|
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:
|
|
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,
|
|
1043
|
-
j||m.info("Pool slot fingerprint matches; skipping setup",{worktreePath:k,slotIndex:
|
|
1039
|
+
await T.raw(["worktree","add","--detach",k,I]),m.info("Created pool slot worktree (detached)",{worktreePath:k,branchName:I,slotIndex:S.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)}
|
|
1040
|
+
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,C),j=0===C.length||_!==S.storedFingerprint,A=0===C.length?null:_===S.storedFingerprint
|
|
1041
|
+
j||m.info("Pool slot fingerprint matches; skipping setup",{worktreePath:k,slotIndex:S.index,fingerprintFiles:C})
|
|
1044
1042
|
const{execSync:P}=await Promise.resolve().then(t.t.bind(t,5317,23))
|
|
1045
1043
|
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:
|
|
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)})}}}
|
|
1048
|
-
e>0&&m.info("Pruned drift from warm slot",{worktreePath:k,slotIndex:
|
|
1044
|
+
if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length,slotIndex:S.index})
|
|
1045
|
+
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)})}}}C.length>0&&await(0,c.writeSlotFingerprint)(k,_),await(0,c.writeWarmSnapshot)(k)}else{const{removed:e}=await(0,c.restoreToWarmSnapshot)(k)
|
|
1046
|
+
e>0&&m.info("Pruned drift from warm slot",{worktreePath:k,slotIndex:S.index,removed:e})}let M
|
|
1049
1047
|
try{const e=(0,n.simpleGit)(k)
|
|
1050
1048
|
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=[]
|
|
1051
1049
|
let x
|
|
@@ -1053,7 +1051,7 @@ const $={worktreePath:k,commitSha:M,fingerprintHit:A}
|
|
|
1053
1051
|
for(const process of b){let t
|
|
1054
1052
|
const s=e.testRunIds?.[O.length]
|
|
1055
1053
|
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)})
|
|
1056
|
-
continue}const o=r(g,process,0,y,p,e.commandNames),n=p?"tester":"auto-tester"
|
|
1054
|
+
continue}const o=r(g,process,0,y,p,e.commandNames,e.skipCommandNames),n=p?"tester":"auto-tester"
|
|
1057
1055
|
try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:o,cwd:k,projectPath:v,projectId:y,title:`[${d}] Test: ${process.name}`,requestId:d,parentSessionId:h,allowParallel:!0,apiClient:f,logger:m,trackChildPid:w})
|
|
1058
1056
|
if(!e.success)throw Error(e.error||"Spawn failed")
|
|
1059
1057
|
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)})
|
|
@@ -1181,7 +1179,7 @@ return t?t[1]:null}(d)
|
|
|
1181
1179
|
e&&l.push({eventType:"task_completed",timing:"after",variables:{...u,taskId:e}})}else if(p){const e=d.match(/(?:Created|Updated|Wrote)\s+(?:file\s+)?([^\s\n]+)/i)
|
|
1182
1180
|
l.push({eventType:"write_file",timing:"after",variables:{...u,path:e?.[1]||""}})}else if(m){const e=t(i?.operations)||[],r=(Array.isArray(e)?e:[]).map((e=>e.path||e.documentId)).filter(Boolean)
|
|
1183
1181
|
l.push({eventType:"replace_in_files",timing:"after",variables:{...u,paths:r.join(", "),fileCount:r.length+""}})}else if(f){const e={...u,path:i?.operationArgs?.path||"",title:i?.title||""},t=d.match(/\*\*Request:\*\*\s*(req\d+)/i)
|
|
1184
|
-
t&&(e.requestId=t[1]),l.push({eventType:"quick_edit",timing:"after",variables:e})
|
|
1182
|
+
t&&(e.requestId=t[1]),l.push({eventType:"quick_edit",timing:"after",variables:e})}else if(y){const e=t(i?.paths)||[],r=(Array.isArray(e)?e:[]).map((e=>e.path||e.documentId)).filter(Boolean)
|
|
1185
1183
|
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||"*"}})
|
|
1186
1184
|
else if(w){const e=t(i?.operations)||[],r=(Array.isArray(e)?e:[]).map((e=>e.operationType)).filter(Boolean)
|
|
1187
1185
|
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})
|
|
@@ -1409,8 +1407,8 @@ if(r?.success&&r?.data?.branchName)return r.data.branchName
|
|
|
1409
1407
|
if(r?.success&&r?.data)return"requests/"+s}catch(e){o.debug("resolveSetupBranch: request lookup failed, falling back to repo HEAD",{requestId:s,error:(0,a.getErrorMessage)(e)})}if(i)try{const e=(0,n.simpleGit)(i),t=(await e.revparse(["--abbrev-ref","HEAD"])).trim()
|
|
1410
1408
|
if(t&&"HEAD"!==t)return t}catch(e){o.debug("resolveSetupBranch: revparse HEAD failed, falling back to main",{error:(0,a.getErrorMessage)(e)})}return"main"},exports.createSetupWorktree=async function(e,t,o,i){(0,s.mkdirSync)(r(e),{recursive:!0})
|
|
1411
1409
|
const a=(0,n.simpleGit)(e)
|
|
1412
|
-
await a.raw(["worktree","add","--detach",t,o]),i.info("Created setup worktree",{worktreePath:t,branch:o})},exports.buildSetupPrompt=function(e,t){const r=["You are configuring the first test process for this project.","","Project path: "+e]
|
|
1413
|
-
return t&&
|
|
1410
|
+
await a.raw(["worktree","add","--detach",t,o]),i.info("Created setup worktree",{worktreePath:t,branch:o})},exports.buildSetupPrompt=function(e,t,r){const s=!(!r||!r.trim()),o=[s?"You are configuring an ADDITIONAL test process for this project (others already exist).":"You are configuring the first test process for this project.","","Project path: "+e]
|
|
1411
|
+
return t&&o.push("Triggered from request: "+t),s&&(o.push(""),o.push(r.trim())),o.push(""),o.push("1. Inspect manifests (package.json, pyproject.toml, Cargo.toml, go.mod, Makefile, CI configs) to identify build/test/lint commands."),o.push("2. Use `testing_reference` for per-language patterns."),o.push("3. Propose a draft to the user (name, setup commands, test commands with timeouts) as a single message."),o.push("4. Iterate until the user confirms."),o.push("5. Call `testing_process_create` with the agreed configuration."),o.push("6. Send a brief report back to your parent via `session_message_operations` (type: report) summarizing what you registered, then end your session."),o.push(""),o.push("You are read-only with respect to project files. Do not modify source code or run tests."),o.join("\n")},exports.buildCommandSetupPrompt=function(e,t,r){const s=["You are configuring fast project commands (typecheck, lint, build, format, dev server) for this project.","","Project path: "+e]
|
|
1414
1412
|
return t&&s.push("Triggered from request: "+t),r&&r.trim()&&(s.push(""),s.push("Caller hint: "+r.trim()),s.push("Treat the caller hint as guidance about what the user likely wants \u2014 still do your full discovery and confirm with the user before registering. Do not blindly register only what was hinted.")),s.push(""),s.push('1. Call `commands({ operation: "list" })` to see what is already registered.'),s.push("2. Inspect manifests at any depth (package.json, pyproject.toml, Cargo.toml, go.mod, Makefile, deno.json, CI configs)."),s.push("3. For monorepos: ask the user which contexts they want commands for. Propose one command per (context x kind) with the right cwd."),s.push("4. Propose a draft to the user (name, command, description, cwd, timeout) in a single message."),s.push("5. Iterate until the user confirms."),s.push('6. Call `commands({ operation: "register", commands: [...] })` to submit the agreed batch \u2014 each item creates a per-command approval notification the user must click through.'),s.push("7. Send a brief report back to your parent via `session_message_operations` (type: report) summarizing what you registered, then end your session."),s.push(""),s.push("You are read-only with respect to project files. Do not modify source code or run the commands."),s.join("\n")}
|
|
1415
1413
|
const s=t(9896),o=t(6928),n=t(3807),i=t(6982),a=t(8824),c=".alchemy_lab",l="setup-worktrees"},8524(e,exports,t){"use strict"
|
|
1416
1414
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
@@ -1547,13 +1545,13 @@ if(!i){try{const e=await r.readFile(w,"utf-8"),t=JSON.parse(e)
|
|
|
1547
1545
|
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)
|
|
1548
1546
|
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
1547
|
i?.openCodeModel?b.model=i.openCodeModel:p?b.model=p:f&&(b.model=f)
|
|
1550
|
-
const
|
|
1551
|
-
await r.mkdir(
|
|
1552
|
-
const
|
|
1548
|
+
const C=c.join(s,".opencode","agent")
|
|
1549
|
+
await r.mkdir(C,{recursive:!0})
|
|
1550
|
+
const S=E||"anthropic/claude-opus-4-5-20251101"
|
|
1553
1551
|
let k,T=null,_=null
|
|
1554
1552
|
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})
|
|
1555
1553
|
for(const e of T){if(!e.enabled)continue
|
|
1556
|
-
const t=this.generateAgentMarkdown(e,d,
|
|
1554
|
+
const t=this.generateAgentMarkdown(e,d,S,k),s=c.join(C,e.name+".md")
|
|
1557
1555
|
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")
|
|
1558
1556
|
else this.logger.warn("No projectId provided - agents must be configured via database")
|
|
1559
1557
|
const j={$schema:"https://opencode.ai/config.json",model:E,mcp:{[d]:{type:"local",command:[n,u,s],enabled:!0,timeout:6e5}}}
|
|
@@ -1566,7 +1564,7 @@ e.name in r&&(t.bash=!!r[e.name]),t.skill=!1,("subagent"===e.mode||"tester"===e.
|
|
|
1566
1564
|
let s=""
|
|
1567
1565
|
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}}
|
|
1568
1566
|
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`
|
|
1569
|
-
await r.writeFile(c.join(
|
|
1567
|
+
await r.writeFile(c.join(C,"explore.md"),e,"utf8"),this.logger.info("Created explore agent override with research prompt"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(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))):[]
|
|
1570
1568
|
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})
|
|
1571
1569
|
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")
|
|
1572
1570
|
let n=null
|
|
@@ -1689,12 +1687,12 @@ if(i.ok){const e=await i.json()
|
|
|
1689
1687
|
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
1688
|
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))
|
|
1691
1689
|
const i=process.argv.slice(2)
|
|
1692
|
-
let
|
|
1693
|
-
const
|
|
1694
|
-
const k=i.filter(((e,t)=>"--port"!==e&&(-1===
|
|
1690
|
+
let C
|
|
1691
|
+
const S=i.indexOf("--port");-1!==S&&i[S+1]&&(C=parseInt(i[S+1],10),isNaN(C)&&(console.error("\u274c Invalid port number"),process.exit(1)))
|
|
1692
|
+
const k=i.filter(((e,t)=>"--port"!==e&&(-1===S||t!==S+1)))
|
|
1695
1693
|
if(0===k.length)return void await async function(r={}){const i=process.cwd(),l=(0,h.join)(i,E.ALCHEMY_DIR_NAME)
|
|
1696
1694
|
await(0,u.mkdir)(l,{recursive:!0})
|
|
1697
|
-
const
|
|
1695
|
+
const C=(0,h.join)(l,"server.log"),{createLogger:S}=await Promise.resolve().then(e.t.bind(e,2782,23)),k=S({serviceName:"alchemy-server",console:!1,logToFile:!0,logFilePath:C,baseDir:i}),T=await a(k),_=new m.TokenManager(k,i,{remoteServerUrl:T,allowInteractiveAuth:!1})
|
|
1698
1696
|
await _.initialize()
|
|
1699
1697
|
let j=await _.getToken()
|
|
1700
1698
|
if(!j?.token){console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
|
|
@@ -1743,7 +1741,7 @@ u=setInterval((async()=>{if(!y)try{if(await _.hasValidToken())return
|
|
|
1743
1741
|
y=!0,console.log(""),console.log("\u26a0\ufe0f Auth token expired. Opening browser to re-authenticate...")
|
|
1744
1742
|
const e=await _.startAuthenticationFlow()
|
|
1745
1743
|
if(console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(_)){const e=await _.getToken()
|
|
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:
|
|
1744
|
+
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:C})
|
|
1747
1745
|
const T=k[0].toLowerCase()
|
|
1748
1746
|
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()
|
|
1749
1747
|
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))
|
|
@@ -1757,7 +1755,7 @@ const h=e.port||await(0,b.findAvailablePort)(3456),g=new y.AlchemyActionServer(o
|
|
|
1757
1755
|
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("")
|
|
1758
1756
|
let e=!1
|
|
1759
1757
|
const t=async()=>{e||(e=!0,console.log(""),console.log("Shutting down action server..."),await g.stop(),process.exit(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:
|
|
1758
|
+
process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){o.error("Failed to start server",{error:(0,I.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,I.getErrorMessage)(e)),process.exit(1)}}({port:C})
|
|
1761
1759
|
break
|
|
1762
1760
|
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})
|
|
1763
1761
|
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.")}()
|