alchemylab 1.3.4 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/60.js +13 -13
- package/dist/939.js +16 -13
- package/dist/cli.js +1279 -888
- package/dist/http-server.js +2164 -2305
- package/dist/index.js +2232 -2370
- package/dist/mcp-direct.js +2162 -2300
- package/dist/plugins/hooks/lib/dedup-utils.js +6 -14
- package/dist/plugins/hooks/lib/hook-utils.js +5 -12
- package/dist/plugins/hooks/plugin.js +47 -244
- package/dist/plugins/inject-session.js +8 -10
- package/dist/plugins/system-logger.js +9 -11
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,71 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
(()=>{function e(t){var
|
|
3
|
-
if(void 0!==
|
|
2
|
+
(()=>{function e(t){var r=n[t]
|
|
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,s,
|
|
5
|
+
return o[t].call(module.exports,module,module.exports,e),module.exports}var t,r,s,o={45(module){"use strict"
|
|
6
|
+
module.exports=require("alchemylab-opencode/package.json")},270(e,exports){"use strict"
|
|
7
|
+
Object.defineProperty(exports,"__esModule",{value:!0})},355(e,exports,t){"use strict"
|
|
8
|
+
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
|
+
try{const{eventType:r}=e.params,{sessionId:s}=e.query,a=i()
|
|
10
|
+
if(!a)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
11
|
+
n.info("Proxying event hooks request",{projectId:a,eventType:r,sessionId:s})
|
|
12
|
+
const c=s?"?sessionId="+encodeURIComponent(s):"",l=await o.get(`/api/projects/${a}/event-hooks/by-event/${r}${c}`)
|
|
13
|
+
t.json(l)}catch(e){n.error("Failed to fetch event hooks",{error:(0,r.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,r.getErrorMessage)(e)})}}(e,s,t))),e.post("/api/hook-triggers",((e,s)=>async function(e,t,s){const{apiClient:o,logger:n,getProjectId:i}=s
|
|
14
|
+
try{const r=i()
|
|
15
|
+
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
16
|
+
const{hookId:s,hookName:a,eventType:c,executionMode:l,agent:d}=e.body
|
|
17
|
+
n.debug("Proxying hook trigger notification",{projectId:r,hookName:a,eventType:c})
|
|
18
|
+
const u=await o.post(`/api/projects/${r}/hook-triggers`,{hookId:s,hookName:a,eventType:c,executionMode:l,agent:d})
|
|
19
|
+
t.json(u)}catch(e){n.debug("Failed to proxy hook trigger",{error:(0,r.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,r.getErrorMessage)(e)})}}(e,s,t))),e.get("/api/hook-iterations/:sessionId/:hookId",((e,s)=>async function(e,t,s){const{apiClient:o,logger:n,getProjectId:i}=s
|
|
20
|
+
try{const r=i()
|
|
21
|
+
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
22
|
+
const{sessionId:s,hookId:n}=e.params,a=await o.get(`/api/projects/${r}/hook-iterations/${s}/${n}`)
|
|
23
|
+
t.json(a)}catch(e){n.debug("Failed to get hook iteration",{error:(0,r.getErrorMessage)(e)}),t.json({success:!0,data:{iterationCount:0}})}}(e,s,t))),e.post("/api/hook-iterations/:sessionId/:hookId/increment",((e,s)=>async function(e,t,s){const{apiClient:o,logger:n,getProjectId:i}=s
|
|
24
|
+
try{const r=i()
|
|
25
|
+
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
26
|
+
const{sessionId:s,hookId:n}=e.params,a=await o.post(`/api/projects/${r}/hook-iterations/${s}/${n}/increment`,{})
|
|
27
|
+
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
|
+
const r=t(8824)},441(e,exports){"use strict"
|
|
29
|
+
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=C.shift()
|
|
31
|
+
e&&setImmediate(e)}async function o(e,t){const o=Date.now()
|
|
32
|
+
!function(e){let t=!1
|
|
33
|
+
for(const r of E.values())r.busy&&r.busySince&&e-r.busySince>b&&(r.busy=!1,r.busySince=void 0,t=!0)
|
|
34
|
+
t&&s()}(o)
|
|
35
|
+
for(let s=0;t>s;s++){const t=r(e,s)
|
|
36
|
+
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)
|
|
37
|
+
t&&t.busy&&(t.busy=!1,t.busySince=void 0,s())}function i(e,t){setTimeout((()=>n(e)),t).unref()}async function a(e){try{return(await d.promises.readFile((0,h.join)(e,f),"utf-8")).trim()||null}catch{return null}}async function c(e){const t=[]
|
|
38
|
+
return await async function r(s){const o=s?(0,h.join)(e,s):e
|
|
39
|
+
let n
|
|
40
|
+
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
|
+
!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=I){return await o(e,t)||new Promise(((i,a)=>{let c=!1
|
|
43
|
+
const l=Date.now(),d=setTimeout((()=>{if(c)return
|
|
44
|
+
c=!0
|
|
45
|
+
const e=C.indexOf(u)
|
|
46
|
+
0>e||C.splice(e,1),a(Error(`All ${t} pool slot(s) busy after waiting ${Math.round((Date.now()-l)/1e3)}s`))}),r)
|
|
47
|
+
d.unref()
|
|
48
|
+
const u=async()=>{if(c)return
|
|
49
|
+
const r=await o(e,t)
|
|
50
|
+
c?r&&n(r.path):r?(c=!0,clearTimeout(d),i(r)):(C.unshift(u),s())}
|
|
51
|
+
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
|
|
52
|
+
const o=setInterval((()=>{try{process.kill(e,0)}catch{n(t),clearInterval(o)}}),s)
|
|
53
|
+
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
|
+
const r=(0,u.createHash)("sha256")
|
|
55
|
+
for(const s of t)try{const t=await d.promises.readFile((0,h.join)(e,s),"utf-8")
|
|
56
|
+
r.update(s).update("\0").update(t).update("\n")}catch{r.update(s).update("\0:MISSING\n")}return r.digest("hex")},exports.listSlotFiles=c,exports.writeWarmSnapshot=async function(e){try{const t=await c(e)
|
|
57
|
+
await d.promises.writeFile((0,h.join)(e,y),t.join("\n")+"\n","utf-8")}catch(e){console.warn("worktree-pool: writeWarmSnapshot failed (non-fatal):",(0,g.getErrorMessage)(e))}},exports.readWarmSnapshot=l,exports.restoreToWarmSnapshot=async function(e){const t=await l(e)
|
|
58
|
+
if(!t)return{removed:0}
|
|
59
|
+
const r=await c(e)
|
|
60
|
+
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(),C.length=0}
|
|
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"]),b=12e5,I=6e5,E=new Map,C=[]},833(e,exports,t){"use strict"
|
|
63
|
+
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
64
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.getOpenCodeUrlFromConfig=function(e){const t=o.default.join(e,".alchemy_lab","opencode.json")
|
|
65
|
+
try{if(!s.default.existsSync(t))return null
|
|
66
|
+
const e=JSON.parse(s.default.readFileSync(t,"utf-8"))
|
|
67
|
+
if(e.port)return"http://127.0.0.1:"+e.port}catch{}return null}
|
|
68
|
+
const s=r(t(9896)),o=r(t(6928))},848(e,exports,t){"use strict"
|
|
6
69
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleFinalizeRequestAction=async function(e,t){const{requestId:o}=e,{logger:n,apiClient:i,projectPath:a,projectId:c}=t
|
|
7
70
|
if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
8
71
|
n.info("Starting finalize-request action",{requestId:o})
|
|
@@ -10,607 +73,702 @@ try{const e=await i.post(`/api/projects/${c}/requests/${o}/validate-completion`,
|
|
|
10
73
|
if(!e.success||!e.data)return{success:!1,message:"Server validation failed: "+(e.data?.reason||"Unknown error"),error:"Validation failed"}
|
|
11
74
|
const t=e.data
|
|
12
75
|
if(!t.canComplete){let e="Cannot finalize request: "+t.reason
|
|
13
|
-
return t.blockedByTasks?.length>0&&(e+="\n\nTasks that must be completed first:\n",t.blockedByTasks.forEach(((t,
|
|
14
|
-
if(g.current!==
|
|
15
|
-
let
|
|
76
|
+
return t.blockedByTasks?.length>0&&(e+="\n\nTasks that must be completed first:\n",t.blockedByTasks.forEach(((t,r)=>{e+=`${r+1}. ${t.taskId}: ${t.title} (${t.status})\n`}))),{success:!1,message:e,error:t.reason}}const l=await i.get(`/api/projects/${c}/requests/${o}`),d=l?.data,u="requests/"+o,h=(0,r.simpleGit)(a),g=await h.status(),p=await h.branch(),m=p.all.includes("main")?"main":"master"
|
|
77
|
+
if(g.current!==m&&await h.checkout(m),!p.all.includes(u))return{success:!1,message:`Branch '${u}' not found. The request may not have any file changes to merge.`,error:"Branch not found"}
|
|
78
|
+
let f,y=!1
|
|
16
79
|
try{const e=d?.title?`Merge ${u}-${d.title}`:"Merge "+u
|
|
17
80
|
await h.merge([u,"--no-ff","-m",e])
|
|
18
81
|
const t=await h.log({maxCount:1})
|
|
19
|
-
|
|
20
|
-
if(t.toLowerCase().includes("conflict")){try{await h.merge(["--abort"])}catch(e){n.error("CRITICAL: git merge --abort failed \u2014 repo may be in inconsistent state",{requestId:o,error:e instanceof Error?e.message:e+""})}return{success:!1,message:`Merge conflicts detected for request ${o}. Please resolve conflicts before finalizing.`,error:"Merge conflicts",data:{hasConflicts:!0}}}return{success:!1,message:"Merge failed: "+t,error:t}}try{await h.deleteLocalBranch(u,!0),y=!0}catch{}try{await i.post(`/api/projects/${c}/requests/${o}/finalize-completion`,{summary:"Finalized request "+o,acceptanceEvidence:[{evidence:"Finalized via GUI after review"}],mergeResult:{mergeCommitHash:
|
|
21
|
-
return n.error("CRITICAL: Merge succeeded but server finalization failed",{requestId:o,mergeCommitHash:
|
|
82
|
+
f=t.latest?.hash,n.info("Git merge completed",{requestId:o,mergeCommit:f?.substring(0,8)})}catch(e){const t=(0,s.getErrorMessage)(e)
|
|
83
|
+
if(t.toLowerCase().includes("conflict")){try{await h.merge(["--abort"])}catch(e){n.error("CRITICAL: git merge --abort failed \u2014 repo may be in inconsistent state",{requestId:o,error:e instanceof Error?e.message:e+""})}return{success:!1,message:`Merge conflicts detected for request ${o}. Please resolve conflicts before finalizing.`,error:"Merge conflicts",data:{hasConflicts:!0}}}return{success:!1,message:"Merge failed: "+t,error:t}}try{await h.deleteLocalBranch(u,!0),y=!0}catch{}try{await i.post(`/api/projects/${c}/requests/${o}/finalize-completion`,{summary:"Finalized request "+o,acceptanceEvidence:[{evidence:"Finalized via GUI after review"}],mergeResult:{mergeCommitHash:f||"unknown",cleanedUp:y,method:"action-server-merge"}})}catch(e){const t=(0,s.getErrorMessage)(e)
|
|
84
|
+
return n.error("CRITICAL: Merge succeeded but server finalization failed",{requestId:o,mergeCommitHash:f,error:t}),{success:!1,message:`Code was merged (commit: ${f?.substring(0,8)}) but server finalization failed: ${t}`,error:t,data:{mergeCommitHash:f,partialSuccess:!0}}}return{success:!0,message:`Successfully finalized request ${o}: ${d?.title||o}`,data:{mergeCommitHash:f,branchDeleted:y}}}catch(e){const t=(0,s.getErrorMessage)(e)
|
|
22
85
|
return n.error("finalize-request action failed",{requestId:o,error:t}),{success:!1,message:"Failed to finalize request: "+t,error:t}}}
|
|
23
|
-
const
|
|
86
|
+
const r=t(3807),s=t(8824)},857(module){"use strict"
|
|
24
87
|
module.exports=require("os")},1152(e,exports){"use strict"
|
|
25
|
-
function t(e=!0){return new
|
|
26
|
-
class
|
|
27
|
-
return
|
|
28
|
-
const
|
|
29
|
-
this.counters.set(
|
|
30
|
-
const
|
|
31
|
-
this.gauges.set(
|
|
32
|
-
const
|
|
33
|
-
o.push(t),this.histograms.set(
|
|
88
|
+
function t(e=!0){return new r(e)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.createMetricsCollector=t,exports.createNullMetricsCollector=function(){return t(!1)}
|
|
89
|
+
class r{constructor(e=!0){this.enabled=e,this.counters=new Map,this.gauges=new Map,this.histograms=new Map}getKey(e,t){const r=Object.entries(t).map((([e,t])=>`${e}="${t+""}"`)).join(",")
|
|
90
|
+
return r?`${e}{${r}}`:e}incrementCounter(e,t=1,r={}){if(!this.enabled)return
|
|
91
|
+
const s=this.getKey(e,r),o=this.counters.get(s)||0
|
|
92
|
+
this.counters.set(s,o+t)}setGauge(e,t,r={}){if(!this.enabled)return
|
|
93
|
+
const s=this.getKey(e,r)
|
|
94
|
+
this.gauges.set(s,t)}observeHistogram(e,t,r={}){if(!this.enabled)return
|
|
95
|
+
const s=this.getKey(e,r),o=this.histograms.get(s)||[]
|
|
96
|
+
o.push(t),this.histograms.set(s,o)}recordValue(e,t,r={}){this.observeHistogram(e,t,r)}startTimer(e){if(!this.enabled)return()=>{}
|
|
34
97
|
const t=Date.now()
|
|
35
|
-
return(
|
|
36
|
-
this.observeHistogram(e,r
|
|
37
|
-
|
|
98
|
+
return(r={})=>{const s=Date.now()-t
|
|
99
|
+
this.observeHistogram(e,s,r)}}getMetrics(){return{counters:Object.fromEntries(this.counters),gauges:Object.fromEntries(this.gauges),histograms:Object.fromEntries(this.histograms)}}reset(){this.counters.clear(),this.gauges.clear(),this.histograms.clear()}}},1187(e,exports,t){"use strict"
|
|
100
|
+
function r(){const e=n.default.homedir(),t=o.default.join(e,".alchemy_lab","opencode","config","opencode.json")
|
|
101
|
+
let r=null
|
|
102
|
+
try{const e=JSON.parse(i.default.readFileSync(t,"utf-8"))
|
|
103
|
+
Array.isArray(e.plugin)&&(r=e.plugin.find((e=>"string"==typeof e&&/^opencode-claude-auth(@|$)/.test(e)))||null)}catch{r=null}const s=o.default.join(e,".cache","opencode","packages"),a=r?o.default.join(s,r):null,c=!!a&&i.default.existsSync(a)
|
|
104
|
+
let l=null,d=!1
|
|
105
|
+
if(a){const e=o.default.join(a,"node_modules","opencode-claude-auth","package.json")
|
|
106
|
+
if(d=i.default.existsSync(e),d)try{const t=JSON.parse(i.default.readFileSync(e,"utf-8"))
|
|
107
|
+
l="string"==typeof t.version?t.version:null}catch{l=null}}return{at:(new Date).toISOString(),pinnedSpec:r,expectedDir:a,dirExists:c,installedPkgJsonExists:d,installedVersion:l}}var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
38
108
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.UnifiedLauncher=void 0
|
|
39
|
-
const
|
|
40
|
-
exports.UnifiedLauncher=class{constructor(e,t,
|
|
41
|
-
this.logger.info("UnifiedLauncher: Starting launch",{projectPath:t,installPath:
|
|
42
|
-
try{const
|
|
43
|
-
return
|
|
44
|
-
try{(0,
|
|
45
|
-
const{resolveServeCommand:
|
|
46
|
-
return new Promise((e=>{const t=
|
|
47
|
-
let
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
109
|
+
const o=s(t(6928)),n=s(t(857)),i=s(t(9896)),a=t(3962),c=t(5419),l=t(5317),d=t(8824)
|
|
110
|
+
exports.UnifiedLauncher=class{constructor(e,t,r){this.logger=e,this.installContext=t,this.configurer=r}async launch(e){const{projectPath:t,settings:s,multiAgent:o}=e,{installPath:n,nodePath:i}=this.installContext
|
|
111
|
+
this.logger.info("UnifiedLauncher: Starting launch",{projectPath:t,installPath:n})
|
|
112
|
+
try{const a=await this.configurer.configureOpenCode(n,t,i,s,o)
|
|
113
|
+
return a.success?(this.logger.info("UnifiedLauncher: Configuration complete",{configPath:a.configPath}),this.logger.info("[plugin-diag] pre-spawn",r()),this.launchBackground(e,a)):(this.logger.error("UnifiedLauncher: Configuration failed",{error:a.error}),{success:!1,error:a.error})}catch(e){return this.logger.error("UnifiedLauncher: Launch failed",{error:(0,d.getErrorMessage)(e)}),{success:!1,error:(0,d.getErrorMessage)(e)}}}async launchBackground(e,s){const{projectPath:n,envVars:u}=e
|
|
114
|
+
try{(0,l.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{return{success:!1,error:"OpenCode not found in PATH."}}this.logger.info("UnifiedLauncher: Launching opencode serve in background",{command:"opencode serve",cwd:n})
|
|
115
|
+
const{resolveServeCommand:h}=await Promise.resolve().then(t.bind(t,9068)),g=h()
|
|
116
|
+
return new Promise((e=>{const t=o.default.join(n,".alchemy_lab","opencode"),h=o.default.join(t,"log")
|
|
117
|
+
let p=null
|
|
118
|
+
const m=e=>{if(!p)return
|
|
119
|
+
const t=p
|
|
120
|
+
p=null,t.end(e)}
|
|
121
|
+
try{i.default.mkdirSync(h,{recursive:!0})
|
|
122
|
+
const e=o.default.join(h,"opencode-serve.log")
|
|
123
|
+
i.default.writeFileSync(e,`# opencode-serve.log \u2014 launched ${(new Date).toISOString()}\n`),p=i.default.createWriteStream(e,{flags:"a"})}catch(e){this.logger.warn("UnifiedLauncher: Could not open opencode log file",{error:(0,d.getErrorMessage)(e)})}const f=(0,l.spawn)(g.command,[...g.prependArgs,"--port","0","--data-dir",t],{stdio:["ignore","pipe","pipe"],cwd:n,detached:!0,env:{...process.env,...(0,c.getOpencodeSpawnEnv)(n,(0,a.isDevMode)()),...u}})
|
|
124
|
+
let y="",v=!1
|
|
125
|
+
const w=t=>{y+=t
|
|
126
|
+
const o=y.match(/listening on http:\/\/[^:]+:(\d+)/)
|
|
127
|
+
if(o&&!v){const t=parseInt(o[1],10)
|
|
128
|
+
v=!0,this.logger.info("UnifiedLauncher: Discovered opencode serve port",{pid:f.pid,port:t}),this.logger.info("[plugin-diag] port-ready",{pid:f.pid,port:t,...r()}),setTimeout((()=>{this.logger.info("[plugin-diag] port-ready+5s",{pid:f.pid,...r()})}),5e3).unref(),setTimeout((()=>{this.logger.info("[plugin-diag] port-ready+15s",{pid:f.pid,...r()})}),15e3).unref(),f.unref(),e({success:!0,pid:f.pid,port:t,configPath:s.configPath})}}
|
|
129
|
+
f.stdout?.on("data",(e=>{p&&p.write(e),w(e.toString())})),f.stderr?.on("data",(e=>{if(p){const t=e.toString().split("\n")
|
|
130
|
+
for(let e=0;e<t.length;e++){const r=t[e]
|
|
131
|
+
0===r.length&&e===t.length-1||p.write(`[stderr] ${r}\n`)}}w(e.toString())})),f.on("exit",((e,t)=>{m(`\n# opencode exited code=${e} signal=${t} at=${(new Date).toISOString()}\n`)})),setTimeout((()=>{v||(v=!0,this.logger.warn("UnifiedLauncher: Timeout waiting for opencode serve port",{outputBuffer:y.slice(0,500)}),f.unref(),e({success:!0,pid:f.pid,configPath:s.configPath,error:"Could not determine port from opencode serve output"}))}),1e4),f.on("error",(t=>{v||(v=!0,this.logger.error("UnifiedLauncher: opencode serve failed to start",{error:t.message}),m(`\n# child error: ${t.message}\n`),e({success:!1,error:"Failed to start opencode serve: "+t.message}))})),f.on("exit",(t=>{v||(v=!0,this.logger.error("UnifiedLauncher: opencode serve exited early",{code:t,output:y.slice(0,500)}),e({success:!1,error:"opencode serve exited with code "+t}))}))}))}}},1253(e,exports,t){"use strict"
|
|
132
|
+
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
54
133
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.TokenManager=void 0
|
|
55
|
-
const r
|
|
56
|
-
exports.TokenManager=class{constructor(e,t,
|
|
57
|
-
return e.includes("node_modules")?
|
|
58
|
-
try{const e=t(9896).readFileSync(
|
|
134
|
+
const s=r(t(6928)),o=r(t(1943)),n=r(t(6982)),i=r(t(857)),a=t(9067),c=t(8824),l="alchemy-lab"
|
|
135
|
+
exports.TokenManager=class{constructor(e,t,r={}){this.projectId=null,this.encryptionKey=null,this.pendingAuthCheck=!1,this.tempAuthCode=null,this.CACHE_TTL_MS=6e4,this.TOKEN_CACHE={manualToken:null,encryptedToken:null,hasValidToken:null},this.initPromise=null,this.initialized=!1,this.logger=e.child({component:"token-manager"}),this.remoteServerUrl=r.remoteServerUrl||"https://api.alchemylabcode.com",this.allowInteractiveAuth=!1!==r.allowInteractiveAuth,void 0!==r.cacheTtlMs&&r.cacheTtlMs>0&&(this.CACHE_TTL_MS=r.cacheTtlMs),this.serverInstallDir=this.getModuleDir(),this.tokenDirPath=this.serverInstallDir,this.authFlow=new a.ExternalAuthFlow(e,this.remoteServerUrl),this.logger.info("TokenManager initialized",{workingDir:t,serverInstallDir:this.serverInstallDir,tokenDirPath:this.tokenDirPath,allowInteractiveAuth:this.allowInteractiveAuth,cacheTtlMs:this.CACHE_TTL_MS}),this.generateEncryptionKey()}getModuleDir(){const e=__dirname||process.cwd(),t=i.default.homedir()
|
|
136
|
+
return e.includes("node_modules")?s.default.join(t,".alchemy_lab","tokens"):s.default.join(t,".alchemy_lab","tokens-dev")}async initialize(){return this.initPromise||(this.initPromise=this.ensureTokenDirectoryExists().then((()=>{this.initialized=!0,this.logger.info("TokenManager initialization completed")})).catch((e=>{throw this.logger.error("TokenManager initialization failed",{error:e instanceof Error?e.stack:e+""}),Error("TokenManager initialization failed")}))),this.initPromise}getStableMachineId(){try{const e=t(857),r=s.default.join(this.serverInstallDir,".machine-id")
|
|
137
|
+
try{const e=t(9896).readFileSync(r,"utf8").trim()
|
|
59
138
|
if(e&&e.length>0)return e}catch{}const o=e.platform(),i=e.arch(),a=e.homedir(),c=e.cpus(),l=`${o}-${i}-${c&&c.length>0?c[0].model:"unknown"}-${a}`,d=n.default.createHash("sha256").update(l).digest("hex")
|
|
60
|
-
try{t(9896).writeFileSync(
|
|
61
|
-
this.encryptionKey=n.default.createHash("sha256").update(
|
|
62
|
-
return n.default.createHash("sha256").update(
|
|
63
|
-
return
|
|
139
|
+
try{t(9896).writeFileSync(r,d,"utf8"),this.logger.debug("Created new machine ID file",{path:r})}catch(e){this.logger.warn("Could not persist machine ID, will regenerate on restart",{error:e instanceof Error?e.message:e+""})}return d}catch(e){return this.logger.error("Error getting stable machine ID",{error:(0,c.getErrorMessage)(e)}),n.default.randomBytes(32).toString("hex")}}generateEncryptionKey(){try{const e=t(857).platform(),r=this.getStableMachineId(),s=`${l}-${e}-${r}`
|
|
140
|
+
this.encryptionKey=n.default.createHash("sha256").update(s).digest(),this.logger.debug("Generated encryption key using stable machine ID")}catch(e){this.logger.error("Error generating encryption key",{error:(0,c.getErrorMessage)(e)}),this.encryptionKey=Buffer.from(l.repeat(4),"utf8")}}generateLegacyEncryptionKey(){try{const e=t(857).platform(),r=t(857).hostname(),s=`${l}-${e}-${r}`
|
|
141
|
+
return n.default.createHash("sha256").update(s).digest()}catch(e){return this.logger.error("Error generating legacy encryption key",{error:(0,c.getErrorMessage)(e)}),Buffer.from(l.repeat(4),"utf8")}}async ensureTokenDirectoryExists(){try{await o.default.mkdir(this.tokenDirPath,{recursive:!0}),this.logger.debug("Token directory ensured exists",{path:this.tokenDirPath})}catch(e){throw this.logger.error("Error creating token directory",{path:this.tokenDirPath,error:(0,c.getErrorMessage)(e)}),Error("Error creating token directory: "+this.tokenDirPath)}}setProjectId(e){this.projectId=e,this.logger.info("Set project ID for token manager",{projectId:e})}getAccountKey(){return"default-user-"+n.default.createHash("sha256").update(this.remoteServerUrl).digest("hex").substring(0,8)}getTokenFilePath(e){const t=e.replace(/[^a-zA-Z0-9]/g,"_")
|
|
142
|
+
return s.default.join(this.tokenDirPath,t+".token")}invalidateCache(){this.logger.debug("Invalidating all token caches"),this.TOKEN_CACHE.manualToken=null,this.TOKEN_CACHE.encryptedToken=null,this.TOKEN_CACHE.hasValidToken=null}async checkForManualToken(){const e=Date.now(),t=s.default.join(this.tokenDirPath,"manual-token.json")
|
|
64
143
|
if(this.TOKEN_CACHE.manualToken&&e<this.TOKEN_CACHE.manualToken.expiresAt){const t=this.TOKEN_CACHE.manualToken
|
|
65
144
|
return this.logger.debug("Using cached manual token check result",{ageSecs:Math.round((e-t.timestamp)/1e3),exists:t.exists,hasToken:null!==t.value}),t.exists&&t.value?t.value:null}this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Checking for manual token file",{tokenFilePath:t})
|
|
66
|
-
try{let
|
|
67
|
-
try{await o.default.stat(t),
|
|
68
|
-
new Date(n.expiresAt)>new Date?(
|
|
69
|
-
const t=n.default.randomBytes(16),
|
|
70
|
-
let r
|
|
71
|
-
return r
|
|
145
|
+
try{let r=!1,s=null
|
|
146
|
+
try{await o.default.stat(t),r=!0,this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Manual token file exists")}catch{this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Manual token file does not exist")}if(r)try{const r=await o.default.readFile(t,"utf8"),n=JSON.parse(r)
|
|
147
|
+
new Date(n.expiresAt)>new Date?(s=n,this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Valid manual token found",{username:s.username,expiresAt:s.expiresAt})):this.logger.debug("Manual token has expired",{expiresAt:n.expiresAt})}catch(e){this.logger.error("Error parsing manual token file",{error:e instanceof Error?e.message:e+""})}return this.TOKEN_CACHE.manualToken={value:s,timestamp:e,expiresAt:e+this.CACHE_TTL_MS,lastCheck:e,exists:r},s}catch(e){return this.logger.error("Error checking for manual token",{error:(0,c.getErrorMessage)(e)}),null}}encrypt(e){if(!this.encryptionKey)throw Error("Encryption key not available")
|
|
148
|
+
const t=n.default.randomBytes(16),r=n.default.createCipheriv("aes-256-cbc",this.encryptionKey,t)
|
|
149
|
+
let s=r.update(e,"utf8","hex")
|
|
150
|
+
return s+=r.final("hex"),`${t.toString("hex")}:${s}`}decrypt(e){if(!this.encryptionKey)throw Error("Encryption key not available")
|
|
72
151
|
try{return this.decryptWithKey(e,this.encryptionKey)}catch(t){this.logger.warn("Failed to decrypt with current key, trying legacy hostname-based key",{error:t instanceof Error?t.message:t+""})
|
|
73
|
-
try{const t=this.generateLegacyEncryptionKey(),
|
|
74
|
-
return this.logger.info("Successfully decrypted with legacy key, will re-encrypt with new key on next store"),
|
|
75
|
-
const
|
|
76
|
-
try{o.default.rename(
|
|
77
|
-
if(!s
|
|
78
|
-
const o=Buffer.from(
|
|
79
|
-
let a=i.update(
|
|
152
|
+
try{const t=this.generateLegacyEncryptionKey(),r=this.decryptWithKey(e,t)
|
|
153
|
+
return this.logger.info("Successfully decrypted with legacy key, will re-encrypt with new key on next store"),r}catch(e){this.logger.error("Failed to decrypt with both current and legacy keys",{primaryError:t instanceof Error?t.message:t+"",legacyError:e instanceof Error?e.message:e+""})
|
|
154
|
+
const r=this.getAccountKey(),s=this.getTokenFilePath(r)
|
|
155
|
+
try{o.default.rename(s,s+".corrupt").catch((()=>this.logger.warn("Could not backup corrupted token file")))}catch(e){this.logger.error("Error backing up corrupted file: "+e)}throw Error("Could not decrypt token file with current or legacy key - it may be corrupted")}}}decryptWithKey(e,t){try{const[r,s]=e.split(":")
|
|
156
|
+
if(!r||!s)throw Error("Invalid encrypted data format")
|
|
157
|
+
const o=Buffer.from(r,"hex"),i=n.default.createDecipheriv("aes-256-cbc",t,o)
|
|
158
|
+
let a=i.update(s,"hex","utf8")
|
|
80
159
|
return a+=i.final("utf8"),a}catch(e){throw e}}async storeToken(e){!this.initPromise&&this.initialized?await this.initPromise:this.initialized||await this.initialize()
|
|
81
|
-
try{const t=this.getAccountKey(),
|
|
82
|
-
await o.default.writeFile(
|
|
160
|
+
try{const t=this.getAccountKey(),r=this.getTokenFilePath(t),s=JSON.stringify(e),n=this.encrypt(s)
|
|
161
|
+
await o.default.writeFile(r,n,"utf8"),this.logger.info("Token stored securely",{username:e.username,expiresAt:e.expiresAt}),this.invalidateCache()}catch(e){throw this.logger.error("Error storing token",{error:(0,c.getErrorMessage)(e)}),Error("Failed to store token securely")}}async getToken(){!this.initPromise&&this.initialized?await this.initPromise:this.initialized||await this.initialize()
|
|
83
162
|
try{if(this.hasPendingAuth()&&this.tempAuthCode){const e=await this.checkAuthStatus(this.tempAuthCode)
|
|
84
163
|
if(e)return e}const e=await this.checkForManualToken()
|
|
85
164
|
if(e)return e
|
|
86
165
|
const t=Date.now()
|
|
87
166
|
if(this.TOKEN_CACHE.encryptedToken&&t<this.TOKEN_CACHE.encryptedToken.expiresAt){const e=this.TOKEN_CACHE.encryptedToken
|
|
88
|
-
return this.logger.debug("Using cached encrypted token",{ageSecs:Math.round((t-e.timestamp)/1e3),hasToken:null!==e.value}),e.value}const
|
|
89
|
-
try{await o.default.stat(
|
|
90
|
-
return this.logger.info("Token expiration check details",{tokenExpiresAt:
|
|
91
|
-
try{const e=
|
|
92
|
-
await o.default.rename(
|
|
167
|
+
return this.logger.debug("Using cached encrypted token",{ageSecs:Math.round((t-e.timestamp)/1e3),hasToken:null!==e.value}),e.value}const r=this.getAccountKey(),s=this.getTokenFilePath(r)
|
|
168
|
+
try{await o.default.stat(s)}catch{return this.logger.debug("No encrypted token file found"),this.TOKEN_CACHE.encryptedToken={value:null,timestamp:t,expiresAt:t+this.CACHE_TTL_MS,lastCheck:t,exists:!1},this.allowInteractiveAuth&&!this.hasPendingAuth()?(this.logger.debug("No stored token found, starting authentication flow"),this.startAuthenticationFlow().catch((e=>{this.logger.error("Failed to start authentication flow",{error:e instanceof Error?e.message:e+""})}))):this.logger.debug("Interactive authentication disabled or already pending"),null}try{const e=await o.default.readFile(s,"utf8"),t=this.decrypt(e),r=JSON.parse(t),n=new Date(r.expiresAt),i=new Date,a=n.getTime()-i.getTime(),c=a/36e5
|
|
169
|
+
return this.logger.info("Token expiration check details",{tokenExpiresAt:r.expiresAt,currentTime:i.toISOString(),isExpired:i>=n,timeUntilExpiryMs:a,timeUntilExpiryHours:Math.round(100*c)/100,username:r.username,systemTimezone:Intl.DateTimeFormat().resolvedOptions().timeZone}),n>i?(this.logger.info("Retrieved valid token from storage",{username:r.username,expiresAt:r.expiresAt}),this.TOKEN_CACHE.encryptedToken={value:r,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!0},r):(this.logger.info("Stored token has expired",{expiresAt:r.expiresAt,expiredByMs:i.getTime()-n.getTime(),expiredByHours:Math.round((i.getTime()-n.getTime())/36e5*100)/100}),this.TOKEN_CACHE.encryptedToken={value:null,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!1},null)}catch(e){this.logger.error("Error reading/decrypting token file",{error:(0,c.getErrorMessage)(e),path:s})
|
|
170
|
+
try{const e=s+".backup"
|
|
171
|
+
await o.default.rename(s,e),this.logger.info("Moved corrupted token file to backup",{from:s,to:e})}catch(e){this.logger.error("Error creating backup of corrupted token file",{error:e instanceof Error?e.message:e+""})}return this.TOKEN_CACHE.encryptedToken={value:null,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!1},null}}catch(e){return this.logger.error("Error retrieving token",{error:(0,c.getErrorMessage)(e)}),null}}async removeToken(){!this.initPromise&&this.initialized?await this.initPromise:this.initialized||await this.initialize()
|
|
93
172
|
try{const e=this.getAccountKey(),t=this.getTokenFilePath(e)
|
|
94
173
|
try{await o.default.stat(t)}catch{return this.logger.info("No token file to remove"),!1}return await o.default.unlink(t),this.logger.info("Token removed from secure storage"),this.invalidateCache(),!0}catch(e){return this.logger.error("Error removing token",{error:(0,c.getErrorMessage)(e)}),!1}}async hasValidToken(){const e=Date.now()
|
|
95
174
|
if(this.TOKEN_CACHE.hasValidToken&&e<this.TOKEN_CACHE.hasValidToken.expiresAt){const t=this.TOKEN_CACHE.hasValidToken
|
|
96
175
|
return this.logger.debug("Using cached hasValidToken result",{ageSecs:Math.round((e-t.timestamp)/1e3),hasToken:t.value}),t.value}this.logger.debug("Checking if valid token exists (cache miss or expired)")
|
|
97
176
|
const t=null!==await this.getToken()
|
|
98
|
-
return this.TOKEN_CACHE.hasValidToken={value:t,timestamp:e,expiresAt:e+this.CACHE_TTL_MS,lastCheck:e,exists:t},t}getTokenDirectoryPath(){return this.tokenDirPath}getManualTokenFilePath(){return
|
|
177
|
+
return this.TOKEN_CACHE.hasValidToken={value:t,timestamp:e,expiresAt:e+this.CACHE_TTL_MS,lastCheck:e,exists:t},t}getTokenDirectoryPath(){return this.tokenDirPath}getManualTokenFilePath(){return s.default.join(this.tokenDirPath,"manual-token.json")}logTokenLocationInstructions(){const e=this.getManualTokenFilePath()
|
|
99
178
|
console.log("========================================"),console.log("To use the alchemy CLI, you need to authenticate."),console.log("Token file location: "+e),console.log("You can either:"),console.log('\n1. Use the authenticate tool (recommended)\n2. Manually place a token file at the above location\n3. Set up automatic authentication flow\n\nFor manual token placement, create a JSON file with:\n{\n "token": "your_token_here",\n "userId": "your_user_id",\n "username": "your_username", \n "role": "user",\n "expiresAt": "2025-12-31T23:59:59.000Z"\n}\n'),console.log("For more information, see the authentication documentation."),console.log("========================================")}async startAuthenticationFlow(){this.pendingAuthCheck=!0
|
|
100
179
|
try{const{tempCode:e,authUrl:t}=await this.authFlow.startAuthFlow()
|
|
101
180
|
return this.tempAuthCode=e,this.logger.info("Started external authentication flow",{tempCode:e,authUrl:t}),this.authFlow.startPolling(e,(async t=>{this.logger.info("Authentication flow completed",{tempCode:e.substring(0,4)+"***",username:t.username}),await this.storeToken(t),this.pendingAuthCheck=!1,this.tempAuthCode=null,this.invalidateCache(),console.log("Authentication successful!"),console.log(`Welcome, ${t.username}!`),console.log("You can now use the alchemy CLI."),console.log("========================================")}),(()=>{this.logger.info("Authentication flow timed out",{tempCode:e.substring(0,4)+"***"}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow timed out."),console.log("Please try again or manually place a token file."),console.log("Token file location:"),console.log(""+this.getManualTokenFilePath()),console.log("========================================")}),(t=>{this.logger.error("Authentication flow error",{tempCode:e.substring(0,4)+"***",error:t}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow error."),console.log("Error: "+t),console.log("Please try again or manually place a token file."),console.log(""+this.getManualTokenFilePath()),console.log("========================================")})),{tempCode:e,authUrl:t}}catch(e){throw this.logger.error("Error starting authentication flow",{error:(0,c.getErrorMessage)(e)}),this.pendingAuthCheck=!1,this.tempAuthCode=null,e}}hasPendingAuth(){return this.pendingAuthCheck||!!this.tempAuthCode}getAuthStatus(){return this.hasPendingAuth()?{hasToken:!1,isPendingAuth:!0,tempCode:this.tempAuthCode,authUrl:this.authFlow.getAuthUrl(this.tempAuthCode)}:{hasToken:!1,isPendingAuth:!1}}async checkAuthStatus(e){if(!e)return null
|
|
102
181
|
try{const t=this.authFlow.getPollingStatus()
|
|
103
182
|
if(t.status===a.PollingStatus.AUTHENTICATED&&t.tokenInfo)return this.logger.info("Authentication polling has token available",{tempCode:e.substring(0,4)+"***",username:t.tokenInfo.username}),t.tokenInfo
|
|
104
183
|
if(t.status===a.PollingStatus.ERROR||t.status===a.PollingStatus.TIMEOUT)return this.pendingAuthCheck=!1,this.tempAuthCode=null,null
|
|
105
|
-
const
|
|
106
|
-
if(
|
|
184
|
+
const r=this.authFlow.getPollingUrl(e),s=await fetch(r)
|
|
185
|
+
if(s.ok){const e=await s.json()
|
|
107
186
|
if(e.authenticated&&e.token){const t=this.authFlow.parseTokenResponse(e)
|
|
108
187
|
if(t)return await this.storeToken(t),this.pendingAuthCheck=!1,this.tempAuthCode=null,this.invalidateCache(),t}}return null}catch(e){return this.logger.error("Error checking authentication status",{error:(0,c.getErrorMessage)(e)}),this.pendingAuthCheck=!1,this.tempAuthCode=null,null}}async validateTokenWithRemoteServer(){this.logger.debug("Validating stored token with remote server")
|
|
109
188
|
try{const e=await this.getToken()
|
|
110
189
|
if(!e)return this.logger.debug("No token available for validation"),!1
|
|
111
190
|
const t=this.remoteServerUrl+"/auth/validate"
|
|
112
191
|
this.logger.debug("Making token validation request",{validationUrl:t})
|
|
113
|
-
const
|
|
114
|
-
if(
|
|
115
|
-
return this.logger.info("Token validation successful",{status:e.status||"unknown",username:e.username||"unknown"}),!0}return this.logger.info("Token validation failed - token may be invalid or expired",{status:
|
|
192
|
+
const r=await fetch(t,{method:"GET",headers:{Authorization:"Bearer "+e.token,"Content-Type":"application/json"},signal:AbortSignal.timeout(1e4)})
|
|
193
|
+
if(r.ok){const e=await r.json()
|
|
194
|
+
return this.logger.info("Token validation successful",{status:e.status||"unknown",username:e.username||"unknown"}),!0}return this.logger.info("Token validation failed - token may be invalid or expired",{status:r.status,statusText:r.statusText}),this.invalidateCache(),!1}catch(e){return e instanceof Error&&"AbortError"===e.name?(this.logger.info("Token validation request timed out - assuming offline or server unavailable"),!0):(this.logger.info("Token validation request failed",{error:(0,c.getErrorMessage)(e)}),!0)}}async hasValidAndWorkingToken(){return!!await this.hasValidToken()&&await this.validateTokenWithRemoteServer()}async isGoogleOAuthAvailable(){return await this.authFlow.isGoogleOAuthAvailable()}async startGoogleOAuthFlow(){return await this.authFlow.startGoogleOAuthFlow()}async startGoogleOAuthPolling(e,t,r,s,o){return await this.authFlow.startGoogleOAuthPolling(e,t,r,s,o)}}},1292(e,exports,t){"use strict"
|
|
116
195
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.SessionSpawnService=void 0
|
|
117
|
-
const
|
|
118
|
-
exports.SessionSpawnService=class{constructor(e){this.deps=e}getOpenCodeServerUrl(e){
|
|
119
|
-
if(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const f=await fetch(h+"/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:g,...a?{parentID:a}:{},metadata:p})})
|
|
196
|
+
const r=t(2424),s=t(833),o=t(8824),n=t(5671),i=t(8955)
|
|
197
|
+
exports.SessionSpawnService=class{constructor(e){this.deps=e}getOpenCodeServerUrl(e){return(0,s.getOpenCodeUrlFromConfig)(e)}async prepareSession(e){const{agent:t,prompt:s,projectPath:n,requestId:i,parentSessionId:a,specialization:c,specializationHeadline:l,callerAgentType:d}=e,{logger:u}=this.deps,h=this.deps.projectId,g=this.getOpenCodeServerUrl(n)
|
|
198
|
+
if(!g)throw Error("Cannot prepare session: OpenCode server not running (no .alchemy_lab/opencode.json)")
|
|
199
|
+
const p=e.title?e.title:i?`[${i}] ${s.slice(0,40)}${s.length>40?"...":""}`:`${s.slice(0,50)}${s.length>50?"...":""}`,m={agentType:t}
|
|
200
|
+
i&&(m.requestId=i),c&&(m.specialization=c),l&&(m.specializationHeadline=l),d&&(m.callerAgentType=d)
|
|
201
|
+
const f=await fetch(g+"/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:p,...a?{parentID:a}:{},metadata:m})})
|
|
124
202
|
if(!f.ok)throw Error("Failed to create session in OpenCode: "+f.status)
|
|
125
|
-
const
|
|
126
|
-
if(!
|
|
127
|
-
const y
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
return{sessionId:
|
|
131
|
-
try{let e,
|
|
132
|
-
if(
|
|
133
|
-
e=o?.id,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
203
|
+
const y=await f.json()
|
|
204
|
+
if(!y.id)throw Error("Session created in OpenCode but no ID returned")
|
|
205
|
+
const v=y.id
|
|
206
|
+
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}),b=this.resolveModel(t,n),I=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,b,I])
|
|
208
|
+
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
|
+
try{let e,g
|
|
210
|
+
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
|
|
211
|
+
e=o?.id,g=o?.workflowId,h.info("Parent session upsert result",{parentExternalId:r,parentDbId:e??null,parentWorkflowId:g??null,responseHasSuccess:void 0!==t?.success,responseDataKeys:o?Object.keys(o):[]})}catch(e){h.warn("Failed to register parent session",{parentSessionId:r,error:(0,o.getErrorMessage)(e)})}const p={externalSessionId:t,providerId:"opencode",parentSessionId:e||null,title:i,status:"active",agentType:n,specialization:a,specializationHeadline:c,workflowId:g||void 0}
|
|
212
|
+
if(h.info("Child session upsert payload",{sessionId:t,agent:n,parentExternalId:r??null,parentDbIdSent:p.parentSessionId,workflowIdSent:p.workflowId??null}),await u.post(`/api/projects/${s}/cli-sessions`,p),h.info("Registered session in alchemy DB",{sessionId:t,agent:n,parentSessionId:r}),d){try{await u.post(`/api/projects/${s}/session-request-associations`,{opencodeSessionId:t,requestLocalId:d}),h.info("Created session-request association",{sessionId:t,requestId:d})}catch(e){h.warn("Failed to create session-request association",{error:(0,o.getErrorMessage)(e)})}if("coder"===n)try{await u.patch(`/api/projects/${s}/requests/${d}`,{cliOrigin:{providerId:"opencode",sessionId:t,createdAt:(new Date).toISOString()}}),h.debug("Updated request cliOrigin",{sessionId:t,requestId:d})}catch(e){h.warn("Failed to update request cliOrigin",{error:(0,o.getErrorMessage)(e)})}}}catch(e){h.warn("Failed to register session in alchemy DB",{error:(0,o.getErrorMessage)(e)})}}async resolveModel(e,t){const{logger:r}=this.deps,s=this.fetchProjectDefaultModel()||void 0
|
|
213
|
+
let a
|
|
214
|
+
try{const r=await this.fetchAgentConfig(e)
|
|
215
|
+
if(r){const e=r.preferredProvider||"inherit",o=r.preferredModelTier||"same",i=(0,n.resolveAgentModel)(t,e,o,s)
|
|
216
|
+
i.resolved&&(a=i.fullModel)}else{const r=(0,i.getBuiltInAgentConfig)(e)
|
|
217
|
+
if(r){const e=(0,n.resolveAgentModel)(t,r.preferredProvider||"inherit",r.preferredModelTier||"same",s)
|
|
218
|
+
e.resolved&&(a=e.fullModel)}}}catch(t){r.debug("Error resolving agent model",{agent:e,error:(0,o.getErrorMessage)(t)})}return!a&&s&&(a=s),a}async fetchAgentConfig(e){try{const t=await this.deps.apiClient.get(`/api/projects/${this.deps.projectId}/agents`)
|
|
219
|
+
return(t?.data||t||[]).find((t=>t.name===e))||null}catch{return null}}fetchProjectDefaultModel(){return this.deps.getDefaultModel()||null}}},1401(e,exports,t){"use strict"
|
|
220
|
+
function r(e,t){let r=0,s=0
|
|
221
|
+
for(const o of e)exports.IN_FLIGHT_REQUEST_STATUSES.has(o.status)?r++:t.has(o.id)&&s++
|
|
222
|
+
return{openCount:r+s,statusCount:r,taskOnlyCount:s}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.QuestTickScheduler=exports.IN_FLIGHT_REQUEST_STATUSES=void 0,exports.computeOpenCount=r
|
|
223
|
+
const s=t(4769),o=t(3416)
|
|
224
|
+
exports.IN_FLIGHT_REQUEST_STATUSES=new Set(["open","in_progress","resolving_conflicts","ready_for_review","ready_for_merge"]),exports.QuestTickScheduler=class{constructor(e){this.inFlight=new Set,this.cachedQuests=[],this.lastQuestsFetch=0,this.cachedAlchemistSessionId=null,this.cachedAlchemistAt=0,this.apiClient=e.apiClient,this.logger=e.logger,this.getOpenCodeUrl=e.getOpenCodeUrl,this.getDefaultModel=e.getDefaultModel,this.projectId=e.projectId,this.projectPath=e.projectPath}start(){this.tickTimer||(this.logger.info("Quest tick scheduler starting",{tickInterval:1e4}),setTimeout((()=>this.tick().catch((()=>{}))),5e3),this.tickTimer=setInterval((()=>{this.tick().catch((()=>{}))}),1e4))}stop(){this.tickTimer&&(clearInterval(this.tickTimer),this.tickTimer=void 0),this.inFlight.clear(),this.logger.info("Quest tick scheduler stopped")}async tick(){if(!this.projectId)return
|
|
225
|
+
let e
|
|
226
|
+
try{e=await this.fetchOpenQuests()}catch(e){return void this.logger.warn("Quest tick: fetch quests failed",{error:e+""})}if(0!==e.length){this.logger.info("Quest tick scan",{openQuests:e.length,projectId:this.projectId})
|
|
227
|
+
for(const t of e){if(this.inFlight.has(t.id))continue
|
|
228
|
+
const e=t.metadata||{}
|
|
229
|
+
if((e.status||o.QUEST_STATUS_OPEN)!==o.QUEST_STATUS_OPEN)continue
|
|
230
|
+
const r=e.tickIntervalSeconds||o.QUEST_DEFAULTS.tickIntervalSeconds,s=e.lastTickAt?Date.parse(e.lastTickAt):NaN,n=Number.isFinite(s)?s:0,i=Date.now()
|
|
231
|
+
n>0&&r>(i-n)/1e3||(this.inFlight.add(t.id),this.fireTick(t).finally((()=>this.inFlight.delete(t.id))))}}}async fireTick(e){const t=e.metadata||{},n=t.maxInFlight??o.QUEST_DEFAULTS.maxInFlight,i=t.maxPerDay??o.QUEST_DEFAULTS.maxPerDay,a=e.localId||e.id
|
|
232
|
+
let c
|
|
233
|
+
try{c=await this.fetchQuestRequests(e.id)}catch(e){return void this.logger.warn("Quest tick: fetch requests failed; skipping",{questId:a,error:e+""})}const l=new Set
|
|
234
|
+
try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/tasks?status=in_progress,pending&limit=1000`),t=e?.data||e||[],r=new Set(c.map((e=>e.id)))
|
|
235
|
+
for(const e of t)e.requestId&&r.has(e.requestId)&&l.add(e.requestId)}catch(e){this.logger.warn("Quest tick: task fetch failed; counting by status only",{questId:a,error:e+""})}const{openCount:d,statusCount:u,taskOnlyCount:h}=r(c,l)
|
|
236
|
+
if(d>=n)return void this.logger.info("Quest tick: maxInFlight reached, skipping",{questId:a,openCount:d,maxInFlight:n,statusCount:u,taskOnlyCount:h})
|
|
237
|
+
const g=(new Date).toISOString().slice(0,10),p=t.ticksTodayDate===g?t.ticksTodayCount??0:0
|
|
238
|
+
if(p>=i)return void this.logger.info("Quest tick: maxPerDay reached, skipping",{questId:a,ticksToday:p,maxPerDay:i})
|
|
239
|
+
const m=await this.findAlchemistSessionId()
|
|
240
|
+
if(!m)return void this.logger.info("Quest tick: no persistent alchemist session in project, skipping",{questId:a,projectId:this.projectId})
|
|
241
|
+
const f=(t.currentIteration||0)+1
|
|
242
|
+
let y=!1
|
|
243
|
+
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},b={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}}}
|
|
245
|
+
try{await(0,s.executeSameSession)(v,m,w,b),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
|
+
if(18e5>e-this.lastQuestsFetch)return this.cachedQuests
|
|
247
|
+
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/plans?type=Quest&limit=100`),r=t?.data||t||[]
|
|
248
|
+
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`)
|
|
249
|
+
return(t?.data||t||[]).map((e=>({id:e.id,status:e.status,questDocumentId:e.questDocumentId,createdAt:e.createdAt})))}async findAlchemistSessionId(){if(this.cachedAlchemistAt>0&&18e5>Date.now()-this.cachedAlchemistAt)return this.cachedAlchemistSessionId
|
|
250
|
+
const e=e=>(this.cachedAlchemistSessionId=e,this.cachedAlchemistAt=Date.now(),e)
|
|
251
|
+
try{const t=await this.apiClient.get("/api/projects/"+this.projectId),r=t?.data||t,s=r?.activeAlchemistSessionId
|
|
252
|
+
if(s){const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/${s}`),r=t?.data||t
|
|
253
|
+
if(r?.externalSessionId&&"active"===r?.status)return e(r.externalSessionId)
|
|
254
|
+
this.logger.info("Quest tick: project active alchemist not active, ignoring",{projectId:this.projectId,sessionUlid:s,status:r?.status})}}catch(e){this.logger.warn("Quest tick: project active-alchemist lookup failed",{error:e+""})}try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/agent-types`),r=(t?.data||t||[]).filter((e=>"alchemist"===e.agentType))
|
|
255
|
+
return 0===r.length?(this.logger.info("Quest tick: no alchemist session found in project",{projectId:this.projectId}),e(null)):(this.logger.info("Quest tick: active-alchemist not set, using first alchemist as fallback",{projectId:this.projectId,alchemistCount:r.length}),e(r[0].externalSessionId))}catch(e){return this.logger.warn("Quest tick: alchemist fallback lookup failed",{error:e+""}),null}}async bumpQuestMetadata(e,t){const r=await this.apiClient.get(`/api/projects/${this.projectId}/documents/${encodeURIComponent(e.id)}`),s=r?.data||r,n=s?.metadata||{}
|
|
256
|
+
if((n.status||o.QUEST_STATUS_OPEN)!==o.QUEST_STATUS_OPEN)return!1
|
|
257
|
+
const i=(new Date).toISOString().slice(0,10),a=(n.ticksTodayDate===i?n.ticksTodayCount??0:0)+1,c={...n,lastTickAt:(new Date).toISOString(),currentIteration:t,ticksTodayDate:i,ticksTodayCount:a}
|
|
258
|
+
return await this.apiClient.patch(`/api/projects/${this.projectId}/documents/${encodeURIComponent(e.id)}`,{metadata:c}),!0}buildTickPrompt(e,t,r,s,n){const i=e.metadata||{},a=e.localId||e.id,c=i.minIterations??o.QUEST_DEFAULTS.minIterations
|
|
259
|
+
return[`[Quest tick: ${a}, iteration ${t}]`,"","Filename: "+(e.filename||"(unknown)"),"Criteria: "+(i.criteria||"(none specified)"),"",`Caps: maxInFlight=${i.maxInFlight??o.QUEST_DEFAULTS.maxInFlight}, maxPerDay=${i.maxPerDay??o.QUEST_DEFAULTS.maxPerDay}, minIterations=${c}.`,`Currently in flight under this quest: ${r}${s>0?` (incl. ${s} via in-progress/pending tasks under non-active requests)`:""}. Ticks today (incl. this): ${n}/${i.maxPerDay??o.QUEST_DEFAULTS.maxPerDay}.`,'\nDecide what to take action on this tick. If progress requires a request, call specialist_ops with operation="delegate". Inside the prompt you write for the specialist, you MUST include this exact line so the spawned request is tagged to the quest:',` "When you call create_request, pass questDocumentId="${a}"."`,"Without that instruction in the specialist's prompt, the spawned request will not be tagged and the runner can't track it.\nDo NOT mark this quest complete \u2014 quest end is a user action, not yours. Just do this tick's work and stop."].join("\n")}}},1405(e,exports,t){"use strict"
|
|
260
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleStartBuildingAction=async function(e,t){const{plannerSessionId:n,parentSessionId:i,requestIds:a,summary:c}=e,{logger:l,apiClient:d,projectId:u,projectPath:h}=t
|
|
261
|
+
if(!n)return{success:!1,message:"plannerSessionId is required",error:"missing_params"}
|
|
262
|
+
l.info("Starting start-building action",{plannerSessionId:n,requestIds:a,payloadParent:i})
|
|
263
|
+
let g=null
|
|
264
|
+
try{const e=await d.get(`/api/projects/${u}/cli-sessions/resolve/${n}`)
|
|
265
|
+
e.success&&(g=e.data)}catch(e){l.warn("Failed to resolve planner session",{error:(0,r.getErrorMessage)(e)})}const p=i||g?.parent?.externalSessionId||null
|
|
266
|
+
if(!p)return{success:!1,message:"Could not find parent session for the planner. The planner session may not have been properly linked at creation time.",error:"parent_not_found"}
|
|
267
|
+
l.info("Resolved parent session",{plannerSessionId:n,parentSessionId:p})
|
|
268
|
+
const m=async(e,t)=>{try{return await t()}catch(s){return l.warn(e+": first attempt failed, retrying",{error:(0,r.getErrorMessage)(s)}),await new Promise((e=>setTimeout(e,500))),await t()}}
|
|
150
269
|
let f=!1
|
|
151
|
-
try{await
|
|
152
|
-
const
|
|
153
|
-
if(
|
|
154
|
-
try{const
|
|
155
|
-
return{model:{providerID:e,modelID:
|
|
156
|
-
|
|
157
|
-
if(f&&
|
|
158
|
-
if(e)try{await
|
|
159
|
-
const
|
|
270
|
+
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
|
+
const v=(0,o.getOpenCodeServerUrl)(h,l)
|
|
272
|
+
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)("inserted-prompt")}],...t.cachedDefaultModel?(()=>{const[e,...r]=t.cachedDefaultModel.split("/")
|
|
274
|
+
return{model:{providerID:e,modelID:r.join("/")}}})():{}})})))
|
|
275
|
+
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
|
+
if(f&&y){const e=g?.id
|
|
277
|
+
if(e)try{await d.patch(`/api/projects/${u}/cli-sessions/${e}`,{status:"archived"}),l.info("Archived planner session",{plannerSessionId:n,sessionLocalId:e})}catch(e){l.warn("Failed to archive planner session (non-fatal)",{error:(0,r.getErrorMessage)(e)})}}return f&&y?{success:!0,message:"Build report sent to parent session",data:{parentSessionId:p}}:{success:!1,message:`Build handoff failed (report: ${f}, prompt: ${y}). The parent session was not fully notified \u2014 please retry.`,error:"handoff_partial_failure",data:{parentSessionId:p,reportOk:f,promptOk:y}}}
|
|
278
|
+
const r=t(8824),s=t(2272),o=t(8148)},1943(module){"use strict"
|
|
160
279
|
module.exports=require("fs/promises")},2096(e,exports,t){"use strict"
|
|
161
|
-
var
|
|
280
|
+
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
162
281
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.ProjectConfigService=void 0
|
|
163
|
-
const r
|
|
164
|
-
exports.ProjectConfigService=class{constructor(e){this.logger=e.child({component:"project-config"})}async readProjectId(e){try{const t=o.default.join(e,i.ALCHEMY_DIR_NAME,"project.json"),
|
|
165
|
-
return n.id?n.id+"":null}catch(e){return this.logger.debug("Could not read project ID",{error:(0,a.getErrorMessage)(e)}),null}}async readProjectConfig(e){try{const t=o.default.join(e,i.ALCHEMY_DIR_NAME,"config.json"),
|
|
166
|
-
return JSON.parse(
|
|
167
|
-
await
|
|
282
|
+
const s=r(t(1943)),o=r(t(6928)),n=r(t(857)),i=t(4139),a=t(8824)
|
|
283
|
+
exports.ProjectConfigService=class{constructor(e){this.logger=e.child({component:"project-config"})}async readProjectId(e){try{const t=o.default.join(e,i.ALCHEMY_DIR_NAME,"project.json"),r=await s.default.readFile(t,"utf-8"),n=JSON.parse(r)
|
|
284
|
+
return n.id?n.id+"":null}catch(e){return this.logger.debug("Could not read project ID",{error:(0,a.getErrorMessage)(e)}),null}}async readProjectConfig(e){try{const t=o.default.join(e,i.ALCHEMY_DIR_NAME,"config.json"),r=await s.default.readFile(t,"utf-8")
|
|
285
|
+
return JSON.parse(r)}catch(e){return this.logger.debug("Could not read project config",{error:(0,a.getErrorMessage)(e)}),{}}}async saveProjectConfig(e,t){try{const r=o.default.join(e,i.ALCHEMY_DIR_NAME),n=o.default.join(r,"config.json")
|
|
286
|
+
await s.default.mkdir(r,{recursive:!0})
|
|
168
287
|
let a={}
|
|
169
|
-
try{const e=await
|
|
170
|
-
a=JSON.parse(e)}catch(e){this.logger.debug("No existing config.json, creating new one")}void 0!==t&&(a.autoJournal=t),a.lockTimeout||(a.lockTimeout=3600),a.worktreeCleanupDelay||(a.worktreeCleanupDelay=900),a.maxWorktreeAge||(a.maxWorktreeAge=24),a.cleanupInterval||(a.cleanupInterval=15),void 0===a.preserveBranchesOnCleanup&&(a.preserveBranchesOnCleanup=!0),a.maxConcurrentSessions||(a.maxConcurrentSessions=10),a.sessionTimeoutWarning||(a.sessionTimeoutWarning=45),await
|
|
288
|
+
try{const e=await s.default.readFile(n,"utf-8")
|
|
289
|
+
a=JSON.parse(e)}catch(e){this.logger.debug("No existing config.json, creating new one")}void 0!==t&&(a.autoJournal=t),a.lockTimeout||(a.lockTimeout=3600),a.worktreeCleanupDelay||(a.worktreeCleanupDelay=900),a.maxWorktreeAge||(a.maxWorktreeAge=24),a.cleanupInterval||(a.cleanupInterval=15),void 0===a.preserveBranchesOnCleanup&&(a.preserveBranchesOnCleanup=!0),a.maxConcurrentSessions||(a.maxConcurrentSessions=10),a.sessionTimeoutWarning||(a.sessionTimeoutWarning=45),await s.default.writeFile(n,JSON.stringify(a,null,2),"utf-8"),this.logger.info("Saved project config",{configPath:n})}catch(e){this.logger.error("Failed to save project config",{error:(0,a.getErrorMessage)(e)})}}getToolPreferencesPath(){const e=n.default.homedir()
|
|
171
290
|
return o.default.join(e,".alchemy_lab","tool-preferences.json")}async loadToolPreferences(e){const t=this.getToolPreferencesPath()
|
|
172
|
-
try{const
|
|
173
|
-
return e&&o[e]?o[e]:e?null:o}catch(t){return this.logger.debug("No user preferences found",{error:(0,a.getErrorMessage)(t)}),e?null:{}}}async saveToolPreferences(e,t,
|
|
174
|
-
this.logger.info("Saving user tool preferences",{clientType:e,preset:t,disabledToolsCount:
|
|
291
|
+
try{const r=await s.default.readFile(t,"utf-8"),o=JSON.parse(r)
|
|
292
|
+
return e&&o[e]?o[e]:e?null:o}catch(t){return this.logger.debug("No user preferences found",{error:(0,a.getErrorMessage)(t)}),e?null:{}}}async saveToolPreferences(e,t,r,n){const i=this.getToolPreferencesPath(),a=o.default.dirname(i)
|
|
293
|
+
this.logger.info("Saving user tool preferences",{clientType:e,preset:t,disabledToolsCount:r?.length||0,allowedToolsCount:n?.length||0}),await s.default.mkdir(a,{recursive:!0})
|
|
175
294
|
let c={}
|
|
176
|
-
try{const e=await
|
|
177
|
-
c=JSON.parse(e)}catch(e){}c[e]={preset:t,disabledTools:
|
|
178
|
-
|
|
295
|
+
try{const e=await s.default.readFile(i,"utf-8")
|
|
296
|
+
c=JSON.parse(e)}catch(e){}c[e]={preset:t,disabledTools:r||[],allowedAlchemyTools:n||[],updatedAt:(new Date).toISOString()},await s.default.writeFile(i,JSON.stringify(c,null,2),"utf-8"),this.logger.info("User tool preferences saved",{clientType:e,prefsFile:i})}}},2272(e,exports){"use strict"
|
|
297
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.partMeta=function(e,t){return t?{alchemy:{kind:e,payload:t}}:{alchemy:{kind:e}}},exports.alchemyMeta=function(e){return{_meta:{alchemy:e}}},exports.withAlchemyKind=function(e,t,r){const s=e._meta,o=s?.alchemy??{}
|
|
298
|
+
return{...e,_meta:{...s,alchemy:{...o,...r,kind:t}}}},exports.readAlchemyMeta=function(e){return e?._meta?.alchemy}},2366(e,exports,t){"use strict"
|
|
299
|
+
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
179
300
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
|
|
180
|
-
const r
|
|
181
|
-
class
|
|
182
|
-
const
|
|
183
|
-
this.apiClient=new n.BaseApiClient(t.remoteServerUrl,e,
|
|
184
|
-
return e.use(((e,t,
|
|
185
|
-
if(!
|
|
186
|
-
const o=await this.apiClient.get(`/api/projects/${
|
|
187
|
-
t.json({success:!0,ancestors:n})}catch(e){t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.get("/api/registry/projects",(async(e,
|
|
188
|
-
|
|
189
|
-
!
|
|
190
|
-
if(!
|
|
191
|
-
const i=await this.hookExecutor.processEvent({eventType:
|
|
192
|
-
t.json({success:!0,data:i})}catch(e){this.logger.error("Hook event processing failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.post("/api/
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
t.json({success:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
t.json({
|
|
301
|
+
const s=r(t(7252)),o=r(t(8577)),n=t(4192),i=t(1152),a=r(t(6928)),c=r(t(9896)),l=r(t(857)),d=t(5607),u=t(8984),h=t(8524),g=t(8824),p=t(2096),m=t(7210),f=t(7227),y=t(5419),v=t(3962),w=t(2928),b=t(6563),I=t(1401),E=t(1292),C=t(8148),S=t(8463),k=t(489),T=t(3807),_=t(7478),j=t(5317),A=t(355),P=t(833)
|
|
302
|
+
class M{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
|
+
const r=(0,i.createMetricsCollector)(!1)
|
|
304
|
+
this.apiClient=new n.BaseApiClient(t.remoteServerUrl,e,r,t.authToken,{timeout:35e3}),this.configurer=new u.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.cliBridge=new h.CLIBridgeWebSocket(e),(0,h.setCLIBridge)(this.cliBridge),this.hookExecutor=new m.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
|
+
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/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,A.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
|
|
306
|
+
if(!s)return void t.status(400).json({success:!1,error:"No project"})
|
|
307
|
+
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})))
|
|
308
|
+
t.json({success:!0,ancestors:n})}catch(e){t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.get("/api/registry/projects",(async(e,r)=>{try{const{listLive:e}=await t.e(640).then(t.bind(t,2640))
|
|
309
|
+
r.json({success:!0,data:e()})}catch(e){r.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.post("/api/mcp/tool-call-event",((e,t)=>{const{action:r,callId:s}=e.body||{}
|
|
310
|
+
!s||"start"!==r&&"end"!==r?t.status(400).json({success:!1,error:"action must be start|end, callId required"}):(this.handleMcpToolCallEvent(r,s+""),t.json({success:!0}))})),e.post("/api/hook-event",(async(e,t)=>{try{const{eventType:r,sessionId:s,variables:o,timing:n}=e.body
|
|
311
|
+
if(!r)return void t.status(400).json({success:!1,error:"eventType is required"})
|
|
312
|
+
const i=await this.hookExecutor.processEvent({eventType:r,sessionId:s||"",variables:o||{},timing:n})
|
|
313
|
+
t.json({success:!0,data:i})}catch(e){this.logger.error("Hook event processing failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.post("/api/tool-event",(async(e,t)=>{try{const{toolName:r,sessionId:s,input:o,output:n,timing:i}=e.body||{}
|
|
314
|
+
if(!r||"before"!==i&&"after"!==i)return void t.status(400).json({success:!1,error:"toolName and timing ('before'|'after') are required"})
|
|
315
|
+
const a=(0,f.parseToolEvents)({toolName:r,sessionId:s||"",input:o,output:n,timing:i})
|
|
316
|
+
let c=0
|
|
317
|
+
for(const e of a)c+=(await this.hookExecutor.processEvent({eventType:e.eventType,sessionId:s||"",variables:e.variables,timing:e.timing})).triggered
|
|
318
|
+
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,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.post("/api/direct-report",this.handleDirectReport.bind(this)),e.post("/api/cli-bridge/command",this.handleCLIBridgeCommand.bind(this)),e.get("/api/cli-bridge/status",this.handleCLIBridgeStatus.bind(this)),e.post("/api/spawn-session",this.handleSpawnSession.bind(this)),e.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,_.generateRequestBranchName)(r,""):void 0)
|
|
319
|
+
if(!s)return void t.status(400).json({success:!1,error:"requestId or branch required"})
|
|
320
|
+
const o=(await(0,T.simpleGit)(this.options.projectPath).revparse([s])).trim()
|
|
321
|
+
t.json({success:!0,data:{branch:s,sha:o}})}catch(e){this.logger.debug("branch-head lookup failed",{error:(0,g.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||{}
|
|
322
|
+
if(!r||"string"!=typeof r)return void t.status(400).json({success:!1,error:"worktreePath required"});(0,k.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,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.get("/api/timer-hooks/status",((e,t)=>{const r=this.timerHookScheduler?.getStatus()??{running:!1,trackedHooks:0,inFlight:[],hooks:[]}
|
|
323
|
+
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"})
|
|
324
|
+
const r=this.timerHookScheduler.pauseHook(e.params.hookId)
|
|
325
|
+
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"})
|
|
326
|
+
const r=this.timerHookScheduler.resumeHook(e.params.hookId)
|
|
327
|
+
t.json({success:r})})),e.post("/api/timer-hooks/:hookId/stop",((e,t)=>{if(!this.timerHookScheduler)return void t.status(503).json({success:!1,error:"Scheduler not running"})
|
|
328
|
+
const r=this.timerHookScheduler.stopHook(e.params.hookId)
|
|
329
|
+
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()
|
|
330
|
+
try{c.default.accessSync(s,c.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))
|
|
331
|
+
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,g.getErrorMessage)(e)})}})),e.use("/api/opencode",((e,t)=>this.handleOpenCodeProxy(e,t))),e}handleHealth(e,t){t.json({status:"healthy",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),timestamp:(new Date).toISOString()})}async handleAuthStatus(e,t){const r=this.options.tokenManager
|
|
332
|
+
if(r)try{const e=await r.hasValidToken(),s=r.getAuthStatus(),o=e?await r.getToken():null
|
|
333
|
+
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,g.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=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
334
|
+
try{if(!c.default.existsSync(r))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
|
|
335
|
+
const e=c.default.readFileSync(r,"utf-8"),s=JSON.parse(e)
|
|
336
|
+
t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:"http://localhost:"+s.port,directory:this.options.projectPath,proxyEnabled:!0})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,g.getErrorMessage)(e)})}}updateDefaultModel(e){void 0!==e&&e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),e&&this.projectId&&this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,g.getErrorMessage)(e)}))))}handleMcpToolCallEvent(e,t){"start"!==e?(this.activeMcpCalls.delete(t),0===this.activeMcpCalls.size&&this.pendingAgentResync&&(this.pendingAgentResync=!1,this.logger.info("Firing deferred agent resync now that MCP is idle"),this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Deferred resync failed",{error:(0,g.getErrorMessage)(e)}))))):this.activeMcpCalls.set(t,Date.now())}startMcpStaleCleanup(){this.mcpStaleCleanupInterval||(this.mcpStaleCleanupInterval=setInterval((()=>{const e=Date.now()-12e4
|
|
205
337
|
let t=0
|
|
206
|
-
for(const[s
|
|
338
|
+
for(const[r,s]of this.activeMcpCalls)e>s&&(this.activeMcpCalls.delete(r),t++)
|
|
207
339
|
t>0&&(this.logger.warn("Cleaned up stale MCP tool-call tracking entries",{removed:t}),0===this.activeMcpCalls.size&&this.pendingAgentResync&&(this.pendingAgentResync=!1,this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Deferred resync after cleanup failed",{error:(0,g.getErrorMessage)(e)})))))}),6e4))}async resyncAgentMarkdowns(){if(!this.projectId)return
|
|
208
340
|
if(this.activeMcpCalls.size>0)return this.pendingAgentResync=!0,void this.logger.info("Deferring agent resync until MCP tool calls complete",{inFlight:this.activeMcpCalls.size})
|
|
209
341
|
const e=await this.configurer.fetchAgentsFromRemote(this.projectId)
|
|
210
342
|
if(!e?.length)return
|
|
211
|
-
const t=await this.configurer.fetchProjectFromRemote(this.projectId),
|
|
212
|
-
await c.default.promises.mkdir(
|
|
343
|
+
const t=await this.configurer.fetchProjectFromRemote(this.projectId),r=t?.metadata?.globalSystemPrompt,s=a.default.join(this.options.projectPath,".opencode","agent")
|
|
344
|
+
await c.default.promises.mkdir(s,{recursive:!0})
|
|
213
345
|
const o=this.cachedDefaultModel||"anthropic/claude-opus-4-5"
|
|
214
346
|
for(const t of e){if(!t.enabled)continue
|
|
215
|
-
const e=this.configurer.generateAgentMarkdown(t,"alchemy",o,
|
|
216
|
-
await c.default.promises.writeFile(a.default.join(
|
|
217
|
-
try{const e=
|
|
218
|
-
e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3)})).ok&&this.logger.info("Triggered OpenCode config reload")}catch(e){this.logger.debug("Failed to trigger OpenCode config reload",{error:(0,g.getErrorMessage)(e)})}}async handleSyncAgents(e,t){try{const e=this.
|
|
219
|
-
if(!e
|
|
220
|
-
this.logger.info("Syncing agents from remote",{projectId:e
|
|
221
|
-
const
|
|
222
|
-
if(!
|
|
223
|
-
const
|
|
224
|
-
this.logger.debug("Fetched project for global prompt",{projectId:e
|
|
225
|
-
const o=
|
|
226
|
-
o?this.logger.info("Using global system prompt from project metadata",{projectId:e
|
|
347
|
+
const e=this.configurer.generateAgentMarkdown(t,"alchemy",o,r)
|
|
348
|
+
await c.default.promises.writeFile(a.default.join(s,t.name+".md"),e,"utf8")}this.logger.info("Re-synced agent markdowns after model change",{model:o,count:e.length})
|
|
349
|
+
try{const e=this.getOpenCodeUrl()
|
|
350
|
+
e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3)})).ok&&this.logger.info("Triggered OpenCode config reload")}catch(e){this.logger.debug("Failed to trigger OpenCode config reload",{error:(0,g.getErrorMessage)(e)})}}async handleSyncAgents(e,t){try{const e=this.projectId
|
|
351
|
+
if(!e)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
352
|
+
this.logger.info("Syncing agents from remote",{projectId:e})
|
|
353
|
+
const r=await this.configurer.fetchAgentsFromRemote(e)
|
|
354
|
+
if(!r||0===r.length)return void t.json({success:!0,message:"No agents to sync",count:0})
|
|
355
|
+
const s=await this.configurer.fetchProjectFromRemote(e)
|
|
356
|
+
this.logger.debug("Fetched project for global prompt",{projectId:e,hasProject:!!s,hasGlobalSystemPrompt:!!s?.metadata?.globalSystemPrompt})
|
|
357
|
+
const o=s?.metadata?.globalSystemPrompt
|
|
358
|
+
o?this.logger.info("Using global system prompt from project metadata",{projectId:e,promptLength:o.length}):this.logger.debug("No global system prompt found in project metadata")
|
|
227
359
|
const n=a.default.join(this.options.projectPath,".opencode","agent")
|
|
228
360
|
await c.default.promises.mkdir(n,{recursive:!0})
|
|
229
|
-
const i="alchemy",l=this.cachedDefaultModel||"anthropic/claude-opus-4-5",d=
|
|
230
|
-
return c.default.promises.writeFile(
|
|
361
|
+
const i="alchemy",l=this.cachedDefaultModel||"anthropic/claude-opus-4-5",d=r.filter((e=>e.enabled)),u=await Promise.allSettled(d.map((e=>{const t=this.configurer.generateAgentMarkdown(e,i,l,o),r=a.default.join(n,e.name+".md")
|
|
362
|
+
return c.default.promises.writeFile(r,t,"utf8").then((()=>{this.logger.info("Synced agent file",{name:e.name,path:r,mode:e.mode})}))}))),h=u.filter((e=>"fulfilled"===e.status)).length,p=u.filter((e=>"rejected"===e.status)).length
|
|
231
363
|
p>0&&this.logger.warn("Some agent file writes failed",{writeFails:p})
|
|
232
364
|
try{const e=a.default.join(this.options.projectPath,"opencode.json")
|
|
233
|
-
if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),o=JSON.parse(t),n=
|
|
234
|
-
if(o.agent){for(const e of
|
|
235
|
-
c.default.writeFileSync(e,JSON.stringify(o,null,2),"utf-8"),this.logger.info("Updated opencode.json agent tools",{bashOverrides:Object.keys(n).length})}}}catch(e){this.logger.warn("Failed to update opencode.json agent tools",{error:(0,g.getErrorMessage)(e)})}let
|
|
236
|
-
try{const e=
|
|
237
|
-
e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3)})).ok&&(this.logger.info("Triggered OpenCode config reload after agent sync"),
|
|
238
|
-
if(!
|
|
239
|
-
if(!/^req\d+$/.test(
|
|
240
|
-
this.logger.info("Generating journal for request",{requestId:
|
|
241
|
-
const o=`Generate a journal entry for request ${
|
|
242
|
-
a.pid&&this.trackChildPid(a.pid),a.unref(),
|
|
243
|
-
if(!
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
248
|
-
const{hookId:r,hookName:o,eventType:n,executionMode:i,agent:a}=e.body
|
|
249
|
-
this.logger.debug("Proxying hook trigger notification",{projectId:s,hookName:o,eventType:n})
|
|
250
|
-
const c=await this.apiClient.post(`/api/projects/${s}/hook-triggers`,{hookId:r,hookName:o,eventType:n,executionMode:i,agent:a})
|
|
251
|
-
t.json(c)}catch(e){this.logger.debug("Failed to proxy hook trigger",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGetHookIteration(e,t){try{const s=this.readProjectConfig()
|
|
252
|
-
if(!s?.id)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
253
|
-
const{sessionId:r,hookId:o}=e.params,n=await this.apiClient.get(`/api/projects/${s.id}/hook-iterations/${r}/${o}`)
|
|
254
|
-
t.json(n)}catch(e){this.logger.debug("Failed to get hook iteration",{error:(0,g.getErrorMessage)(e)}),t.json({success:!0,data:{iterationCount:0}})}}async handleIncrementHookIteration(e,t){try{const s=this.readProjectConfig()
|
|
255
|
-
if(!s?.id)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
256
|
-
const{sessionId:r,hookId:o}=e.params,n=await this.apiClient.post(`/api/projects/${s.id}/hook-iterations/${r}/${o}/increment`,{})
|
|
257
|
-
t.json(n)}catch(e){this.logger.debug("Failed to increment hook iteration",{error:(0,g.getErrorMessage)(e)}),t.json({success:!0,data:{iterationCount:0}})}}async handleDirectReport(e,t){try{const s=this.projectId
|
|
258
|
-
if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
259
|
-
const{sessionId:r,requestId:o,summary:n,status:i}=e.body
|
|
260
|
-
if(!r)return void t.status(400).json({success:!1,error:"sessionId is required"})
|
|
261
|
-
this.logger.info("Direct report creation",{sessionId:r,requestId:o,status:i})
|
|
365
|
+
if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),o=JSON.parse(t),n=s?.metadata?.bashToolAgents||{}
|
|
366
|
+
if(o.agent){for(const e of r)e.enabled&&o.agent[e.name]&&e.name in n&&(o.agent[e.name].tools.bash=!!n[e.name])
|
|
367
|
+
c.default.writeFileSync(e,JSON.stringify(o,null,2),"utf-8"),this.logger.info("Updated opencode.json agent tools",{bashOverrides:Object.keys(n).length})}}}catch(e){this.logger.warn("Failed to update opencode.json agent tools",{error:(0,g.getErrorMessage)(e)})}let m=!1
|
|
368
|
+
try{const e=this.getOpenCodeUrl()
|
|
369
|
+
e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3)})).ok&&(this.logger.info("Triggered OpenCode config reload after agent sync"),m=!0)}catch(e){this.logger.warn("Could not reload OpenCode config",{error:(0,g.getErrorMessage)(e)})}t.json({success:!0,message:`Synced ${h} agent(s) to local files${m?" - config reloaded":""}`,count:h,reloaded:m})}catch(e){this.logger.error("Failed to sync agents",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGenerateJournal(e,r){try{const{requestId:s}=e.body
|
|
370
|
+
if(!s)return void r.status(400).json({success:!1,error:"requestId is required"})
|
|
371
|
+
if(!/^req\d+$/.test(s))return void r.status(400).json({success:!1,error:"Invalid requestId format"})
|
|
372
|
+
this.logger.info("Generating journal for request",{requestId:s})
|
|
373
|
+
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,j.spawn)(i.command,[...i.prependArgs,"run","--agent","journal",o],{detached:!0,stdio:"ignore",cwd:this.options.projectPath,env:{...process.env,...(0,y.getOpencodeSpawnEnv)(this.options.projectPath,(0,v.isDevMode)())}})
|
|
374
|
+
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,g.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleDirectReport(e,t){try{const r=this.projectId
|
|
375
|
+
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
376
|
+
const{sessionId:s,requestId:o,summary:n,status:i}=e.body
|
|
377
|
+
if(!s)return void t.status(400).json({success:!1,error:"sessionId is required"})
|
|
378
|
+
this.logger.info("Direct report creation",{sessionId:s,requestId:o,status:i})
|
|
262
379
|
const a=[]
|
|
263
|
-
try{const e=await this.apiClient.get(`/api/projects/${
|
|
264
|
-
let o=
|
|
265
|
-
for(const e of t)a.push({ancestorId:e.externalSessionId,childId:o}),o=e.externalSessionId}catch(e){this.logger.warn("Failed to fetch ancestor chain",{sessionId:
|
|
380
|
+
try{const e=await this.apiClient.get(`/api/projects/${r}/cli-sessions/external/opencode/${encodeURIComponent(s)}/ancestors`),t=e?.data||e||[]
|
|
381
|
+
let o=s
|
|
382
|
+
for(const e of t)a.push({ancestorId:e.externalSessionId,childId:o}),o=e.externalSessionId}catch(e){this.logger.warn("Failed to fetch ancestor chain",{sessionId:s,error:(0,g.getErrorMessage)(e)})}if(0===a.length)return void t.status(404).json({success:!1,error:"No parent session found"})
|
|
266
383
|
let c,l=n
|
|
267
|
-
if(!l&&o)try{const e=await this.apiClient.get(`/api/projects/${
|
|
384
|
+
if(!l&&o)try{const e=await this.apiClient.get(`/api/projects/${r}/requests/${o}`),t=e?.data||e
|
|
268
385
|
c=t?.title
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
const d=o||
|
|
386
|
+
const s=t?.completionSummary
|
|
387
|
+
s?l=s:c&&(l="Done: "+c)}catch(e){this.logger.debug("Could not fetch request details for summary",{requestId:o,error:(0,g.getErrorMessage)(e)})}l||(l="Completed request "+(o||"unknown"))
|
|
388
|
+
const d=o||s
|
|
272
389
|
if(this.directReportDedup.size>=1e3){const e=this.directReportDedup.keys().next().value
|
|
273
390
|
void 0!==e&&this.directReportDedup.delete(e)}this.directReportDedup.has(d)||this.directReportDedup.set(d,new Set)
|
|
274
|
-
const u=this.directReportDedup.get(d),h=a.filter((({ancestorId:e})=>!u.has(e)||(this.logger.info("Direct report dedup: already notified",{ancestorId:e,dedupKey:d}),!1))),p=await Promise.allSettled(h.map((async({ancestorId:e,childId:t})=>{const
|
|
275
|
-
return u.add(e),this.logger.info("Direct report created for ancestor",{messageCode:o?.messageCode,ancestorId:e,fromChild:t}),{externalSessionId:e,messageCode:o?.messageCode,fromSessionId:t}}))),
|
|
276
|
-
for(const e of p)"fulfilled"===e.status&&
|
|
391
|
+
const u=this.directReportDedup.get(d),h=a.filter((({ancestorId:e})=>!u.has(e)||(this.logger.info("Direct report dedup: already notified",{ancestorId:e,dedupKey:d}),!1))),p=await Promise.allSettled(h.map((async({ancestorId:e,childId:t})=>{const s=await this.apiClient.post(`/api/projects/${r}/session-messages/reports`,{fromSessionId:t,toSessionId:e,status:i||"success",summary:l}),o=s?.data||s
|
|
392
|
+
return u.add(e),this.logger.info("Direct report created for ancestor",{messageCode:o?.messageCode,ancestorId:e,fromChild:t}),{externalSessionId:e,messageCode:o?.messageCode,fromSessionId:t,content:o?.content}}))),m=[]
|
|
393
|
+
for(const e of p)"fulfilled"===e.status&&m.push(e.value)
|
|
277
394
|
for(const e of p)"rejected"===e.status&&this.logger.warn("Failed to create report for ancestor",{error:(0,g.getErrorMessage)(e.reason)})
|
|
278
|
-
if(setTimeout((()=>{this.directReportDedup.delete(d)}),3e5),o)try{const e=await this.apiClient.get(`/api/projects/${
|
|
279
|
-
this.logger.info("Released request associations",{sessionId:
|
|
280
|
-
if(
|
|
281
|
-
if(this.logger.info("Agent type DB lookup",{sessionId:
|
|
282
|
-
if(this.logger.info("Agent type bridge lookup",{sessionId:
|
|
283
|
-
if(
|
|
284
|
-
return void t.json({success:!0,parentSessionId:o})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:
|
|
285
|
-
if(!
|
|
286
|
-
const
|
|
287
|
-
if(i){const
|
|
288
|
-
if(!e.ok||!e.body)return
|
|
395
|
+
if(setTimeout((()=>{this.directReportDedup.delete(d)}),3e5),o)try{const e=await this.apiClient.get(`/api/projects/${r}/session-request-associations/by-session/${encodeURIComponent(s)}?activeOnly=true`),t=e?.data||e||[],o=(await Promise.allSettled(t.map((e=>this.apiClient.post(`/api/projects/${r}/session-request-associations/end`,{sessionId:s,requestLocalId:e.requestLocalId}))))).filter((e=>"rejected"===e.status)).length
|
|
396
|
+
this.logger.info("Released request associations",{sessionId:s,count:t.length,failCount:o})}catch(e){this.logger.warn("Failed to release associations",{sessionId:s,error:(0,g.getErrorMessage)(e)})}t.json({success:!0,data:{notifiedSessions:m,report:m[0]?{messageCode:m[0].messageCode}:void 0,parentExternalSessionId:a[0]?.ancestorId||null}})}catch(e){this.logger.error("Direct report creation failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGetSessionAgentType(e,t){try{const{sessionId:r}=e.params,s=this.projectId
|
|
397
|
+
if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}`),o=e?.data||e
|
|
398
|
+
if(this.logger.info("Agent type DB lookup",{sessionId:r,dbAgentType:o?.agentType||null,dbId:o?.id||o?.localId||null}),o?.agentType)return void t.json({success:!0,agentType:o.agentType,source:"db"})}catch(e){this.logger.info("Agent type DB lookup failed",{sessionId:r,error:(0,g.getErrorMessage)(e)})}if(this.cliBridge.isUIConnected())try{const e=await this.cliBridge.sendCommand("get-session-status",{sessionId:r},5e3)
|
|
399
|
+
if(this.logger.info("Agent type bridge lookup",{sessionId:r,bridgeAgentType:e?.data?.agentType||null,bridgeSuccess:e?.success}),e.success&&e.data?.agentType)return void t.json({success:!0,agentType:e.data.agentType,source:"bridge"})}catch(e){this.logger.info("Agent type bridge lookup failed",{sessionId:r,error:(0,g.getErrorMessage)(e)})}this.logger.info("Agent type not found",{sessionId:r}),t.json({success:!0,agentType:null,source:"unknown"})}catch(e){this.logger.debug("Failed to get session agent type",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGetSessionParent(e,t){try{const{sessionId:r}=e.params,s=this.projectId
|
|
400
|
+
if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/resolve/${encodeURIComponent(r)}`),o=e?.data?.parent?.externalSessionId||""
|
|
401
|
+
return void t.json({success:!0,parentSessionId:o})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:r,error:(0,g.getErrorMessage)(e)})}t.json({success:!0,parentSessionId:""})}catch(e){this.logger.debug("Failed to get parent session",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleOpenCodeProxy(e,t){try{const r=this.getOpenCodeUrl()
|
|
402
|
+
if(!r)return void t.status(502).json({error:"OpenCode not running"})
|
|
403
|
+
const s=e.path.replace(/^\//,""),o=e.originalUrl.includes("?")?e.originalUrl.substring(e.originalUrl.indexOf("?")):"",n=`${r}/${s}${o}`,i=e.headers.accept?.includes("text/event-stream")
|
|
404
|
+
if(i){const r=new AbortController,s=()=>{this.proxyAbortControllers.delete(r)},o=(async()=>{try{const e=await fetch(n,{headers:{Accept:"text/event-stream"},signal:r.signal})
|
|
405
|
+
if(!e.ok||!e.body)return s(),void t.status(e.status).json({error:"SSE upstream error"})
|
|
289
406
|
t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"}),t.flushHeaders()
|
|
290
407
|
const o=e.body.getReader()
|
|
291
|
-
try{for(;;){const{done:e,value:
|
|
408
|
+
try{for(;;){const{done:e,value:r}=await o.read()
|
|
292
409
|
if(e)break
|
|
293
|
-
t.write(
|
|
294
|
-
return this.proxyAbortControllers.set(
|
|
410
|
+
t.write(r),"function"==typeof t.flush&&t.flush()}}catch{}t.end()}catch{t.headersSent||t.status(502).json({error:"SSE upstream error"})}finally{s()}})()
|
|
411
|
+
return this.proxyAbortControllers.set(r,o),void e.on("close",(()=>{try{r.abort()}catch{}}))}const a={"Content-Type":e.headers["content-type"]||"application/json"},c={method:e.method,headers:a}
|
|
295
412
|
let l
|
|
296
413
|
"GET"!==e.method&&"HEAD"!==e.method&&e.body&&(c.body=JSON.stringify(e.body))
|
|
297
414
|
let d=n
|
|
298
415
|
try{l=await fetch(n,c)}catch(e){this.openCodePort=null,this.openCodeUrlCache=null
|
|
299
416
|
const t=this.getOpenCodeUrl()
|
|
300
|
-
if(!t||t===
|
|
301
|
-
d=`${t}/${
|
|
417
|
+
if(!t||t===r)throw e
|
|
418
|
+
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()
|
|
302
419
|
const u=l.headers.get("content-type")||""
|
|
303
420
|
let h
|
|
304
|
-
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(
|
|
305
|
-
if(!s
|
|
421
|
+
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,g.getErrorMessage)(r),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,g.getErrorMessage)(r)})}}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
|
|
422
|
+
if(!r||!s)return void t.status(400).json({success:!1,error:"agent and prompt are required"})
|
|
423
|
+
if(!this.projectId)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
424
|
+
const u=this.projectId,h=new E.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})
|
|
425
|
+
t.json({success:!0,data:g})}catch(e){this.logger.error("Spawn session failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleRunTestsNow(e,r){try{const{requestId:s,parentSessionId:o,testProcessIds:n,commandNames:i}=e.body||{}
|
|
426
|
+
if(!this.projectId)return void r.status(400).json({success:!1,error:"No project configured"})
|
|
427
|
+
if(!s)return void r.status(400).json({success:!1,error:"requestId is required"})
|
|
428
|
+
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)
|
|
429
|
+
if(!l.success)return void r.status(500).json({success:!1,error:l.error||l.message})
|
|
430
|
+
r.json({success:!0,data:l.data})}catch(e){this.logger.error("Run tests now failed",{error:(0,g.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleSetupTests(e,t){try{const{requestId:r,parentSessionId:s,branchName:o}=e.body||{}
|
|
431
|
+
if(!this.projectId)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
432
|
+
const n=this.projectId,i=this.options.projectPath
|
|
433
|
+
await(0,S.sweepStaleSetupWorktrees)(i,this.logger)
|
|
434
|
+
const c=await(0,S.resolveSetupBranch)(this.apiClient,n,o,r,this.logger,i),l=(0,S.generateSetupId)(),d=a.default.join((0,S.getSetupWorktreesBase)(i),l)
|
|
435
|
+
try{await(0,S.createSetupWorktree)(i,d,c,this.logger)}catch(e){const r=(0,g.getErrorMessage)(e)
|
|
436
|
+
return this.logger.error("Failed to create setup worktree",{branch:c,error:r}),void t.status(500).json({success:!1,error:"Failed to create setup worktree: "+r})}const u=(0,S.buildSetupPrompt)(i,r),h=await(0,C.spawnActionAgent)({agent:"test-architect",prompt:u,cwd:d,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})
|
|
437
|
+
if(!h.success)return void t.status(500).json({success:!1,error:h.error||"Spawn failed"})
|
|
438
|
+
t.json({success:!0,data:{sessionId:h.sessionId,worktreePath:d,branch:c}})}catch(e){this.logger.error("Setup tests failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleSetupCommands(e,t){try{const{requestId:r,parentSessionId:s,branchName:o,context:n}=e.body||{}
|
|
306
439
|
if(!this.projectId)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
440
|
+
const i=this.projectId,c=this.options.projectPath
|
|
441
|
+
await(0,S.sweepStaleSetupWorktrees)(c,this.logger)
|
|
442
|
+
const l=await(0,S.resolveSetupBranch)(this.apiClient,i,o,r,this.logger,c),d=(0,S.generateSetupId)(),u=a.default.join((0,S.getSetupWorktreesBase)(c),d)
|
|
443
|
+
try{await(0,S.createSetupWorktree)(c,u,l,this.logger)}catch(e){const r=(0,g.getErrorMessage)(e)
|
|
444
|
+
return this.logger.error("Failed to create setup worktree (commands)",{branch:l,error:r}),void t.status(500).json({success:!1,error:"Failed to create setup worktree: "+r})}const h=(0,S.buildCommandSetupPrompt)(c,r,n),p=await(0,C.spawnActionAgent)({agent:"command-architect",prompt:h,cwd:u,projectPath:c,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})
|
|
445
|
+
if(!p.success)return void t.status(500).json({success:!1,error:p.error||"Spawn failed"})
|
|
446
|
+
t.json({success:!0,data:{sessionId:p.sessionId,worktreePath:u,branch:l}})}catch(e){this.logger.error("Setup commands failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGetAgentByName(e,t){try{const r=this.projectId
|
|
447
|
+
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
448
|
+
const{agentName:s}=e.params,o=await this.apiClient.get(`/api/projects/${r}/agents`),n=(o?.data||o||[]).find((e=>e.name===s))
|
|
449
|
+
if(!n)return void t.status(404).json({success:!1,error:`Agent '${s}' not found`})
|
|
450
|
+
t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}readProjectConfig(){if(this.cachedProjectConfig&&Date.now()-this.cachedProjectConfig.timestamp<M.PROJECT_CONFIG_TTL)return this.cachedProjectConfig.config
|
|
313
451
|
const e=a.default.join(this.options.projectPath,".alchemy_lab","project.json")
|
|
314
452
|
try{if(!c.default.existsSync(e))return this.cachedProjectConfig={config:null,timestamp:Date.now()},null
|
|
315
|
-
const t=c.default.readFileSync(e,"utf-8"),
|
|
316
|
-
return this.cachedProjectConfig={config:
|
|
453
|
+
const t=c.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
454
|
+
return this.cachedProjectConfig={config:r,timestamp:Date.now()},r}catch(e){return this.logger.error("Failed to read project config",{error:(0,g.getErrorMessage)(e)}),this.cachedProjectConfig={config:null,timestamp:Date.now()},null}}setOpenCodePort(e){this.openCodePort=e
|
|
317
455
|
const t="http://127.0.0.1:"+e
|
|
318
456
|
this.openCodeUrlCache={url:t,timestamp:Date.now()},this.logger.info("OpenCode port set directly",{port:e}),this.cachedDefaultModel||this.seedDefaultModelFromZen(e).catch((e=>this.logger.debug("Failed to seed zen default model",{error:(0,g.getErrorMessage)(e)})))}async seedDefaultModelFromZen(e){const t=await fetch(`http://localhost:${e}/config/providers`,{signal:AbortSignal.timeout(5e3)})
|
|
319
457
|
if(!t.ok)return
|
|
320
|
-
const
|
|
458
|
+
const r=await t.json(),s=r?.default||{},[o,n]=Object.entries(s)[0]||[]
|
|
321
459
|
if(!o||!n)return
|
|
322
460
|
const i=`${o}/${n}`
|
|
323
461
|
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,g.getErrorMessage)(e)})))}getOpenCodeUrl(){if(this.openCodePort)return"http://127.0.0.1:"+this.openCodePort
|
|
324
462
|
if(this.openCodeUrlCache&&3e4>Date.now()-this.openCodeUrlCache.timestamp)return this.openCodeUrlCache.url
|
|
325
|
-
const e=
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
return this.openCodeUrlCache={url:e,timestamp:Date.now()},e}}catch{}return null}async handleSessionCommand(e,t){const s=this.getOpenCodeUrl()
|
|
330
|
-
if(!s)return{success:!1,error:"OpenCode server not available. Is 'opencode serve' running?"}
|
|
331
|
-
try{switch(e){case"list-sessions":{const[e,r]=await Promise.all([fetch(s+"/session"),fetch(s+"/session/status")])
|
|
463
|
+
const e=(0,P.getOpenCodeUrlFromConfig)(this.options.projectPath)
|
|
464
|
+
return e&&(this.openCodeUrlCache={url:e,timestamp:Date.now()}),e}async handleSessionCommand(e,t){const r=this.getOpenCodeUrl()
|
|
465
|
+
if(!r)return{success:!1,error:"OpenCode server not available. Is 'opencode serve' running?"}
|
|
466
|
+
try{switch(e){case"list-sessions":{const[e,s]=await Promise.all([fetch(r+"/session"),fetch(r+"/session/status")])
|
|
332
467
|
if(!e.ok)return{success:!1,error:"Failed to list sessions: "+e.status}
|
|
333
|
-
const o=await e.json(),n=
|
|
468
|
+
const o=await e.json(),n=s.ok?await s.json():{}
|
|
334
469
|
let i={}
|
|
335
470
|
if(this.projectId)try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions`,{limit:200}),t=e?.data||e||[]
|
|
336
471
|
for(const e of t)e.externalSessionId&&e.agentType&&(i[e.externalSessionId]=e.agentType)}catch{}let a=o.map((e=>({id:e.id,title:e.title||"(untitled)",parentId:e.parentID||void 0,status:n[e.id]?.type||"idle",agentType:i[e.id]||void 0,time:e.time})))
|
|
337
472
|
if(t.status&&(a=a.filter((e=>e.status===t.status))),t.agentType&&(a=a.filter((e=>e.agentType===t.agentType))),t.search){const e=t.search.toLowerCase()
|
|
338
473
|
a=a.filter((t=>t.title?.toLowerCase().includes(e)))}return t.limit&&(a=a.slice(0,t.limit)),{success:!0,data:a}}case"get-messages":{const e=t.sessionId
|
|
339
474
|
if(!e)return{success:!1,error:"sessionId is required"}
|
|
340
|
-
const
|
|
475
|
+
const s=t.limit||50,o=await fetch(`${r}/session/${e}/message?limit=${s}`)
|
|
341
476
|
return o.ok?{success:!0,data:(await o.json()).map((e=>({role:e.info.role,time:e.info.time?.created,parts:e.parts||[]})))}:404===o.status?{success:!1,error:"Session not found"}:{success:!1,error:"Failed to get messages: "+o.status}}case"send-message":{const e=t.sessionId
|
|
342
477
|
if(!e)return{success:!1,error:"sessionId is required"}
|
|
343
478
|
if(!t.message)return{success:!1,error:"message is required"}
|
|
344
|
-
const
|
|
345
|
-
if(t.agent&&(
|
|
346
|
-
|
|
479
|
+
const s={parts:[{type:"text",text:t.message}]}
|
|
480
|
+
if(t.agent&&(s.agent=t.agent),t.variant&&(s.variant=t.variant),t.system&&(s.system=t.system),this.cachedDefaultModel){const[e,...t]=this.cachedDefaultModel.split("/")
|
|
481
|
+
s.model={providerID:e,modelID:t.join("/")}}const o=await fetch(`${r}/session/${e}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)})
|
|
347
482
|
if(!o.ok){const e=await o.text().catch((()=>"Unknown error"))
|
|
348
483
|
return{success:!1,error:`Failed to send message: ${o.status} ${e}`}}return{success:!0,data:{messageId:null}}}case"create-session":{const e={}
|
|
349
484
|
t.parentId&&(e.parentID=t.parentId),t.title&&(e.title=t.title)
|
|
350
|
-
const
|
|
351
|
-
if(!
|
|
352
|
-
const o=await
|
|
485
|
+
const s=await fetch(r+"/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})
|
|
486
|
+
if(!s.ok)return{success:!1,error:"Failed to create session: "+s.status}
|
|
487
|
+
const o=await s.json()
|
|
353
488
|
return{success:!0,data:{id:o.id,title:o.title||t.title||"(untitled)",status:"idle",agentType:t.agent||void 0}}}case"get-session-status":{const e=t.sessionId
|
|
354
489
|
if(!e)return{success:!1,error:"sessionId is required"}
|
|
355
|
-
const[
|
|
490
|
+
const[s,o]=await Promise.all([fetch(r+"/session/status"),fetch(r+"/session")]),n=s.ok?await s.json():{}
|
|
356
491
|
let i,a
|
|
357
492
|
if(o.ok){const t=(await o.json()).find((t=>t.id===e))
|
|
358
|
-
i=t?.title}if(this.projectId)try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),
|
|
359
|
-
|
|
493
|
+
i=t?.title}if(this.projectId)try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),r=t?.data||t
|
|
494
|
+
r?.agentType&&(a=r.agentType)}catch{}return{success:!0,data:{id:e,title:i||"(untitled)",status:n[e]?.type||"idle",agentType:a}}}default:return{success:!1,error:"Unknown session action: "+e}}}catch(e){return{success:!1,error:(0,g.getErrorMessage)(e)}}}async register(){const e=this.readProjectConfig()
|
|
360
495
|
if(!e?.id)throw Error("No project configuration found")
|
|
361
496
|
this.projectId=e.id,this.hookExecutor.setProjectId(this.projectId),this.timerHookScheduler?.setProjectId(this.projectId)
|
|
362
497
|
try{const e=a.default.resolve(__dirname,"../../package.json"),t=JSON.parse(c.default.readFileSync(e,"utf8"))
|
|
363
|
-
this.clientVersion=t.version}catch{}try{const e=t(
|
|
364
|
-
throw e.code="MODULE_NOT_FOUND",e}()))
|
|
498
|
+
this.clientVersion=t.version}catch{}try{const e=t(45)
|
|
365
499
|
this.opencodeVersion=e.version}catch{try{const e=process.env.ALCHEMY_OPENCODE_DIR
|
|
366
500
|
if(e){const t=JSON.parse(c.default.readFileSync(a.default.join(e,"package.json"),"utf8"))
|
|
367
|
-
this.opencodeVersion=t.version}}catch{}}const
|
|
501
|
+
this.opencodeVersion=t.version}}catch{}}const r={projectId:this.projectId,projectPath:this.options.projectPath,serverPort:this.port,hostname:l.default.hostname(),platform:process.platform,clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}
|
|
368
502
|
this.logger.info("Registering with remote",{projectId:this.projectId})
|
|
369
|
-
const
|
|
370
|
-
if(
|
|
371
|
-
const e=
|
|
503
|
+
const s=await this.apiClient.post("/api/servers/register",r)
|
|
504
|
+
if(s.success&&s.data?.serverId){this.serverId=s.data.serverId,this.logger.info("Registered successfully",{serverId:this.serverId})
|
|
505
|
+
const e=s.data.versionWarnings
|
|
372
506
|
if(e&&e.length>0){const t=e.some((e=>"required"===e.severity))
|
|
373
507
|
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`))
|
|
374
|
-
this.versionWarnings=e,t&&(this._pendingUpdateBlock=!0)}}else this.logger.warn("Registration failed, continuing without remote connection",{error:
|
|
375
|
-
e(this.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if(!
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
try{const e=await fetch(s,{headers:{Accept:"text/event-stream"},signal:this.sseAbortController.signal})
|
|
508
|
+
this.versionWarnings=e,t&&(this._pendingUpdateBlock=!0)}}else this.logger.warn("Registration failed, continuing without remote connection",{error:s.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`))
|
|
509
|
+
this.versionWarnings=e,!e.some((e=>"required"===e.severity))&&!this._pendingUpdateBlock||this.updateBlocked||await this.enterUpdateMode()}}startHeartbeat(){this.serverId&&(this.heartbeatInterval=setInterval((async()=>{if(this.projectId)try{const{touchHeartbeat:e}=await t.e(640).then(t.bind(t,2640))
|
|
510
|
+
e(this.projectId)}catch{}try{const e=await this.apiClient.post(`/api/servers/${this.serverId}/heartbeat`,{timestamp:(new Date).toISOString(),clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}),t=e?.data?.versionWarnings??[]
|
|
511
|
+
await this.applyVersionWarnings(t)}catch(e){this.logger.warn("Heartbeat failed",{error:(0,g.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,g.getErrorMessage)(e)})))}async connectToOpenCodeSSE(){const e=this.getOpenCodeUrl()
|
|
512
|
+
if(!e)return this.logger.debug("SSE forwarder: OpenCode URL unavailable, will retry"),void setTimeout((()=>this.connectToOpenCodeSSE()),5e3)
|
|
513
|
+
const t=e+"/event"
|
|
514
|
+
this.logger.info("SSE forwarder connecting",{url:t}),this.sseAbortController=new AbortController
|
|
515
|
+
try{const e=await fetch(t,{headers:{Accept:"text/event-stream"},signal:this.sseAbortController.signal})
|
|
383
516
|
if(!e.ok||!e.body)return this.logger.warn("SSE forwarder: upstream error",{status:e.status}),void setTimeout((()=>this.connectToOpenCodeSSE()),5e3)
|
|
384
|
-
const
|
|
385
|
-
let o="";(async()=>{try{for(;;){const{done:e,value:
|
|
517
|
+
const r=e.body.getReader(),s=new TextDecoder
|
|
518
|
+
let o="";(async()=>{try{for(;;){const{done:e,value:t}=await r.read()
|
|
386
519
|
if(e)break
|
|
387
|
-
o+=
|
|
520
|
+
o+=s.decode(t,{stream:!0})
|
|
388
521
|
const n=o.split("\n")
|
|
389
522
|
o=n.pop()||""
|
|
390
|
-
for(const e of n)if(e.startsWith("data: "))try{const t=JSON.parse(e.slice(6)),
|
|
391
|
-
this.sseEventBuffer.push({type:
|
|
523
|
+
for(const e of n)if(e.startsWith("data: "))try{const t=JSON.parse(e.slice(6)),r=t.type||"unknown"
|
|
524
|
+
this.sseEventBuffer.push({type:r,data:t}),this.handleSSEHookEvent(t).catch((e=>this.logger.warn("SSE hook event processing failed",{type:t?.type,error:(0,g.getErrorMessage)(e)}))),"session.idle"===r&&this.flushSSEEvents().catch((e=>this.logger.debug("Immediate SSE flush failed",{error:(0,g.getErrorMessage)(e)})))}catch{}}}catch(e){"AbortError"===e?.name||(e?.message+"").includes("terminated")||this.logger.debug("SSE forwarder stream ended",{error:(0,g.getErrorMessage)(e)})}this.sseForwarderInterval&&(this.logger.info("SSE forwarder reconnecting in 3s"),setTimeout((()=>this.connectToOpenCodeSSE()),3e3))})()}catch(e){"AbortError"===e?.name||(e?.message+"").includes("terminated")||(this.logger.debug("SSE forwarder connection failed",{error:(0,g.getErrorMessage)(e)}),this.sseForwarderInterval&&setTimeout((()=>this.connectToOpenCodeSSE()),5e3))}}async handleSSEHookEvent(e){const t=e?.type
|
|
392
525
|
if(t&&this.projectId)if("session.idle"===t){const t=e?.properties?.sessionID||e?.sessionID||""
|
|
393
526
|
if(!t)return
|
|
394
|
-
this.logger.debug("SSE hook: session_idle",{sessionId:t}),await this.hookExecutor.processEvent({eventType:"session_idle",sessionId:t,variables:{sessionId:t}})}else if("session.created"===t){const t=e?.properties?.info||e?.info,
|
|
395
|
-
if(!
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
if(
|
|
399
|
-
|
|
400
|
-
|
|
527
|
+
this.logger.debug("SSE hook: session_idle",{sessionId:t}),await this.hookExecutor.processEvent({eventType:"session_idle",sessionId:t,variables:{sessionId:t}})}else if("session.created"===t){const t=e?.properties?.info||e?.info,r=t?.id||""
|
|
528
|
+
if(!r)return
|
|
529
|
+
const s=t?.metadata
|
|
530
|
+
if(s?.agentType&&this.projectId){const e=t?.parentID||null
|
|
531
|
+
this.logger.info("SSE-driven cli-sessions upsert",{sessionId:r,agentType:s.agentType,parentExternalIdFromOpenCode:e,note:"no parent fields included in body; preserves existing parent if record exists"}),this.apiClient.post(`/api/projects/${this.projectId}/cli-sessions`,{externalSessionId:r,providerId:"opencode",agentType:s.agentType,status:"active",specialization:s.specialization||void 0}).catch((e=>{this.logger.debug("SSE session registration (idempotent)",{sessionId:r,error:(0,g.getErrorMessage)(e)})}))}setTimeout((async()=>{this.logger.debug("SSE hook: session_created (delayed)",{sessionId:r}),await this.hookExecutor.processEvent({eventType:"session_created",sessionId:r,variables:{sessionId:r}})}),500)}}async flushSSEEvents(){if(!this.projectId)return
|
|
532
|
+
if(0===this.sseEventBuffer.length&&0===this.droppedEventCount)return
|
|
533
|
+
if(!this.hasRemoteEventConsumers){if(this.sseEventBuffer.length>500){const e=this.sseEventBuffer.length-500
|
|
534
|
+
this.sseEventBuffer.splice(0,e),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=e}return}const e=this.sseEventBuffer.splice(0),t=this.droppedEventCount>0?{type:"alchemy.events.dropped",data:{count:this.droppedEventCount,firstDropAt:this.firstDropAt,reportedAt:Date.now()}}:null
|
|
535
|
+
t&&e.unshift(t)
|
|
536
|
+
try{await this.apiClient.post(`/api/projects/${this.projectId}/opencode-events`,{events:e}),t&&(this.logger.warn("Reported dropped SSE events to remote",{count:t.data.count,firstDropAt:t.data.firstDropAt}),this.droppedEventCount=0,this.firstDropAt=0)}catch(r){this.logger.debug("SSE event flush failed",{error:(0,g.getErrorMessage)(r),count:e.length})
|
|
537
|
+
const s=t?e.slice(1):e
|
|
538
|
+
if(this.sseEventBuffer.unshift(...s),this.sseEventBuffer.length>500){const e=this.sseEventBuffer.length-500
|
|
539
|
+
this.sseEventBuffer.splice(0,e),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=e}}}startPolling(){this.projectId&&(this.isPolling=!0,this.pollAbortController=new AbortController,this.logger.info("Starting action long-polling",{projectId:this.projectId}),this.longPollLoop())}async longPollLoop(){for(;this.isPolling;)try{await this.pollOnce()}catch(e){if(!this.isPolling)return
|
|
401
540
|
this.logger.debug("Long-poll error, retrying in 5s",{error:(0,g.getErrorMessage)(e)}),await new Promise((e=>setTimeout(e,5e3)))}}async pollOnce(){if(this.isPolling&&this.projectId&&!this.updateBlocked)try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=30000`,t={"Content-Type":"application/json",Accept:"application/json"}
|
|
402
541
|
this.apiClient.authToken&&(t.Authorization="Bearer "+this.apiClient.authToken)
|
|
403
|
-
const
|
|
404
|
-
t["X-Service-Secret"]=
|
|
405
|
-
const
|
|
406
|
-
if(!
|
|
407
|
-
const o=await
|
|
542
|
+
const r=process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"
|
|
543
|
+
t["X-Service-Secret"]=r,this.clientVersion&&(t["X-Client-Version"]=this.clientVersion),this.opencodeVersion&&(t["X-OpenCode-Version"]=this.opencodeVersion)
|
|
544
|
+
const s=await fetch(e,{headers:t,signal:AbortSignal.timeout(35e3)})
|
|
545
|
+
if(!s.ok)throw Error("Poll failed with status "+s.status)
|
|
546
|
+
const o=await s.json(),n={success:!0,data:o.data||o}
|
|
408
547
|
if(n.success&&n.data&&void 0!==n.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=n.data.hasRemoteEventConsumers),n.success&&n.data?.settings){const{defaultModel:e,autoJournal:t}=n.data.settings
|
|
409
548
|
if(void 0!==e&&this.updateDefaultModel(e),void 0!==t&&t!==this.cachedAutoJournal){this.logger.info("autoJournal setting changed",{oldValue:this.cachedAutoJournal,newValue:t})
|
|
410
549
|
const e=new p.ProjectConfigService(this.logger)
|
|
411
|
-
await e.saveProjectConfig(this.options.projectPath,t),this.cachedAutoJournal=t}}if(n.success&&n.data?.
|
|
412
|
-
|
|
550
|
+
await e.saveProjectConfig(this.options.projectPath,t),this.cachedAutoJournal=t}}if(n.success&&Array.isArray(n.data?.invalidations)&&n.data.invalidations.length>0){const e=n.data.invalidations
|
|
551
|
+
this.logger.info("Received cache invalidations from remote",{invalidations:e}),e.includes("hooks")&&this.timerHookScheduler?.invalidateCache(),e.includes("quests")&&this.questTickScheduler?.invalidateCache(),e.includes("alchemist")&&this.questTickScheduler?.invalidateAlchemistCache()}if(n.success&&Array.isArray(n.data?.versionWarnings)&&this.applyVersionWarnings(n.data.versionWarnings),n.success&&n.data?.actions?.length>0){const e=[],t=[]
|
|
552
|
+
for(const r of n.data.actions)"opencode-proxy"===r.type||"run-tests"===r.type?e.push(r):t.push(r)
|
|
413
553
|
for(const t of e)this.processAction(t).catch((e=>this.logger.warn("Proxy action failed",{actionId:t.id,error:(0,g.getErrorMessage)(e)})))
|
|
414
554
|
if(t.length>0&&!this.isProcessingAction){this.isProcessingAction=!0
|
|
415
555
|
try{for(const e of t)await this.processAction(e)}finally{this.isProcessingAction=!1}}}}catch(e){throw e}}async syncProjectSettings(){try{const e=await this.apiClient.get("/api/projects/"+this.projectId)
|
|
416
|
-
if(e.success&&e.data?.metadata){const{autoJournal:t,defaultModel:
|
|
417
|
-
if(void 0!==
|
|
556
|
+
if(e.success&&e.data?.metadata){const{autoJournal:t,defaultModel:r}=e.data.metadata
|
|
557
|
+
if(void 0!==r&&this.updateDefaultModel(r),void 0!==t&&t!==this.cachedAutoJournal){this.logger.info("autoJournal setting changed, syncing to local config",{oldValue:this.cachedAutoJournal,newValue:t})
|
|
418
558
|
const e=new p.ProjectConfigService(this.logger)
|
|
419
559
|
await e.saveProjectConfig(this.options.projectPath,t),this.cachedAutoJournal=t}}}catch(e){this.logger.debug("Settings sync failed",{error:(0,g.getErrorMessage)(e)})}}async processAction(e){this.logger.info("Processing action",{actionId:e.id,type:e.type})
|
|
420
560
|
const t=Date.now()
|
|
421
|
-
try{const
|
|
422
|
-
await this.apiClient.post(`/api/actions/${e.id}/result`,{success:
|
|
423
|
-
await this.apiClient.post(`/api/actions/${e.id}/result`,{success:!1,error:
|
|
424
|
-
if(!
|
|
425
|
-
if(["list-notifications","get-notification","respond-to-notification"].includes(
|
|
426
|
-
return void t.json(e)}if(["list-sessions","get-messages","send-message","create-session","get-session-status"].includes(
|
|
427
|
-
return void t.json(e)}if(["list-requests","get-request"].includes(
|
|
428
|
-
return void t.json(e)}const n=await this.cliBridge.sendCommand(s
|
|
561
|
+
try{const r={projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},s=await(0,d.executeAction)(e.type,e.payload||{},r)
|
|
562
|
+
await this.apiClient.post(`/api/actions/${e.id}/result`,{success:s.success,result:s.data,error:s.message||s.error,duration:Date.now()-t}),this.logger.info("Action processed",{actionId:e.id,type:e.type,success:s.success,duration:Date.now()-t})}catch(r){const s=(0,g.getErrorMessage)(r)
|
|
563
|
+
await this.apiClient.post(`/api/actions/${e.id}/result`,{success:!1,error:s,duration:Date.now()-t}),this.logger.error("Action failed",{actionId:e.id,type:e.type,error:s})}}async handleCLIBridgeCommand(e,t){try{const{action:r,params:s,timeout:o}=e.body
|
|
564
|
+
if(!r)return void t.status(400).json({success:!1,error:"action is required"})
|
|
565
|
+
if(["list-notifications","get-notification","respond-to-notification"].includes(r)){const e=await this.handleNotificationCommand(r,s||{})
|
|
566
|
+
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||{})
|
|
567
|
+
return void t.json(e)}if(["list-requests","get-request"].includes(r)){const e=await this.handleRequestCommand(r,s||{})
|
|
568
|
+
return void t.json(e)}const n=await this.cliBridge.sendCommand(r,s||{},o)
|
|
429
569
|
t.json(n)}catch(e){t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleNotificationCommand(e,t){if(!this.projectId)return{success:!1,error:"Project not registered yet"}
|
|
430
|
-
const
|
|
570
|
+
const r=this.projectId
|
|
431
571
|
try{switch(e){case"list-notifications":{const e={limit:t.limit||30}
|
|
432
|
-
return t.sessionId&&(e.sessionId=t.sessionId),t.messageType&&(e.messageType=t.messageType),t.status&&(e.status=t.status),t.readStatus&&(e.readStatus=t.readStatus),{success:!0,data:(await this.apiClient.get(`/api/projects/${
|
|
572
|
+
return t.sessionId&&(e.sessionId=t.sessionId),t.messageType&&(e.messageType=t.messageType),t.status&&(e.status=t.status),t.readStatus&&(e.readStatus=t.readStatus),{success:!0,data:(await this.apiClient.get(`/api/projects/${r}/session-messages`,e)).data}}case"get-notification":return t.messageCode?{success:!0,data:(await this.apiClient.get(`/api/projects/${r}/session-messages/by-code/${encodeURIComponent(t.messageCode)}`)).data}:{success:!1,error:"messageCode is required"}
|
|
433
573
|
case"respond-to-notification":{if(!t.messageCode)return{success:!1,error:"messageCode is required"}
|
|
434
574
|
const e=t.messageCode
|
|
435
|
-
return{success:!0,data:(await this.apiClient.post(`/api/projects/${
|
|
436
|
-
const
|
|
575
|
+
return{success:!0,data:(await this.apiClient.post(`/api/projects/${r}/session-messages/${encodeURIComponent(e)}/answer`,{respondedBySessionId:t.respondedBySessionId||"cli-bridge",text:t.responseText,selectedOptions:t.selectedOptions,notes:t.notes})).data}}default:return{success:!1,error:"Unknown notification action: "+e}}}catch(e){return{success:!1,error:(0,g.getErrorMessage)(e)}}}async handleRequestCommand(e,t){if(!this.projectId)return{success:!1,error:"Project not registered yet"}
|
|
576
|
+
const r=this.projectId
|
|
437
577
|
try{switch(e){case"list-requests":{const e={}
|
|
438
|
-
return t.status&&(e.status=t.status),t.type&&(e.type=t.type),t.limit&&(e.limit=t.limit),t.offset&&(e.offset=t.offset),t.sortBy&&(e.sortBy=t.sortBy),t.sortDirection&&(e.sortDirection=t.sortDirection),{success:!0,data:(await this.apiClient.get(`/api/projects/${
|
|
439
|
-
const e=encodeURIComponent(t.requestId),[
|
|
440
|
-
return{success:!0,data:{...
|
|
578
|
+
return t.status&&(e.status=t.status),t.type&&(e.type=t.type),t.limit&&(e.limit=t.limit),t.offset&&(e.offset=t.offset),t.sortBy&&(e.sortBy=t.sortBy),t.sortDirection&&(e.sortDirection=t.sortDirection),{success:!0,data:(await this.apiClient.get(`/api/projects/${r}/requests`,e)).data}}case"get-request":{if(!t.requestId)return{success:!1,error:"requestId is required"}
|
|
579
|
+
const e=encodeURIComponent(t.requestId),[s,o]=await Promise.all([this.apiClient.get(`/api/projects/${r}/requests/${e}`),this.apiClient.get(`/api/projects/${r}/tasks`,{requestId:t.requestId})])
|
|
580
|
+
return{success:!0,data:{...s.data,tasks:o.data||[]}}}default:return{success:!1,error:"Unknown request action: "+e}}}catch(e){return{success:!1,error:(0,g.getErrorMessage)(e)}}}handleCLIBridgeStatus(e,t){t.json({uiConnected:this.cliBridge.isUIConnected()})}async start(){this.logger.info("Starting AlchemyActionServer",{projectPath:this.options.projectPath,port:this.port})
|
|
441
581
|
for(let e=0;10>e;e++)try{await new Promise(((e,t)=>{try{this.server=this.app.listen(this.port,"127.0.0.1",(()=>{this.logger.info("HTTP server started",{port:this.port}),this.cliBridge.attach(this.server),e()})),this.server.on("error",(e=>{e.code,t(e)}))}catch(e){t(e)}}))
|
|
442
582
|
break}catch(t){if("EADDRINUSE"===t?.code&&9>e){this.logger.info(`Port ${this.port} in use, trying ${this.port+1}`),this.port++
|
|
443
583
|
continue}throw Error(`Port ${this.port} is already in use (tried 10 ports)`)}try{const e=a.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
|
|
444
584
|
c.default.mkdirSync(a.default.dirname(e),{recursive:!0}),c.default.writeFileSync(e,JSON.stringify({port:this.port},null,2),"utf-8"),this.logger.info("Wrote action-server port file",{port:this.port,path:e})}catch(e){this.logger.warn("Failed to write action-server port file",{error:(0,g.getErrorMessage)(e)})}try{const e=this.readProjectConfig()
|
|
445
|
-
if(e?.id){const{upsertEntry:
|
|
446
|
-
|
|
585
|
+
if(e?.id){const{upsertEntry:r}=await t.e(640).then(t.bind(t,2640))
|
|
586
|
+
r({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,g.getErrorMessage)(e)})}try{await this.register(),this.startHeartbeat()}catch(e){const t=(0,g.getErrorMessage)(e)
|
|
447
587
|
t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=5000`,t={"Content-Type":"application/json",Accept:"application/json"}
|
|
448
588
|
this.apiClient.authToken&&(t.Authorization="Bearer "+this.apiClient.authToken)
|
|
449
|
-
const
|
|
450
|
-
t["X-Service-Secret"]=
|
|
451
|
-
const
|
|
452
|
-
if(
|
|
453
|
-
void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel}))}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,g.getErrorMessage)(e)})}this.startPolling(),this.projectId&&this.startSSEEventForwarder(),this.projectId&&this.startStuckSessionWatchdog(),this.projectId&&this.startTimerHookScheduler(),this.startMcpStaleCleanup()}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,g.getErrorMessage)(t)})}}this.trackedChildPids.clear()
|
|
589
|
+
const r=process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"
|
|
590
|
+
t["X-Service-Secret"]=r
|
|
591
|
+
const s=await fetch(e,{headers:t,signal:AbortSignal.timeout(8e3)})
|
|
592
|
+
if(s.ok){const e=await s.json(),t=e?.data?.settings||e?.settings
|
|
593
|
+
void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel}))}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,g.getErrorMessage)(e)})}this.startPolling(),this.projectId&&this.startSSEEventForwarder(),this.projectId&&this.startStuckSessionWatchdog(),this.projectId&&this.startTimerHookScheduler(),this.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,g.getErrorMessage)(t)})}}this.trackedChildPids.clear()
|
|
454
594
|
try{const e=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
455
|
-
if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),
|
|
456
|
-
if(
|
|
457
|
-
t.json({success:!0,data:
|
|
458
|
-
const{sessionId:
|
|
459
|
-
this.logger.info("Test-injected stuck session",{sessionId:o.sessionId}),t.json({success:!0,data:o})}async handleRetryStuckSession(e,t){const{sessionId:
|
|
595
|
+
if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
596
|
+
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,g.getErrorMessage)(e)})}}}}catch{}}startTimerHookScheduler(){this.timerHookScheduler=new b.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 I.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 w.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()??[]
|
|
597
|
+
t.json({success:!0,data:r})}handleTestInjectStuckSession(e,t){if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"Watchdog not running"})
|
|
598
|
+
const{sessionId:r,title:s}=e.body||{},o=this.stuckSessionWatchdog.injectTestStuck(r,s)
|
|
599
|
+
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
|
|
460
600
|
if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"Watchdog not running"})
|
|
461
|
-
const
|
|
462
|
-
|
|
601
|
+
const s=await this.stuckSessionWatchdog.retrySession(r)
|
|
602
|
+
s.success?t.json({success:!0}):t.status(400).json({success:!1,error:s.error})}async enterUpdateMode(){if(!this.updateBlocked){this.updateBlocked=!0,this.logger.error("Entering update-required mode \u2014 shutting down all services"),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\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("\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\x1b[0m")
|
|
463
603
|
for(const e of this.versionWarnings)console.error(`\x1b[31m ${e.message}\x1b[0m`)
|
|
464
|
-
console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.timerHookScheduler?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
|
|
604
|
+
console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.timerHookScheduler?.stop(),this.questTickScheduler?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
|
|
465
605
|
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.clear(),this.cliBridge.close(),this.logger.info("Update-required mode active. HTTP server still running on port "+this.port)}}async stop(){this.logger.info("Stopping AlchemyActionServer...")
|
|
466
|
-
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.timerHookScheduler?.stop(),this.stopSSEEventForwarder(),this.mcpStaleCleanupInterval&&(clearInterval(this.mcpStaleCleanupInterval),this.mcpStaleCleanupInterval=void 0)
|
|
606
|
+
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.timerHookScheduler?.stop(),this.questTickScheduler?.stop(),this.stopSSEEventForwarder(),this.mcpStaleCleanupInterval&&(clearInterval(this.mcpStaleCleanupInterval),this.mcpStaleCleanupInterval=void 0)
|
|
467
607
|
try{const e=a.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
|
|
468
608
|
c.default.existsSync(e)&&c.default.unlinkSync(e)}catch{}if(this.projectId)try{const{removeEntry:e}=await t.e(640).then(t.bind(t,2640))
|
|
469
609
|
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)
|
|
470
|
-
this.server.close((()=>{clearTimeout(t),e()}))}else e()})),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=
|
|
471
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.ensureRequestWorktree=async function(e,t,i){const a="requests/"+t,c=(0,
|
|
472
|
-
if((0,
|
|
473
|
-
const d=(0,o.simpleGit)(e);(0,
|
|
610
|
+
this.server.close((()=>{clearTimeout(t),e()}))}else e()})),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=M,M.PROJECT_CONFIG_TTL=3e4},2424(e,exports,t){"use strict"
|
|
611
|
+
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)
|
|
612
|
+
if((0,r.existsSync)(l))return void i.info("Worktree already exists on disk, skipping pre-spawn creation",{requestId:t,worktreePath:l})
|
|
613
|
+
const d=(0,o.simpleGit)(e);(0,r.mkdirSync)(c,{recursive:!0})
|
|
474
614
|
try{await d.raw(["worktree","prune"])}catch{}const u=await d.branch(),h=u.all.includes(a)
|
|
475
615
|
try{if(h)await d.raw(["worktree","add",l,a]),i.info("Pre-spawn: created worktree for existing branch",{requestId:t,branchName:a,worktreePath:l})
|
|
476
616
|
else{const e=u.all.includes("main")?"main":"master"
|
|
477
|
-
await d.raw(["worktree","add","-b",a,l,e]),i.info("Pre-spawn: created worktree with new branch from latest main",{requestId:t,branchName:a,mainBranch:e,worktreePath:l})}}catch(e){const
|
|
478
|
-
i.warn("Pre-spawn: failed to create worktree (MCP process will retry)",{requestId:t,error:
|
|
479
|
-
const
|
|
480
|
-
function
|
|
481
|
-
if(!
|
|
617
|
+
await d.raw(["worktree","add","-b",a,l,e]),i.info("Pre-spawn: created worktree with new branch from latest main",{requestId:t,branchName:a,mainBranch:e,worktreePath:l})}}catch(e){const r=e instanceof Error?e.message:e+""
|
|
618
|
+
i.warn("Pre-spawn: failed to create worktree (MCP process will retry)",{requestId:t,error:r})}}
|
|
619
|
+
const r=t(9896),s=t(6928),o=t(3807),n=t(4139)},2782(e,exports,t){"use strict"
|
|
620
|
+
function r(){return"true"===process.env.ENABLE_DEBUG_TOOLS?.toLowerCase()}function s(e={}){const t={...function(e){const t={}
|
|
621
|
+
if(!r())return t.level=(0,l.isDevBuild)()?c.LogLevel.INFO:c.LogLevel.ERROR,t.logToFile=!1,t
|
|
482
622
|
if("true"===process.env.DEBUG_LOGGING?.toLowerCase()){t.level=c.LogLevel.DEBUG,t.logToFile=!0
|
|
483
|
-
const
|
|
484
|
-
t.logFilePath=i.default.join(
|
|
485
|
-
return
|
|
486
|
-
if(console&&
|
|
487
|
-
a.default.existsSync(t)||a.default.mkdirSync(t,{recursive:!0})}(
|
|
623
|
+
const r=e||process.cwd()
|
|
624
|
+
t.logFilePath=i.default.join(r,"logs","debug.log")}const s=process.env.DEBUG_LEVEL?.toLowerCase()
|
|
625
|
+
return s&&Object.values(c.LogLevel).includes(s)&&(t.level=s),t}(e.baseDir),...e},{level:o=c.LogLevel.INFO,serviceName:g="mcp-service",logToFile:p=!1,logFilePath:m="./logs/app.log",console=!0}=t,f=[]
|
|
626
|
+
if(console&&f.push(new n.default.transports.Console({format:d,stderrLevels:["error","warn","info","debug"]})),p){!function(e){const t=i.default.dirname(e)
|
|
627
|
+
a.default.existsSync(t)||a.default.mkdirSync(t,{recursive:!0})}(m)
|
|
488
628
|
const e=o===c.LogLevel.DEBUG&&"true"===process.env.DEBUG_LOGGING?.toLowerCase()
|
|
489
|
-
|
|
490
|
-
return o===c.LogLevel.DEBUG&&p&&
|
|
491
|
-
return{error(e,t){
|
|
492
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.createLogger=
|
|
629
|
+
f.push(new n.default.transports.File({filename:m,format:e?h:u}))}const y=n.default.createLogger({level:o,defaultMeta:{service:g},transports:f})
|
|
630
|
+
return o===c.LogLevel.DEBUG&&p&&r()&&y.info("Enhanced logging initialized",{level:o,serviceName:g,logToFile:p,logFilePath:m,debugLogging:process.env.DEBUG_LOGGING}),{error(e,t){y.error(e,t)},warn(e,t){y.warn(e,t)},info(e,t){y.info(e,t)},debug(e,t){y.debug(e,t)},child(e){const r=y.child(e)
|
|
631
|
+
return{error(e,t){r.error(e,t)},warn(e,t){r.warn(e,t)},info(e,t){r.info(e,t)},debug(e,t){r.debug(e,t)},child(e){return s({...t,serviceName:`${g}:${e.component||"nested"}`})}}}}}var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
632
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.createLogger=s,exports.createEnvLogger=function(e={}){return s(e)},exports.isDebugToolsEnabled=r,exports.isDebugLoggingEnabled=function(){return!!r()&&"true"===process.env.DEBUG_LOGGING?.toLowerCase()},exports.getDebugLevelFromEnv=function(){if(!r())return c.LogLevel.INFO
|
|
493
633
|
const e=process.env.DEBUG_LEVEL?.toLowerCase()
|
|
494
634
|
return e&&Object.values(c.LogLevel).includes(e)?e:c.LogLevel.INFO}
|
|
495
|
-
const n=o(t(5124)),i=o(t(6928)),a=o(t(9896)),c=t(6540),l=t(7960),d=n.default.format.combine(n.default.format.timestamp(),n.default.format.colorize(),n.default.format.printf((({level:e,message:t,timestamp:s
|
|
496
|
-
return`${
|
|
635
|
+
const n=o(t(5124)),i=o(t(6928)),a=o(t(9896)),c=t(6540),l=t(7960),d=n.default.format.combine(n.default.format.timestamp(),n.default.format.colorize(),n.default.format.printf((({level:e,message:t,timestamp:r,...s})=>`${r} [${e}]: ${t}${Object.keys(s).length?" "+JSON.stringify(s,null,0):""}`))),u=n.default.format.combine(n.default.format.timestamp(),n.default.format.json()),h=n.default.format.combine(n.default.format.timestamp(),n.default.format.printf((({level:e,message:t,timestamp:r,service:s,component:o,...n})=>{const i=s?`[${s}]`:"",a=o?`[${o}]`:"",c=Object.keys(n).length?" "+JSON.stringify(n,null,2):""
|
|
636
|
+
return`${r} ${i}${a} [${e.toUpperCase()}]: ${t}${c}`}))),g=s({logToFile:!1,console:!0,level:c.LogLevel.INFO})
|
|
497
637
|
exports.default=g},2928(e,exports,t){"use strict"
|
|
498
638
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.StuckSessionWatchdog=void 0
|
|
499
|
-
const
|
|
500
|
-
exports.StuckSessionWatchdog=class{constructor(e){this.stuckSessions=new Map,this.resolvedRecently=new Set,this.notifiedSessions=new Set,this.testInjectedIds=new Set,this.logger=e.logger,this.getOpenCodeUrl=e.getOpenCodeUrl,this.onStuckDetected=e.onStuckDetected,this.onStuckResolved=e.onStuckResolved,this.checkIntervalMs=e.checkIntervalMs??3e4,this.stuckThresholdMs=e.stuckThresholdMs??9e4,this.maxRetries=e.maxRetries??3,this.getDefaultModel=e.getDefaultModel}start(){this.intervalTimer||(this.logger.info("Stuck session watchdog starting",{checkInterval:this.checkIntervalMs,stuckThreshold:this.stuckThresholdMs,maxRetries:this.maxRetries}),setTimeout((()=>this.check().catch((()=>{}))),5e3),this.intervalTimer=setInterval((()=>{this.check().catch((()=>{}))}),this.checkIntervalMs))}stop(){this.intervalTimer&&(clearInterval(this.intervalTimer),this.intervalTimer=void 0),this.stuckSessions.clear(),this.notifiedSessions.clear(),this.resolvedRecently.clear(),this.logger.info("Stuck session watchdog stopped")}getStuckSessions(){return Array.from(this.stuckSessions.values())}isStuck(e){return this.stuckSessions.has(e)}injectTestStuck(e,t){const
|
|
501
|
-
return this.stuckSessions.set(s
|
|
502
|
-
if(e)try{const[t,
|
|
503
|
-
if(!t.ok||!
|
|
504
|
-
const o=await t.json(),n=await
|
|
505
|
-
await Promise.all(i.map((async t=>{try{const
|
|
506
|
-
if(!
|
|
507
|
-
const
|
|
508
|
-
if(!
|
|
509
|
-
const o=
|
|
639
|
+
const r=t(8824)
|
|
640
|
+
exports.StuckSessionWatchdog=class{constructor(e){this.stuckSessions=new Map,this.resolvedRecently=new Set,this.notifiedSessions=new Set,this.testInjectedIds=new Set,this.logger=e.logger,this.getOpenCodeUrl=e.getOpenCodeUrl,this.onStuckDetected=e.onStuckDetected,this.onStuckResolved=e.onStuckResolved,this.checkIntervalMs=e.checkIntervalMs??3e4,this.stuckThresholdMs=e.stuckThresholdMs??9e4,this.maxRetries=e.maxRetries??3,this.getDefaultModel=e.getDefaultModel}start(){this.intervalTimer||(this.logger.info("Stuck session watchdog starting",{checkInterval:this.checkIntervalMs,stuckThreshold:this.stuckThresholdMs,maxRetries:this.maxRetries}),setTimeout((()=>this.check().catch((()=>{}))),5e3),this.intervalTimer=setInterval((()=>{this.check().catch((()=>{}))}),this.checkIntervalMs))}stop(){this.intervalTimer&&(clearInterval(this.intervalTimer),this.intervalTimer=void 0),this.stuckSessions.clear(),this.notifiedSessions.clear(),this.resolvedRecently.clear(),this.logger.info("Stuck session watchdog stopped")}getStuckSessions(){return Array.from(this.stuckSessions.values())}isStuck(e){return this.stuckSessions.has(e)}injectTestStuck(e,t){const r=e||"test-stuck-"+Date.now(),s={sessionId:r,messageId:"test-msg-"+Date.now(),detectedAt:Date.now(),retryCount:0,lastMessageCreated:Date.now()-this.stuckThresholdMs-1e3,sessionTitle:t||"Test Stuck Session"}
|
|
641
|
+
return this.stuckSessions.set(r,s),this.testInjectedIds.add(r),this.notifiedSessions.add(r),this.onStuckDetected?.(s),s}clearTestStuck(e){return!!this.testInjectedIds.has(e)&&(this.testInjectedIds.delete(e),this.stuckSessions.delete(e),this.notifiedSessions.delete(e),this.onStuckResolved?.(e),!0)}async check(){const e=this.getOpenCodeUrl()
|
|
642
|
+
if(e)try{const[t,s]=await Promise.all([fetch(e+"/session",{signal:AbortSignal.timeout(5e3)}),fetch(e+"/session/status",{signal:AbortSignal.timeout(5e3)})])
|
|
643
|
+
if(!t.ok||!s.ok)return
|
|
644
|
+
const o=await t.json(),n=await s.json(),i=o.filter((e=>"running"===n[e.id]?.type)),a=new Set
|
|
645
|
+
await Promise.all(i.map((async t=>{try{const r=await fetch(`${e}/session/${t.id}/message?limit=5`,{signal:AbortSignal.timeout(5e3)})
|
|
646
|
+
if(!r.ok)return
|
|
647
|
+
const s=[...await r.json()].reverse().find((e=>"assistant"===e.info.role))
|
|
648
|
+
if(!s)return
|
|
649
|
+
const o=s.info
|
|
510
650
|
if(o.time.completed)return
|
|
511
651
|
const n=o.tokens
|
|
512
652
|
if(n&&(n.output>0||n.reasoning>0))return
|
|
513
|
-
const i=
|
|
653
|
+
const i=s.parts||[]
|
|
514
654
|
if(i.some((e=>"tool"===e.type&&e.state&&("pending"===e.state.status||"running"===e.state.status))))return
|
|
515
655
|
if(i.some((e=>"text"===e.type&&e.text&&e.text.length>0)))return
|
|
516
656
|
const c=Date.now()-o.time.created
|
|
517
657
|
if(c<this.stuckThresholdMs)return
|
|
518
658
|
if(a.add(t.id),!this.stuckSessions.has(t.id)){const e={sessionId:t.id,messageId:o.id,detectedAt:Date.now(),retryCount:0,lastMessageCreated:o.time.created,sessionTitle:t.title}
|
|
519
|
-
this.stuckSessions.set(t.id,e),this.logger.warn("Session detected as stuck",{sessionId:t.id,title:t.title,messageAge:Math.round(c/1e3)}),e.retryCount<this.maxRetries?(this.logger.info("Auto-retrying stuck session",{sessionId:t.id,retryCount:e.retryCount}),this.retrySession(t.id).then((
|
|
520
|
-
for(const[e]of this.stuckSessions)a.has(e)||this.testInjectedIds.has(e)||(this.stuckSessions.delete(e),this.notifiedSessions.delete(e),this.resolvedRecently.add(e),this.logger.info("Session no longer stuck",{sessionId:e}),this.onStuckResolved?.(e),setTimeout((()=>this.resolvedRecently.delete(e)),6e4))}catch(e){this.logger.debug("Stuck session check failed",{error:(0,
|
|
659
|
+
this.stuckSessions.set(t.id,e),this.logger.warn("Session detected as stuck",{sessionId:t.id,title:t.title,messageAge:Math.round(c/1e3)}),e.retryCount<this.maxRetries?(this.logger.info("Auto-retrying stuck session",{sessionId:t.id,retryCount:e.retryCount}),this.retrySession(t.id).then((r=>{r.success||(this.logger.warn("Auto-retry failed",{sessionId:t.id,error:r.error}),this.notifiedSessions.has(t.id)||(this.notifiedSessions.add(t.id),this.onStuckDetected?.(e)))})).catch((()=>{}))):this.notifiedSessions.has(t.id)||(this.notifiedSessions.add(t.id),this.onStuckDetected?.(e))}}catch(e){this.logger.debug("Failed to check session for stuck state",{sessionId:t.id,error:(0,r.getErrorMessage)(e)})}})))
|
|
660
|
+
for(const[e]of this.stuckSessions)a.has(e)||this.testInjectedIds.has(e)||(this.stuckSessions.delete(e),this.notifiedSessions.delete(e),this.resolvedRecently.add(e),this.logger.info("Session no longer stuck",{sessionId:e}),this.onStuckResolved?.(e),setTimeout((()=>this.resolvedRecently.delete(e)),6e4))}catch(e){this.logger.debug("Stuck session check failed",{error:(0,r.getErrorMessage)(e)})}}async retrySession(e){const t=this.getOpenCodeUrl()
|
|
521
661
|
if(!t)return{success:!1,error:"OpenCode server not available"}
|
|
522
|
-
const
|
|
523
|
-
if(!
|
|
524
|
-
if(
|
|
662
|
+
const s=this.stuckSessions.get(e)
|
|
663
|
+
if(!s)return{success:!1,error:"Session is not currently flagged as stuck"}
|
|
664
|
+
if(s.retryCount>=this.maxRetries)return{success:!1,error:`Max retries (${this.maxRetries}) exceeded for this session`}
|
|
525
665
|
try{this.logger.info("Retrying stuck session: aborting",{sessionId:e})
|
|
526
|
-
const
|
|
527
|
-
|
|
666
|
+
const r=await fetch(`${t}/session/${e}/abort`,{method:"POST",signal:AbortSignal.timeout(5e3)})
|
|
667
|
+
r.ok||this.logger.warn("Abort returned non-OK (may already be idle)",{sessionId:e,status:r.status}),await new Promise((e=>setTimeout(e,1e3))),this.logger.info("Retrying stuck session: sending continue",{sessionId:e})
|
|
528
668
|
const o=await fetch(`${t}/session/${e}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:"Continue"}],...(()=>{const e=this.getDefaultModel?.()
|
|
529
669
|
if(!e)return{}
|
|
530
|
-
const[t,...
|
|
531
|
-
return{model:{providerID:t,modelID:
|
|
670
|
+
const[t,...r]=e.split("/")
|
|
671
|
+
return{model:{providerID:t,modelID:r.join("/")}}})()}),signal:AbortSignal.timeout(1e4)})
|
|
532
672
|
if(!o.ok){const e=await o.text().catch((()=>""))
|
|
533
|
-
return{success:!1,error:`Failed to re-send prompt: ${o.status} ${e}`}}return
|
|
673
|
+
return{success:!1,error:`Failed to re-send prompt: ${o.status} ${e}`}}return s.retryCount++,this.stuckSessions.delete(e),this.notifiedSessions.delete(e),this.logger.info("Stuck session retry sent",{sessionId:e,retryCount:s.retryCount}),{success:!0}}catch(e){return{success:!1,error:(0,r.getErrorMessage)(e)}}}}},3077(e,exports,t){"use strict"
|
|
534
674
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.HookExecutor=void 0
|
|
535
|
-
const
|
|
536
|
-
exports.HookExecutor=class{constructor(e,t,
|
|
675
|
+
const r=t(8740),s=t(4627),o=t(4769)
|
|
676
|
+
exports.HookExecutor=class{constructor(e,t,s,o,n){this.logger=e,this.cache=new r.HookCache(e,t,o),this.ctx={logger:e,projectId:t,projectPath:s,apiClient:o,cachedDefaultModel:n?.cachedDefaultModel,trackChildPid:n?.trackChildPid}}setDefaultModel(e){this.ctx.cachedDefaultModel=e}setProjectId(e){this.ctx.projectId=e,this.cache.setProjectId(e)}async processEvent(e){const{eventType:t,sessionId:r,variables:o,timing:n}=e,i={triggered:0,hooks:[]}
|
|
537
677
|
if(!this.ctx.projectId)return this.logger.debug("No project ID, skipping hook event",{eventType:t}),i
|
|
538
|
-
try{const e=await this.cache.getHooksForEvent(t,
|
|
678
|
+
try{const e=await this.cache.getHooksForEvent(t,r)
|
|
539
679
|
if(0===e.length)return i
|
|
540
680
|
const a=new Set,c=e.filter((e=>!a.has(e.id)&&(a.add(e.id),!0))),l=n||"after",d=c.filter((e=>(e.timing||"after")===l))
|
|
541
681
|
if(0===d.length)return i
|
|
542
682
|
const u={...o}
|
|
543
|
-
if(
|
|
544
|
-
if(t?.agentType&&!u.agentType&&(u.agentType=t.agentType),!u.parentSessionId&&t?.parentSessionId){const e=await this.ctx.apiClient.get(`/api/projects/${this.ctx.projectId}/cli-sessions/${t.parentSessionId}`),
|
|
545
|
-
|
|
683
|
+
if(r&&(!u.parentSessionId||!u.agentType))try{const e=await this.ctx.apiClient.get(`/api/projects/${this.ctx.projectId}/cli-sessions/external/opencode/${encodeURIComponent(r)}`),t=e?.data||e
|
|
684
|
+
if(t?.agentType&&!u.agentType&&(u.agentType=t.agentType),!u.parentSessionId&&t?.parentSessionId){const e=await this.ctx.apiClient.get(`/api/projects/${this.ctx.projectId}/cli-sessions/${t.parentSessionId}`),r=e?.data||e
|
|
685
|
+
r?.externalSessionId&&(u.parentSessionId=r.externalSessionId)}}catch(e){this.logger.warn("Failed to enrich hook variables with session context",{sessionId:r,error:e+""})}const h=d.filter((e=>(0,s.evaluateCondition)(e.condition,u)))
|
|
546
686
|
if(0===h.length)return i
|
|
547
|
-
for(const e of h){await this.executeHook(e,u,
|
|
548
|
-
const t=(0,
|
|
549
|
-
t&&await this.processCustomEvent(t,{...u,sessionId:
|
|
550
|
-
if(this.logger.info("Executing hook",{name:e.name,action:e.action,target:e.target,eventType:e.eventType,sessionId:
|
|
687
|
+
for(const e of h){await this.executeHook(e,u,r),i.triggered++,i.hooks.push(e.name)
|
|
688
|
+
const t=(0,s.determineEventToEmit)(e,u)
|
|
689
|
+
t&&await this.processCustomEvent(t,{...u,sessionId:r||""},0)}"session_idle"!==t&&"session_created"!==t&&r&&this.cache.preWarm("session_idle",r)}catch(e){this.logger.error("Error processing hook event",{eventType:t,error:e+""})}return i}async executeHook(e,t,r){const n=(0,s.interpolatePrompt)(e.promptTemplate,t),i=t.requestId
|
|
690
|
+
if(this.logger.info("Executing hook",{name:e.name,action:e.action,target:e.target,eventType:e.eventType,sessionId:r}),this.notifyHookTrigger(e).catch((()=>{})),"inject_prompt"===e.action)switch(e.target){case"same_session":default:await(0,o.executeSameSession)(n,r,this.ctx)
|
|
551
691
|
break
|
|
552
|
-
case"parent_session":case"ancestor_chain":await(0,o.executeInjectPrompt)(e,e.promptTemplate,
|
|
692
|
+
case"parent_session":case"ancestor_chain":await(0,o.executeInjectPrompt)(e,e.promptTemplate,r,t,this.ctx)}else await(0,o.executeBackground)(e,n,r,i,this.ctx)}async processCustomEvent(e,t,r){if(r>=10)return void this.logger.warn("Custom event chain depth exceeded",{eventName:e,chainDepth:r})
|
|
553
693
|
const n=e.startsWith("custom:")?e:"custom:"+e,i=await this.cache.getHooksForEvent(n,t.sessionId)
|
|
554
694
|
if(0===i.length)return
|
|
555
|
-
const a={...t,chainDepth:
|
|
556
|
-
for(const e of c){const t=(0,
|
|
557
|
-
if(this.logger.info("Executing custom event hook",{name:e.name,event:n,chainDepth:
|
|
695
|
+
const a={...t,chainDepth:r+1+"",triggerEvent:n},c=i.filter((e=>(0,s.evaluateCondition)(e.condition,a)))
|
|
696
|
+
for(const e of c){const t=(0,s.interpolatePrompt)(e.promptTemplate,a),i=a.sessionId||""
|
|
697
|
+
if(this.logger.info("Executing custom event hook",{name:e.name,event:n,chainDepth:r}),"inject_prompt"===e.action)switch(e.target){case"same_session":default:await(0,o.executeSameSession)(t,i,this.ctx)
|
|
558
698
|
break
|
|
559
699
|
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)
|
|
560
|
-
const c=(0,
|
|
561
|
-
c&&await this.processCustomEvent(c,a,
|
|
562
|
-
|
|
700
|
+
const c=(0,s.determineEventToEmit)(e,a)
|
|
701
|
+
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"
|
|
702
|
+
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"
|
|
703
|
+
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
563
704
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.LogLevel=exports.defaultLogger=exports.isDebugToolsEnabled=exports.getDebugLevelFromEnv=exports.isDebugLoggingEnabled=exports.createEnvLogger=exports.createLogger=void 0
|
|
564
|
-
const
|
|
565
|
-
Object.defineProperty(exports,"createLogger",{enumerable:!0,get(){return
|
|
566
|
-
const o=
|
|
705
|
+
const s=t(2782)
|
|
706
|
+
Object.defineProperty(exports,"createLogger",{enumerable:!0,get(){return s.createLogger}}),Object.defineProperty(exports,"createEnvLogger",{enumerable:!0,get(){return s.createEnvLogger}}),Object.defineProperty(exports,"isDebugLoggingEnabled",{enumerable:!0,get(){return s.isDebugLoggingEnabled}}),Object.defineProperty(exports,"getDebugLevelFromEnv",{enumerable:!0,get(){return s.getDebugLevelFromEnv}}),Object.defineProperty(exports,"isDebugToolsEnabled",{enumerable:!0,get(){return s.isDebugToolsEnabled}})
|
|
707
|
+
const o=r(t(2782))
|
|
567
708
|
exports.defaultLogger=o.default
|
|
568
709
|
const n=t(6540)
|
|
569
710
|
Object.defineProperty(exports,"LogLevel",{enumerable:!0,get(){return n.LogLevel}}),exports.default=o.default},3614(e,exports,t){"use strict"
|
|
570
|
-
async function
|
|
571
|
-
try{const o=await i.default.readFile(
|
|
572
|
-
n.id=t,n.registeredRemotely=!0,n.metadata&&(n.metadata.basePath=e,n.metadata.alchemyPath=a.default.join(e,".alchemy_lab")),await i.default.writeFile(
|
|
573
|
-
try{await i.default.access(
|
|
574
|
-
for(const
|
|
575
|
-
t.push(i.default.rm(
|
|
576
|
-
await Promise.all(
|
|
711
|
+
async function r(e,t,r){const s=a.default.join(e,".alchemy_lab","project.json")
|
|
712
|
+
try{const o=await i.default.readFile(s,"utf-8"),n=JSON.parse(o)
|
|
713
|
+
n.id=t,n.registeredRemotely=!0,n.metadata&&(n.metadata.basePath=e,n.metadata.alchemyPath=a.default.join(e,".alchemy_lab")),await i.default.writeFile(s,JSON.stringify(n,null,2),"utf-8"),r.info("Updated project.json",{projectJsonPath:s,newProjectId:t})}catch(e){r.warn("Could not update project.json (will be created on launch)",{error:e instanceof Error?e.message:e+""})}}async function s(e,t,r){const s=a.default.join(t,".alchemy_lab","opencode","opencode-local.db")
|
|
714
|
+
try{await i.default.access(s)}catch{return void r.info("No OpenCode database found, skipping session directory update")}try{await l("sqlite3",[s,`UPDATE session SET directory = replace(directory, '${e.replace(/'/g,"''")}', '${t.replace(/'/g,"''")}') WHERE directory LIKE '${e.replace(/'/g,"''")}%';`],{timeout:1e4}),r.info("Updated OpenCode session directories",{sourcePath:e,targetPath:t})}catch(e){r.warn("Could not update OpenCode session directories",{error:e instanceof Error?e.message:e+""})}}async function o(e){const t=[]
|
|
715
|
+
for(const r of d){const s=a.default.join(e,r)
|
|
716
|
+
t.push(i.default.rm(s,{recursive:!0,force:!0}).catch((()=>{})))}t.push((async()=>{try{const t=a.default.join(e,".alchemy_lab"),r=await i.default.readdir(t)
|
|
717
|
+
await Promise.all(r.filter((e=>e.endsWith(".lock"))).map((e=>i.default.unlink(a.default.join(t,e)).catch((()=>{})))))}catch{}})()),await Promise.all(t)}var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
577
718
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleForkFilesystemAction=async function(e,t){const{logger:n}=t,{sourcePath:a,targetPath:c,newProjectId:h}=e
|
|
578
719
|
if(!a||!c||!h)return{success:!1,message:"sourcePath, targetPath, and newProjectId are required",error:"missing_params"}
|
|
579
720
|
n.info("Starting filesystem fork",{sourcePath:a,targetPath:c,newProjectId:h})
|
|
580
|
-
try{const e=await async function(e,t,
|
|
721
|
+
try{const e=await async function(e,t,r){const s=e.endsWith("/")?e:e+"/"
|
|
581
722
|
try{const o=[...d,...u].flatMap((e=>["--exclude",e]))
|
|
582
|
-
return await l("rsync",["-a",...o,
|
|
583
|
-
const
|
|
584
|
-
return await l("cp",["-R",
|
|
585
|
-
return e?(await Promise.all([
|
|
723
|
+
return await l("rsync",["-a",...o,s,t],{timeout:3e5}),r.info("Directory copied via rsync",{source:e,target:t}),!0}catch(e){r.warn("rsync failed, falling back to cp -R",{error:e instanceof Error?e.message:e+""})}try{await i.default.mkdir(t,{recursive:!0})
|
|
724
|
+
const s=e.endsWith("/")?e+".":e+"/."
|
|
725
|
+
return await l("cp",["-R",s,t+"/"],{timeout:3e5}),r.info("Directory copied via cp -R",{source:e,target:t}),!0}catch(e){return r.error("cp -R failed",{error:e instanceof Error?e.message:e+""}),!1}}(a,c,n)
|
|
726
|
+
return e?(await Promise.all([r(c,h,n),s(a,c,n),o(c)]),n.info("Filesystem fork complete",{targetPath:c,newProjectId:h}),{success:!0,message:"Project forked to "+c,data:{targetPath:c,newProjectId:h}}):{success:!1,message:"Failed to copy project directory",error:"copy_failed"}}catch(e){const t=e instanceof Error?e.message:e+""
|
|
586
727
|
n.error("Filesystem fork failed",{error:t})
|
|
587
728
|
try{await i.default.rm(c,{recursive:!0,force:!0}),n.info("Cleaned up partial fork directory",{targetPath:c})}catch(e){n.warn("Failed to clean up partial fork",{error:e instanceof Error?e.message:e+""})}return{success:!1,message:"Filesystem fork failed: "+t,error:t}}}
|
|
588
729
|
const i=n(t(1943)),a=n(t(6928)),c=t(5317),l=(0,t(9023).promisify)(c.execFile),d=[".alchemy_lab/opencode.json",".alchemy_lab/server.log",".alchemy_lab/action-server-server.log",".alchemy_lab/mcp-identity-debug.log",".alchemy_lab/worktrees",".opencode/node_modules"],u=[".alchemy_lab/*.lock"]},3785(module){"use strict"
|
|
589
730
|
module.exports=require("readline")},3807(module){"use strict"
|
|
590
|
-
module.exports=require("simple-git")},
|
|
591
|
-
function
|
|
731
|
+
module.exports=require("simple-git")},3922(e,exports,t){"use strict"
|
|
732
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.captureCommitHashRobustly=async function(e,t,s,o=3,n=100){t.debug("Starting robust commit hash capture",{context:s,maxRetries:o,delayMs:n})
|
|
733
|
+
for(let i=1;o>=i;i++)try{i>1&&(await new Promise((e=>setTimeout(e,n))),t.debug("Retry attempt after delay",{attempt:i,delayMs:n}))
|
|
734
|
+
const r=await e.revparse(["HEAD"])
|
|
735
|
+
await e.catFile(["-e",r])
|
|
736
|
+
const a=await e.revparse(["HEAD"])
|
|
737
|
+
if(r!==a){if(t.warn("Hash inconsistency detected, retrying",{context:s,attempt:i,firstHash:r.substring(0,8),verificationHash:a.substring(0,8)}),i===o)throw Error(`Git state unstable after ${o} attempts: ${r} vs ${a}`)
|
|
738
|
+
continue}const c=await e.show(["--format=%H %s","--no-patch",r])
|
|
739
|
+
return t.debug("Successfully captured stable commit hash",{context:s,attempt:i,hash:r.substring(0,8),fullHash:r,commitMessage:c.split(" ").slice(1).join(" ")}),r}catch(e){if(t.warn("Failed to capture commit hash",{context:s,attempt:i,error:(0,r.getErrorMessage)(e)}),i===o)throw Error(`Failed to capture stable commit hash after ${o} attempts: ${(0,r.getErrorMessage)(e)}`)}throw Error("Unexpected end of retry loop")},exports.validateCommitHash=async function(e,t,s,o){try{s.debug("Validating commit hash",{context:o,commitHash:t.substring(0,8),fullCommitHash:t}),await e.catFile(["-e",t])
|
|
740
|
+
const r=await e.show(["--format=%H %ct %s","--no-patch",t])
|
|
741
|
+
return s.debug("Commit hash validation successful",{context:o,commitHash:t.substring(0,8),commitDetails:r.trim()}),{valid:!0}}catch(e){const n=(0,r.getErrorMessage)(e)
|
|
742
|
+
return s.warn("Commit hash validation failed",{context:o,commitHash:t.substring(0,8),error:n}),{valid:!1,error:n}}},exports.verifyGitStateConsistency=async function(e,t,s,o){const n=[]
|
|
743
|
+
try{const r=await e.revparse(["HEAD"])
|
|
744
|
+
s.debug("Verifying git state consistency",{context:o,expectedHead:t.substring(0,8),currentHead:r.substring(0,8),match:t===r}),t!==r&&n.push(`HEAD mismatch: expected ${t.substring(0,8)}, got ${r.substring(0,8)}`)
|
|
745
|
+
const i=await e.status()
|
|
746
|
+
return(i.ahead>0||i.behind>0)&&n.push(`Branch not in sync: ahead=${i.ahead}, behind=${i.behind}`),(i.staged.length>0||i.modified.length>0)&&n.push(`Working tree not clean: staged=${i.staged.length}, modified=${i.modified.length}`),{consistent:0===n.length,currentHead:r,issues:n}}catch(e){const t=(0,r.getErrorMessage)(e)
|
|
747
|
+
return n.push("Git state check failed: "+t),{consistent:!1,issues:n}}}
|
|
748
|
+
const r=t(8824)},3962(e,exports,t){"use strict"
|
|
749
|
+
function r(){if(n)return n
|
|
592
750
|
const e=process.argv[1]||""
|
|
593
|
-
let
|
|
594
|
-
try{
|
|
595
|
-
if(
|
|
596
|
-
e>0&&(r
|
|
597
|
-
if(e>0)r
|
|
598
|
-
else{const e=
|
|
599
|
-
e>0&&(r
|
|
600
|
-
|
|
601
|
-
return n={installPath:
|
|
602
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.getInstallContext=
|
|
603
|
-
const o=
|
|
751
|
+
let r=e
|
|
752
|
+
try{r=t(9896).realpathSync(e)}catch{}let s=""
|
|
753
|
+
if(r.includes("node_modules")){const e=r.lastIndexOf("/dist/")
|
|
754
|
+
e>0&&(s=r.substring(0,e+5))}else{const e=r.lastIndexOf("/dist-dev/")
|
|
755
|
+
if(e>0)s=r.substring(0,e+9)
|
|
756
|
+
else{const e=r.lastIndexOf("/dist/")
|
|
757
|
+
e>0&&(s=r.substring(0,e+5))}}if(!s){const e=process.cwd()
|
|
758
|
+
s=e.includes("alchemy-local")||e.includes("alchemy")?o.default.join(e,"dist-dev"):o.default.join(e,"dist")}const i=s.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost")||!1,a=o.default.join(s,"mcp-direct.js"),c=process.env.REMOTE_SERVER_URL||"https://api.alchemylabcode.com"
|
|
759
|
+
return n={installPath:s,scriptPath:a,nodePath:process.execPath,isDevMode:i,serverName:"alchemy",remoteServerUrl:c},n}var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
760
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.getInstallContext=r,exports.clearInstallContextCache=function(){n=null},exports.isDevMode=function(e){return e?e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost")||!1:r().isDevMode},exports.getServerName=function(e){return e??r().isDevMode,"alchemy"}
|
|
761
|
+
const o=s(t(6928))
|
|
604
762
|
let n=null},4139(e,exports){"use strict"
|
|
605
763
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.ALCHEMY_DIR_NAME=exports.TOKEN_DIR_NAME=void 0,exports.TOKEN_DIR_NAME="alchemy-key",exports.ALCHEMY_DIR_NAME=".alchemy_lab"},4192(e,exports,t){"use strict"
|
|
606
764
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.BaseApiClient=void 0
|
|
607
|
-
const
|
|
608
|
-
exports.BaseApiClient=class{constructor(e,t,
|
|
609
|
-
try{(0,
|
|
765
|
+
const r=t(7163)
|
|
766
|
+
exports.BaseApiClient=class{constructor(e,t,s,o,n){this.failureCount=0,this.circuitState="closed",this.circuitResetTimeout=null,this.baseUrl=e,this.logger=t,this.metrics=s,this.authToken=o,this.options={retryAttempts:3,retryDelay:1e3,timeout:1e4,circuitBreakerThreshold:10,circuitBreakerResetTime:1e4,...n},this.logger.debug("BaseApiClient constructor options:",{providedOptions:n?JSON.stringify(n):"undefined",mergedOptions:JSON.stringify(this.options)})
|
|
767
|
+
try{(0,r.debugTokenAccess)(this,"BaseApiClient constructor")}catch(e){}}setAuthToken(e){this.authToken=e||void 0,this.logger.debug("Auth token updated",{hasToken:!!e,tokenLength:e?e.length:0})}getAuthToken(){return this.authToken}async requestWithRetry(e,t,r,s,o){if("open"===this.circuitState)throw this.logger.warn(`Circuit breaker open - rejecting request to ${e} ${t}`),Error("Service unavailable - circuit breaker open")
|
|
610
768
|
const n=t.startsWith("/")?t:"/"+t
|
|
611
769
|
let i=`${this.baseUrl}${n}`
|
|
612
770
|
if(o&&Object.keys(o).length>0){const e=Object.entries(o).filter((([e,t])=>null!=t)).map((([e,t])=>Array.isArray(t)?t.map((t=>`${encodeURIComponent(e)}=${encodeURIComponent(t+"")}`)).join("&"):`${encodeURIComponent(e)}=${encodeURIComponent(t+"")}`)).join("&")
|
|
613
|
-
i=`${i}${i.includes("?")?"&":"?"}${e}`}const a={"Content-Type":"application/json",Accept:"application/json",...
|
|
771
|
+
i=`${i}${i.includes("?")?"&":"?"}${e}`}const a={"Content-Type":"application/json",Accept:"application/json",...s}
|
|
614
772
|
this.authToken?(a.Authorization="Bearer "+this.authToken,this.logger.debug("Using auth token for request",{method:e,path:n,tokenLength:this.authToken.length})):this.logger.warn(`No auth token available for ${e} ${t}`,{operation:"api-request",method:e,path:t,authStatus:"missing"})
|
|
615
773
|
const c=process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"
|
|
616
774
|
a["X-Service-Secret"]=c
|
|
@@ -619,292 +777,307 @@ const d=this.options.retryAttempts,u={method:e,path:n},h=this.metrics.startTimer
|
|
|
619
777
|
for(;d>l;){l++
|
|
620
778
|
try{this.logger.debug(`Making ${e} request to ${i}`,{attempt:l,maxAttempts:d,hasAuth:!!a.Authorization,hasServiceIdentifier:!!a["X-Client-Type"]})
|
|
621
779
|
const t={method:e,headers:{...a},signal:AbortSignal.timeout(this.options.timeout||1e4)}
|
|
622
|
-
if("GET"!==e&&void 0!==
|
|
623
|
-
Object.entries(t.headers).forEach((([t,
|
|
624
|
-
this.logger.debug("Request body",{bodyPreview:e.substring(0,200)+(e.length>200?"...":""),bodyLength:e.length})}const
|
|
625
|
-
if(!
|
|
626
|
-
if(429===
|
|
780
|
+
if("GET"!==e&&void 0!==r&&(t.body=JSON.stringify(r)),this.logger.debug("Request details",{method:e,url:i}),t.headers){const e={}
|
|
781
|
+
Object.entries(t.headers).forEach((([t,r])=>{"authorization"===t.toLowerCase()?e[t]="Bearer [redacted]":e[t]=r})),this.logger.debug("Request headers",{headers:e})}if("GET"!==e&&t.body){const e="string"==typeof t.body?t.body:JSON.stringify(t.body)
|
|
782
|
+
this.logger.debug("Request body",{bodyPreview:e.substring(0,200)+(e.length>200?"...":""),bodyLength:e.length})}const s=await fetch(i,t)
|
|
783
|
+
if(!s.ok){if(this.logger.error("API request failed with status "+s.status,{method:e,url:i,status:s.status,statusText:s.statusText,attempt:l}),401===s.status)throw g({success:!1,status:s.status,retries:l-1}),Error("Authentication failed - invalid or expired token")
|
|
784
|
+
if(429===s.status){const e=s.headers.get("Retry-After"),t=e?1e3*parseInt(e):2*(this.options.retryDelay||1e3)
|
|
627
785
|
if(this.logger.warn(`Rate limited (429), retrying after ${t}ms`,{attempt:l,maxAttempts:d,retryAfter:e}),d>l){await new Promise((e=>setTimeout(e,t)))
|
|
628
|
-
continue}}if(
|
|
629
|
-
this.logger.warn(`Server error ${
|
|
630
|
-
continue}let t=`API request failed with status ${
|
|
631
|
-
try{const e=await
|
|
632
|
-
if(e&&"object"==typeof e&&!1===e.success&&e.error){const
|
|
633
|
-
|
|
634
|
-
e&&e.trim()&&(t=`${t}. Response: ${e.substring(0,200)}`)}catch(t){this.logger.debug("Failed to parse error response",{jsonError:e instanceof Error?e.message:e+"",textError:t instanceof Error?t.message:t+""})}}g({success:!1,status:
|
|
786
|
+
continue}}if(s.status>=500&&d>l){const e=(this.options.retryDelay||1e3)*Math.pow(2,l-1)
|
|
787
|
+
this.logger.warn(`Server error ${s.status}, retry in ${e}ms`,{attempt:l,maxAttempts:d}),await new Promise((t=>setTimeout(t,e)))
|
|
788
|
+
continue}let t=`API request failed with status ${s.status}: ${s.statusText}`,r={}
|
|
789
|
+
try{const e=await s.json()
|
|
790
|
+
if(e&&"object"==typeof e&&!1===e.success&&e.error){const s=e.error
|
|
791
|
+
s.message&&(t=s.message),r={code:s.code,statusCode:s.statusCode,retryable:s.retryable,details:s.details}}else e&&"object"==typeof e&&e.message&&(t=e.message)}catch(e){try{const e=await s.text()
|
|
792
|
+
e&&e.trim()&&(t=`${t}. Response: ${e.substring(0,200)}`)}catch(t){this.logger.debug("Failed to parse error response",{jsonError:e instanceof Error?e.message:e+"",textError:t instanceof Error?t.message:t+""})}}g({success:!1,status:s.status,retries:l-1})
|
|
635
793
|
const o=Error(t)
|
|
636
|
-
throw o.status=
|
|
637
|
-
const o=await
|
|
794
|
+
throw o.status=s.status,o.statusText=s.statusText,o.method=e,o.url=i,o.errorDetails=r,o}this.resetFailureCount()
|
|
795
|
+
const o=await s.json()
|
|
638
796
|
if(null===o||"object"!=typeof o)throw Error("Invalid API response: expected object, got "+typeof o)
|
|
639
797
|
if(o&&"object"==typeof o&&!("success"in o)){this.logger.warn("Non-standard API response detected - wrapping in standard format",{responseKeys:Object.keys(o)})
|
|
640
798
|
const e={success:!0,data:o}
|
|
641
|
-
return g({success:!0,status:
|
|
642
|
-
throw this.logger.error("API returned error response",{errorCode:e.error?.code,errorMessage:e.error?.message,statusCode:e.error?.statusCode}),g({success:!1,status:
|
|
643
|
-
return g({success:!0,status:
|
|
644
|
-
const
|
|
645
|
-
this.logger.warn(`API request failed, retrying in ${
|
|
799
|
+
return g({success:!0,status:s.status,retries:l-1,standardFormat:!1}),e}if(o&&"object"==typeof o&&"success"in o&&!1===o.success){const e=o
|
|
800
|
+
throw this.logger.error("API returned error response",{errorCode:e.error?.code,errorMessage:e.error?.message,statusCode:e.error?.statusCode}),g({success:!1,status:s.status,retries:l-1,errorCode:e.error?.code}),Error(`API error: ${e.error?.code}: ${e.error?.message}`)}if(o&&"object"==typeof o&&"success"in o&&!0===o.success&&"data"in o){const t=o
|
|
801
|
+
return g({success:!0,status:s.status,retries:l-1,standardFormat:!0}),this.logger.debug("API request succeeded with status "+s.status,{method:e,url:i,status:s.status,retries:l-1,hasMetadata:"metadata"in t}),t}return this.logger.warn("Invalid API response format: Unexpected structure",{responseType:typeof o,responseKeys:"object"==typeof o?Object.keys(o):[]}),{success:!0,data:o}}catch(t){if(l>=d){const r=t?.status
|
|
802
|
+
throw void 0!==r&&500>r||this.incrementFailureCount(),this.logger.error(`API request failed after ${l} attempts`,{method:e,url:i,error:t.message||t+"",status:t.status}),g({success:!1,error:!0,retries:l-1}),t}const r=Math.min((this.options.retryDelay||1e3)*Math.pow(2,l-1),5e3)
|
|
803
|
+
this.logger.warn(`API request failed, retrying in ${r}ms`,{method:e,url:i,error:t.message||t+"",attempt:l,maxAttempts:d}),await new Promise((e=>setTimeout(e,r)))}}throw Error("Unexpected end of requestWithRetry")}incrementFailureCount(){this.failureCount++,this.metrics.setGauge("api_failure_count",this.failureCount),"half-open"!==this.circuitState?this.failureCount<this.options.circuitBreakerThreshold||"closed"!==this.circuitState||this.openCircuit():this.logger.warn("Half-open probe failed, will retry on next request")}resetFailureCount(){const e="closed"!==this.circuitState
|
|
646
804
|
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)
|
|
647
805
|
const e=this.options.circuitBreakerResetTime||5e3
|
|
648
|
-
this.circuitResetTimeout=setTimeout((()=>{this.circuitState="half-open",this.logger.info("Circuit breaker half-open, next request will probe")}),e)}async get(e,t,
|
|
806
|
+
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+"'")
|
|
649
807
|
throw t.code="MODULE_NOT_FOUND",t}e.keys=()=>[],e.resolve=e,e.id=4245,module.exports=e},4627(e,exports){"use strict"
|
|
650
|
-
function t(e,t){try{return function(e){const t=
|
|
808
|
+
function t(e,t){try{return function(e){const t=r.get(e)
|
|
651
809
|
if(t)return t
|
|
652
|
-
let
|
|
653
|
-
const o=RegExp(`^${
|
|
654
|
-
return
|
|
655
|
-
const
|
|
656
|
-
return"paths"===e.field&&
|
|
657
|
-
case"not_matches":return e.every((e=>!t(e,
|
|
658
|
-
case"contains":return e.some((e=>e.includes(
|
|
659
|
-
case"not_contains":return e.every((e=>!e.includes(
|
|
660
|
-
case"equals":return e.some((e=>e===
|
|
661
|
-
case"not_equals":return e.every((e=>e!==
|
|
662
|
-
default:return!0}}(
|
|
663
|
-
case"not_matches":return!t(e,
|
|
664
|
-
case"contains":return e.includes(
|
|
665
|
-
case"not_contains":return!e.includes(
|
|
666
|
-
case"equals":return e===
|
|
667
|
-
case"not_equals":return e!==
|
|
668
|
-
default:return!0}}(
|
|
669
|
-
return"OR"===e.logic?
|
|
670
|
-
for(const[e,
|
|
671
|
-
return
|
|
810
|
+
let s=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,"{{GLOBSTAR}}").replace(/\*/g,"[^/]*").replace(/\?/g,".").replace(/\{\{GLOBSTAR\}\}/g,".*")
|
|
811
|
+
const o=RegExp(`^${s}$`)
|
|
812
|
+
return r.size>500&&r.clear(),r.set(e,o),o}(t).test(e)}catch{return!1}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.evaluateCondition=function(e,r){if(!e||!e.conditions||0===e.conditions.length)return!0
|
|
813
|
+
const s=e.conditions.map((e=>{const s=r[e.field]||""
|
|
814
|
+
return"paths"===e.field&&s.includes(", ")?function(e,r,s){switch(r){case"matches":return e.some((e=>t(e,s)))
|
|
815
|
+
case"not_matches":return e.every((e=>!t(e,s)))
|
|
816
|
+
case"contains":return e.some((e=>e.includes(s)))
|
|
817
|
+
case"not_contains":return e.every((e=>!e.includes(s)))
|
|
818
|
+
case"equals":return e.some((e=>e===s))
|
|
819
|
+
case"not_equals":return e.every((e=>e!==s))
|
|
820
|
+
default:return!0}}(s.split(", "),e.operator,e.value):function(e,r,s){switch(r){case"matches":return t(e,s)
|
|
821
|
+
case"not_matches":return!t(e,s)
|
|
822
|
+
case"contains":return e.includes(s)
|
|
823
|
+
case"not_contains":return!e.includes(s)
|
|
824
|
+
case"equals":return e===s
|
|
825
|
+
case"not_equals":return e!==s
|
|
826
|
+
default:return!0}}(s,e.operator,e.value)}))
|
|
827
|
+
return"OR"===e.logic?s.some((e=>e)):s.every((e=>e))},exports.interpolatePrompt=function(e,t){let r=e
|
|
828
|
+
for(const[e,s]of Object.entries(t))r=r.replace(RegExp(`\\{\\{${e}\\}\\}`,"g"),s)
|
|
829
|
+
return r},exports.determineEventToEmit=function(e,t){if(e.emitEventOnCondition){const{condition:r,event:o}=e.emitEventOnCondition,n=t[r.field]||""
|
|
672
830
|
let i=!1
|
|
673
|
-
switch(
|
|
831
|
+
switch(r.operator){case"contains":i=n.includes(r.value)
|
|
674
832
|
break
|
|
675
|
-
case"not_contains":i=!n.includes(
|
|
833
|
+
case"not_contains":i=!n.includes(r.value)
|
|
676
834
|
break
|
|
677
|
-
case"equals":i=n===
|
|
835
|
+
case"equals":i=n===r.value
|
|
678
836
|
break
|
|
679
|
-
case"not_equals":i=n!==
|
|
837
|
+
case"not_equals":i=n!==r.value
|
|
680
838
|
break
|
|
681
|
-
case"matches":try{
|
|
682
|
-
case"not_matches":try{
|
|
683
|
-
if(
|
|
684
|
-
for(const[e,
|
|
839
|
+
case"matches":try{s.has(r.value)||(s.set(r.value,RegExp(r.value)),s.size>500&&s.clear()),i=s.get(r.value).test(n)}catch{i=!1}break
|
|
840
|
+
case"not_matches":try{s.has(r.value)||(s.set(r.value,RegExp(r.value)),s.size>500&&s.clear()),i=!s.get(r.value).test(n)}catch{i=!0}}if(i)return o}return e.emitEvent||null},exports.createToolCallHash=function(e,t){return`${e}:${t?.substring(0,100)||""}`},exports.isDuplicateToolCall=function(e,t=Date.now()){const r=o.get(e)
|
|
841
|
+
if(r&&n>t-r)return!0
|
|
842
|
+
for(const[e,r]of o.entries())t-r>2*n&&o.delete(e)
|
|
685
843
|
return o.set(e,t),!1},exports.tryMarkMessageProcessed=function(e,t){if(!t)return!0
|
|
686
844
|
if(i.get(e)===t)return!1
|
|
687
845
|
if(i.set(e,t),i.size>100){const e=i.keys().next().value
|
|
688
846
|
e&&i.delete(e)}return!0}
|
|
689
|
-
const
|
|
690
|
-
async function
|
|
691
|
-
if(!
|
|
692
|
-
const
|
|
693
|
-
return
|
|
694
|
-
if(!
|
|
847
|
+
const r=new Map,s=new Map,o=new Map,n=2e3,i=new Map},4654(e,exports,t){"use strict"
|
|
848
|
+
async function r(e,t){try{const r=await fetch(e+"/session/status",{signal:AbortSignal.timeout(5e3)})
|
|
849
|
+
if(!r.ok)return"error"
|
|
850
|
+
const s=await r.json()
|
|
851
|
+
return s[t]?.type||"unknown"}catch{return"error"}}function s(e){return new Promise((t=>setTimeout(t,e)))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handlePromptAndWaitAction=async function(e,t){const{logger:n,projectPath:i,projectId:a,apiClient:c,trackChildPid:l}=t,{sessionId:d,message:u,prompt:h,timeoutSeconds:g=300,agent:p="coder",title:m}=e,f=u||h
|
|
852
|
+
if(!f)return{success:!1,message:"message is required",error:"missing_params"}
|
|
695
853
|
const y=(0,o.getOpenCodeServerUrl)(i,n)
|
|
696
854
|
if(!y)return{success:!1,message:"OpenCode not running",error:"no_opencode"}
|
|
697
855
|
const v=Date.now()
|
|
698
856
|
let w=d
|
|
699
|
-
if(w){try{const e=await fetch(`${y}/session/${w}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:
|
|
700
|
-
return{model:{providerID:e,modelID:
|
|
701
|
-
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:
|
|
857
|
+
if(w){try{const e=await fetch(`${y}/session/${w}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:f}],...t.cachedDefaultModel?(()=>{const[e,...r]=t.cachedDefaultModel.split("/")
|
|
858
|
+
return{model:{providerID:e,modelID:r.join("/")}}})():{}}),signal:AbortSignal.timeout(1e4)})
|
|
859
|
+
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}})
|
|
702
860
|
if(!e.success||!e.sessionId)return{success:!1,message:"Failed to spawn session: "+e.error,error:"spawn_failed"}
|
|
703
|
-
w=e.sessionId,n.info("Spawned session for prompt-and-wait",{sessionId:w})}const
|
|
861
|
+
w=e.sessionId,n.info("Spawned session for prompt-and-wait",{sessionId:w})}const b=await async function(e,t,o){const n=Date.now()+1e3*o
|
|
704
862
|
let i=!1
|
|
705
863
|
const a=Date.now()+15e3
|
|
706
|
-
for(;Date.now()<a&&Date.now()<n;){const o=await
|
|
864
|
+
for(;Date.now()<a&&Date.now()<n;){const o=await r(e,t)
|
|
707
865
|
if("busy"===o||"running"===o){i=!0
|
|
708
|
-
break}await
|
|
866
|
+
break}await s(500)}for(;Date.now()<n;){const o=await r(e,t)
|
|
709
867
|
if("error"===o)return{status:"error"}
|
|
710
868
|
if(("idle"===o||"completed"===o)&&i)return{status:"completed"}
|
|
711
|
-
"busy"!==o&&"running"!==o||(i=!0),await
|
|
712
|
-
return
|
|
713
|
-
return""}(
|
|
714
|
-
return{success:!0,message:"Prompt completed",data:{sessionId:w,status:
|
|
869
|
+
"busy"!==o&&"running"!==o||(i=!0),await s(2e3)}return{status:"timeout"}}(y,w,g),I=Date.now()-v,E=await async function(e,t){try{const r=await fetch(`${e}/session/${t}/message?limit=200`,{signal:AbortSignal.timeout(1e4)})
|
|
870
|
+
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
|
|
871
|
+
return""}(E)
|
|
872
|
+
return{success:!0,message:"Prompt completed",data:{sessionId:w,status:b.status,messages:E,toolCallCount:C,elapsedMs:I,finalResponse:S}}}
|
|
715
873
|
const o=t(8148)},4769(e,exports,t){"use strict"
|
|
716
|
-
function
|
|
717
|
-
if(!
|
|
718
|
-
try{const
|
|
719
|
-
return
|
|
720
|
-
let
|
|
721
|
-
if(
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
await
|
|
734
|
-
if(
|
|
735
|
-
const
|
|
736
|
-
if(!
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
const o=t(
|
|
874
|
+
function r(e){return(0,o.getOpenCodeServerUrl)(e.projectPath,e.logger)}async function s(e,t,s,o){const n=r(s)
|
|
875
|
+
if(!n||!t)return!1
|
|
876
|
+
try{const r=await async function(e,t){try{const r=await t.apiClient.get(`/api/projects/${t.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),s=r?.data||r
|
|
877
|
+
return s?.agentType||""}catch{return""}}(t,s)||void 0
|
|
878
|
+
let i
|
|
879
|
+
if(s.cachedDefaultModel){const[e,...t]=s.cachedDefaultModel.split("/")
|
|
880
|
+
i={providerID:e,modelID:t.join("/")}}const a=`${n}/session/${t}/prompt_async`,c={type:"text",text:e}
|
|
881
|
+
o&&(c.metadata=o)
|
|
882
|
+
const l=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[c],...r?{agent:r}:{},...i?{model:i}:{}})})
|
|
883
|
+
return l.ok?(s.logger.debug("Message sent to session",{sessionId:t}),!0):(s.logger.warn("Failed to send message to session",{sessionId:t,status:l.status}),!1)}catch(e){return s.logger.warn("Error sending message to session",{sessionId:t,error:e+""}),!1}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.executeBackground=async function(e,t,r,s,n){const i=await(0,o.spawnActionAgent)({agent:e.agent,prompt:t,cwd:n.projectPath,projectPath:n.projectPath,projectId:n.projectId,title:"Hook: "+e.name,requestId:s,parentSessionId:"independent_session"===e.target?void 0:r||void 0,apiClient:n.apiClient,logger:n.logger,trackChildPid:n.trackChildPid})
|
|
884
|
+
i.success||n.logger.warn("Background hook spawn failed",{hook:e.name,error:i.error})},exports.executeSameSession=async function(e,t,r,o){await s(e,t,r,o)},exports.executeInjectPrompt=async function(e,r,o,i,a){let c=[]
|
|
885
|
+
if("ancestor_chain"===e.target)try{const e=await a.apiClient.get(`/api/projects/${a.projectId}/cli-sessions/external/opencode/${encodeURIComponent(o)}/ancestors`),t=/^ses_[0-9A-Z]{26}$/
|
|
886
|
+
c=(e?.data||e||[]).map((e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title}))).filter((e=>!(!e.externalSessionId||t.test(e.externalSessionId))||(a.logger.warn("inject_prompt: skipping corrupt ancestor (internal-ULID-format externalSessionId)",{sessionId:o,corruptExternalSessionId:e.externalSessionId}),!1))),a.logger.debug("inject_prompt: ancestors fetched",{sessionId:o,count:c.length,ancestors:c.map((e=>e.externalSessionId))})}catch(e){a.logger.warn("inject_prompt: ancestors fetch failed, falling back to parent",{sessionId:o,error:e+""})}if(0===c.length)try{const e=await a.apiClient.get(`/api/projects/${a.projectId}/cli-sessions/external/opencode/${encodeURIComponent(o)}/parent`),t=e?.data?.parentExternalSessionId||e?.parentExternalSessionId
|
|
887
|
+
t&&(c=[{externalSessionId:t}])}catch{}if(0===c.length)return void a.logger.debug("inject_prompt: no ancestors found",{sessionId:o})
|
|
888
|
+
const l=c.map((e=>`${e.agentType||"agent"}${e.title?" "+e.title:""} (${e.externalSessionId})`)).join(" \u2192 "),{interpolatePrompt:d}=await Promise.resolve().then(t.bind(t,4627)),u={...i,chainInfo:l}
|
|
889
|
+
a.logger.debug("inject_prompt: interpolating",{variables:Object.keys(u),chainInfo:l,sessionId:i.sessionId,agentType:i.agentType})
|
|
890
|
+
const h=d(r,u)
|
|
891
|
+
await Promise.allSettled(c.map((e=>(a.logger.debug("inject_prompt: sending to ancestor",{targetSessionId:e.externalSessionId,agentType:e.agentType}),s(h,e.externalSessionId,a,(0,n.partMeta)("inserted-prompt"))))))},exports.executeNotification=async function(e,t){const s=r(t)
|
|
892
|
+
if(s)try{const t=s+"/tui/toast"
|
|
893
|
+
await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:"Background Work Complete",message:e,variant:"success",duration:8e3})})}catch(e){t.logger.warn("Failed to send toast notification",{error:e+""})}},exports.executeDirectReport=async function(e,t,r){try{const o="http://localhost:3456/api/direct-report",n=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sessionId:e,requestId:t||void 0,status:"success"})})
|
|
894
|
+
if(!n.ok)return void r.logger.warn("Direct report failed",{sessionId:e,status:n.status})
|
|
895
|
+
const a=await n.json()
|
|
896
|
+
if(!a.success||!a.data)return void r.logger.warn("Direct report returned failure",{sessionId:e})
|
|
897
|
+
const{notifiedSessions:c,parentExternalSessionId:l}=a.data,d=c||(l?[{externalSessionId:l}]:[])
|
|
898
|
+
await Promise.allSettled(d.map((t=>{const o=t.content?.reportStatus||"success",n=t.content?.summary||"Task completed",a=t.content?.details,c=t.messageCode,l=(0,i.formatReportNotification)({messageCode:c,status:o,summary:n,details:a,fromSessionId:e}),d={alchemy:{kind:"worker-report",payload:{messageCode:c,status:o,summary:n,details:a,fromSessionId:e}}}
|
|
899
|
+
return s(l,t.externalSessionId,r,d)})))}catch(t){r.logger.warn("Error in direct report",{sessionId:e,error:t+""})}}
|
|
900
|
+
const o=t(8148),n=t(2272),i=t(7904)},5086(module){"use strict"
|
|
741
901
|
module.exports=require("ws")},5124(module){"use strict"
|
|
742
902
|
module.exports=require("winston")},5317(module){"use strict"
|
|
743
|
-
module.exports=require("child_process")},
|
|
744
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
903
|
+
module.exports=require("child_process")},5419(e,exports,t){"use strict"
|
|
904
|
+
function r(e){return(0,o.join)((0,s.homedir)(),n.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getGlobalConfigDir=r,exports.getOpencodeSpawnEnv=function(e,t){return{OPENCODE_CLIENT:"alchemy",OPENCODE_DATA_DIR:(0,o.join)(e,n.ALCHEMY_DIR_NAME,"opencode"),OPENCODE_CONFIG_DIR:r(t)}}
|
|
905
|
+
const s=t(857),o=t(6928),n=t(4139)},5607(e,exports,t){"use strict"
|
|
906
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.executeAction=async function(e,t,r){const s=f[e]
|
|
907
|
+
if(!s)return{success:!1,message:"Unknown action type: "+e,error:"No handler registered for action type: "+e}
|
|
908
|
+
try{return await s(t,r)}catch(t){const s=(0,p.getErrorMessage)(t)
|
|
909
|
+
return r.logger.error("Action handler failed",{actionType:e,error:s}),{success:!1,message:"Action failed: "+s,error:s}}}
|
|
910
|
+
const r=t(6636),s=t(7453),o=t(5700),n=t(848),i=t(9735),a=t(7767),c=t(9266),l=t(6555),d=t(3614),u=t(4654),h=t(7078),g=t(1405),p=t(8824),m=t(2272),f={revert_request:r.handleRevertRequestAction,"revert-request":r.handleRevertRequestAction,complete_request:s.handleCompleteRequestAction,"complete-request":s.handleCompleteRequestAction,"finalize-request":n.handleFinalizeRequestAction,"approve-request":i.handleApproveRequestAction,"approve-and-work-request":a.handleApproveAndWorkRequestAction,"restart-mcp":o.handleRestartMcpAction,"opencode-proxy":c.handleOpenCodeProxyAction,"run-tests":l.handleRunTestsAction,async"inject-prompt"(e,r){const{sessionId:s,message:o,kind:n,partPayload:i}=e
|
|
911
|
+
if(!s||!o)return{success:!1,message:"sessionId and message are required",error:"missing_params"}
|
|
912
|
+
const{getOpenCodeUrlFromConfig:a}=await Promise.resolve().then(t.t.bind(t,833,23)),c=a(r.projectPath)
|
|
913
|
+
if(!c)return{success:!1,message:"Cannot read OpenCode config",error:"no_opencode"}
|
|
914
|
+
try{const e=await fetch(`${c}/session/${s}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:o,metadata:(0,m.partMeta)(n??"inserted-prompt",i)}],...r.cachedDefaultModel?(()=>{const[e,...t]=r.cachedDefaultModel.split("/")
|
|
753
915
|
return{model:{providerID:e,modelID:t.join("/")}}})():{}}),signal:AbortSignal.timeout(1e4)})
|
|
754
|
-
return e.ok?{success:!0,message:"Prompt injected into session "+
|
|
755
|
-
function
|
|
756
|
-
return{provider:t||"anthropic",modelId:
|
|
757
|
-
try{const
|
|
758
|
-
if((0,i.existsSync)(
|
|
759
|
-
if(e.provider&&((e.provider.anthropic?.options?.apiKey||process.env.ANTHROPIC_API_KEY)&&(t.anthropic=!0),(e.provider.openai?.options?.apiKey||process.env.OPENAI_API_KEY)&&(t.openai=!0)),e.model){const{provider:
|
|
760
|
-
t[
|
|
761
|
-
if((0,i.existsSync)(t))return JSON.parse((0,i.readFileSync)(t,"utf8")).model||null}catch(e){}return null}function n(e,t,
|
|
916
|
+
return e.ok?{success:!0,message:"Prompt injected into session "+s}:{success:!1,message:"prompt_async failed: "+e.status,error:"prompt_failed"}}catch(e){return{success:!1,message:(0,p.getErrorMessage)(e),error:"prompt_error"}}},"fork-filesystem":d.handleForkFilesystemAction,"prompt-and-wait":u.handlePromptAndWaitAction,"run-ab-prompt":u.handlePromptAndWaitAction,"spawn-auto-planner":h.handleSpawnAutoPlannerAction,"start-building":g.handleStartBuildingAction}},5671(e,exports,t){"use strict"
|
|
917
|
+
function r(e){const[t,...r]=e.split("/")
|
|
918
|
+
return{provider:t||"anthropic",modelId:r.join("/")||e}}function s(e){const t={anthropic:!1,openai:!1}
|
|
919
|
+
try{const s=(0,a.join)(e,"opencode.json")
|
|
920
|
+
if((0,i.existsSync)(s)){const e=JSON.parse((0,i.readFileSync)(s,"utf8"))
|
|
921
|
+
if(e.provider&&((e.provider.anthropic?.options?.apiKey||process.env.ANTHROPIC_API_KEY)&&(t.anthropic=!0),(e.provider.openai?.options?.apiKey||process.env.OPENAI_API_KEY)&&(t.openai=!0)),e.model){const{provider:s}=r(e.model)
|
|
922
|
+
t[s]=!0}}process.env.ANTHROPIC_API_KEY&&(t.anthropic=!0),process.env.OPENAI_API_KEY&&(t.openai=!0)}catch(e){t.anthropic=!0}return t}function o(e){try{const t=(0,a.join)(e,"opencode.json")
|
|
923
|
+
if((0,i.existsSync)(t))return JSON.parse((0,i.readFileSync)(t,"utf8")).model||null}catch(e){}return null}function n(e,t,s,o){const n=r(e)
|
|
762
924
|
let i=n.provider
|
|
763
925
|
if("inherit"!==t){if(!o[t])return{provider:n.provider,modelId:n.modelId,fullModel:e,resolved:!1,reason:`Preferred provider '${t}' not available, using main session model`}
|
|
764
|
-
i=t}const a=function(e,t){const
|
|
765
|
-
if(!
|
|
766
|
-
const
|
|
767
|
-
for(const e of
|
|
926
|
+
i=t}const a=function(e,t){const r=c[e]
|
|
927
|
+
if(!r)return null
|
|
928
|
+
const s=[...r].sort(((e,t)=>t.length-e.length))
|
|
929
|
+
for(const e of s)if(t.includes(e))return e
|
|
768
930
|
return null}(n.provider,n.modelId),d=c[n.provider],u=c[i]
|
|
769
931
|
if(!a||!d||!u)return{provider:n.provider,modelId:n.modelId,fullModel:e,resolved:!1,reason:`Could not determine model family for '${n.modelId}', using main session model`}
|
|
770
932
|
const h=d.indexOf(a)
|
|
771
933
|
let g,p=0
|
|
772
|
-
if("one_down"===
|
|
934
|
+
if("one_down"===s&&(p=1),"two_down"===s&&(p=2),i===n.provider)g=h+p
|
|
773
935
|
else{const e=d.length>1?h/(d.length-1):0
|
|
774
936
|
g=Math.round(e*(u.length-1))+p}g=Math.min(g,u.length-1),g=Math.max(g,0)
|
|
775
|
-
const
|
|
776
|
-
return
|
|
937
|
+
const m=u[g],f=l[i]?.[m]
|
|
938
|
+
return f?{provider:i,modelId:f,fullModel:`${i}/${f}`,resolved:!0}:{provider:n.provider,modelId:n.modelId,fullModel:e,resolved:!0,reason:`No known model for ${i}/${m}, using main session model`}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseModelString=r,exports.getAvailableProviders=s,exports.getMainSessionModel=o,exports.resolveModelTier=n,exports.resolveAgentModel=function(e,t="inherit",i="same",a){const c=a||o(e)
|
|
777
939
|
if(!c)return{provider:"anthropic",modelId:"claude-sonnet-4-5",fullModel:"anthropic/claude-sonnet-4-5",resolved:!1,reason:"Could not determine main session model, using default"}
|
|
778
|
-
if("inherit"===t&&"same"===i){const e=
|
|
779
|
-
return{provider:e.provider,modelId:e.modelId,fullModel:c,resolved:!0}}return n(c,t,i,
|
|
940
|
+
if("inherit"===t&&"same"===i){const e=r(c)
|
|
941
|
+
return{provider:e.provider,modelId:e.modelId,fullModel:c,resolved:!0}}return n(c,t,i,s(e))}
|
|
780
942
|
const i=t(9896),a=t(6928),c={anthropic:["opus","sonnet","haiku"]},l={anthropic:{opus:"claude-opus-4-6",sonnet:"claude-sonnet-4-6",haiku:"claude-haiku-4-5"}}},5700(e,exports,t){"use strict"
|
|
781
|
-
var
|
|
782
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRestartMcpAction=async function(e,t){const{logger:
|
|
783
|
-
|
|
943
|
+
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
944
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRestartMcpAction=async function(e,t){const{logger:r,projectPath:n}=t,i=e.reason||"Manual restart requested"
|
|
945
|
+
r.info("Executing restart-mcp action",{reason:i})
|
|
784
946
|
try{const e=function(e){return o.default.join(e,".alchemy_lab","mcp-restart-trigger")}(n),t=o.default.dirname(e)
|
|
785
|
-
await
|
|
947
|
+
await s.default.mkdir(t,{recursive:!0})
|
|
786
948
|
const a=JSON.stringify({timestamp:(new Date).toISOString(),reason:i},null,2)
|
|
787
|
-
return await
|
|
788
|
-
return
|
|
789
|
-
const r
|
|
949
|
+
return await s.default.writeFile(e,a,"utf-8"),r.info("MCP restart triggered",{triggerPath:e,reason:i}),{success:!0,message:"MCP restart triggered: "+i,data:{triggerPath:e}}}catch(e){const t=e instanceof Error?e.message:e+""
|
|
950
|
+
return r.error("Failed to trigger MCP restart",{error:t}),{success:!1,message:"Failed to trigger MCP restart: "+t,error:t}}}
|
|
951
|
+
const s=r(t(1943)),o=r(t(6928))},6175(e,exports){"use strict"
|
|
790
952
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.TimerConditionEvaluator=void 0
|
|
791
953
|
const t=new Set(["coder","reviewer","tester","journal","planner","researcher"])
|
|
792
|
-
exports.TimerConditionEvaluator=class{constructor(e){this.logger=e}async evaluate(e,t,
|
|
793
|
-
const
|
|
794
|
-
if(!
|
|
795
|
-
const o={totalSessions:
|
|
954
|
+
exports.TimerConditionEvaluator=class{constructor(e){this.logger=e}async evaluate(e,t,r){if(!e||0===e.length)return{passed:!0,variables:{}}
|
|
955
|
+
const s=await this.fetchSessionState(t)
|
|
956
|
+
if(!s)return{passed:!1,variables:{},reason:"Failed to fetch session state"}
|
|
957
|
+
const o={totalSessions:s.sessions.length+"",runningSessions:s.running.length+"",idleSessions:s.idle.length+""}
|
|
796
958
|
let n
|
|
797
|
-
for(const t of e){const e=await this.evaluateSingle(t,r
|
|
959
|
+
for(const t of e){const e=await this.evaluateSingle(t,s,r)
|
|
798
960
|
if(!e.passed)return{passed:!1,variables:o,reason:e.reason}
|
|
799
|
-
Object.assign(o,e.variables),e.targetSessionId&&!n&&(n=e.targetSessionId)}return{passed:!0,targetSessionId:n,variables:o}}async evaluateSingle(e,t,
|
|
800
|
-
case"no_busy_agents":return this.evalNoBusyAgents(t,e.params?.agentType,
|
|
801
|
-
case"session_idle_for":return this.evalSessionIdleFor(t,e.params?.seconds??60,e.params?.agentType,
|
|
961
|
+
Object.assign(o,e.variables),e.targetSessionId&&!n&&(n=e.targetSessionId)}return{passed:!0,targetSessionId:n,variables:o}}async evaluateSingle(e,t,r){switch(e.type){case"no_busy_specialists":return this.evalNoBusySpecialists(t,r)
|
|
962
|
+
case"no_busy_agents":return this.evalNoBusyAgents(t,e.params?.agentType,r)
|
|
963
|
+
case"session_idle_for":return this.evalSessionIdleFor(t,e.params?.seconds??60,e.params?.agentType,r)
|
|
802
964
|
case"any_session_running":return this.evalAnySessionRunning(t)
|
|
803
965
|
case"all_sessions_idle":return this.evalAllSessionsIdle(t)
|
|
804
|
-
default:return this.logger.warn("Unknown timer condition type",{type:e.type}),{passed:!0,variables:{}}}}async evalNoBusySpecialists(e,
|
|
805
|
-
if(!
|
|
806
|
-
let
|
|
807
|
-
for(const o of e.running){const e=await
|
|
808
|
-
t.has(e)&&
|
|
809
|
-
if(!
|
|
810
|
-
let
|
|
811
|
-
for(const o of e.running)await
|
|
812
|
-
return{passed:0===
|
|
966
|
+
default:return this.logger.warn("Unknown timer condition type",{type:e.type}),{passed:!0,variables:{}}}}async evalNoBusySpecialists(e,r){if(0===e.running.length)return{passed:!0,variables:{busySpecialists:"0"}}
|
|
967
|
+
if(!r)return{passed:0===e.running.length,variables:{busySpecialists:e.running.length+""},reason:"No agent type resolver"}
|
|
968
|
+
let s=0
|
|
969
|
+
for(const o of e.running){const e=await r(o.id)
|
|
970
|
+
t.has(e)&&s++}return{passed:0===s,variables:{busySpecialists:s+""},reason:s>0?s+" specialist(s) still running":void 0}}async evalNoBusyAgents(e,t,r){if(!t)return{passed:0===e.running.length,variables:{busyAgents:e.running.length+""},reason:e.running.length>0?e.running.length+" session(s) still running":void 0}
|
|
971
|
+
if(!r)return{passed:!1,variables:{},reason:"No agent type resolver for type filtering"}
|
|
972
|
+
let s=0
|
|
973
|
+
for(const o of e.running)await r(o.id)===t&&s++
|
|
974
|
+
return{passed:0===s,variables:{busyAgents:s+"",checkedAgentType:t},reason:s>0?`${s} ${t} session(s) still running`:void 0}}async evalSessionIdleFor(e,t,r,s){const o=1e3*t,n=Date.now()
|
|
813
975
|
for(const t of e.idle){const e=n-t.time.updated
|
|
814
|
-
if(e>=o){if(s&&
|
|
815
|
-
return{passed:!0,targetSessionId:t.id,variables:{idleSessionId:t.id,idleDurationSeconds:Math.round(e/1e3)+"",idleSessionTitle:t.title||""}}}}return{passed:!1,variables:{},reason:
|
|
976
|
+
if(e>=o){if(r&&s&&await s(t.id)!==r)continue
|
|
977
|
+
return{passed:!0,targetSessionId:t.id,variables:{idleSessionId:t.id,idleDurationSeconds:Math.round(e/1e3)+"",idleSessionTitle:t.title||""}}}}return{passed:!1,variables:{},reason:r?`No ${r} session idle for ${t}s`:`No session idle for ${t}s`}}evalAnySessionRunning(e){const t=e.running.length>0
|
|
816
978
|
return{passed:t,variables:{runningSessions:e.running.length+""},reason:t?void 0:"No sessions running"}}evalAllSessionsIdle(e){if(0===e.sessions.length)return{passed:!1,variables:{},reason:"No sessions exist"}
|
|
817
979
|
const t=0===e.running.length
|
|
818
|
-
return{passed:t,variables:{idleSessions:e.idle.length+"",runningSessions:e.running.length+""},reason:t?void 0:e.running.length+" session(s) still running"}}async fetchSessionState(e){try{const[t,
|
|
819
|
-
if(!t.ok||!
|
|
820
|
-
const
|
|
980
|
+
return{passed:t,variables:{idleSessions:e.idle.length+"",runningSessions:e.running.length+""},reason:t?void 0:e.running.length+" session(s) still running"}}async fetchSessionState(e){try{const[t,r]=await Promise.all([fetch(e+"/session",{signal:AbortSignal.timeout(5e3)}),fetch(e+"/session/status",{signal:AbortSignal.timeout(5e3)})])
|
|
981
|
+
if(!t.ok||!r.ok)return null
|
|
982
|
+
const s=await t.json(),o=await r.json(),n=s.filter((e=>"running"===o[e.id]?.type)),i=s.filter((e=>{const t=o[e.id]?.type
|
|
821
983
|
return"idle"===t||"completed"===t}))
|
|
822
|
-
return{sessions:
|
|
984
|
+
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"
|
|
823
985
|
var t
|
|
824
986
|
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"
|
|
825
|
-
function
|
|
826
|
-
let
|
|
827
|
-
for(let e=0;e<
|
|
828
|
-
|
|
829
|
-
for(const t of e.acceptanceCriteria)
|
|
830
|
-
|
|
831
|
-
if(!
|
|
832
|
-
m.info("Starting run-tests action",{requestId:
|
|
833
|
-
try{const
|
|
834
|
-
if(!
|
|
835
|
-
const
|
|
987
|
+
function r(e,process,t,r,s=!1,o){const n=process.config||{},i=(n.setup,n.commands||[]),a=o&&o.length>0?o.map((e=>i.find((t=>t.name===e)))).filter((e=>e)):i,c=o&&o.length>0&&a.length<i.length
|
|
988
|
+
let l=`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\n## Test Commands${c?" (partial run \u2014 only the commands listed below)":""}\nExecute these in order:\n`
|
|
989
|
+
for(let e=0;e<a.length;e++){const t=a[e]
|
|
990
|
+
l+=`\n### ${e+1}. ${t.name}${!1!==t.required?" (REQUIRED)":" (optional)"}\n- **Command:** \`${t.command}\`\n- **Timeout:** ${t.timeout||300}s\n`}if(l+=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.`,n.verifyAcceptanceCriteria&&e.acceptanceCriteria?.length>0){l+="\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"
|
|
991
|
+
for(const t of e.acceptanceCriteria)l+=`- ${t}\n`
|
|
992
|
+
l+='\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 l}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
|
|
993
|
+
if(!d)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
994
|
+
m.info("Starting run-tests action",{requestId:d,testProcessIds:u})
|
|
995
|
+
try{const l=await f.get(`/api/projects/${y}/requests/${d}`)
|
|
996
|
+
if(!l.success||!l.data)return{success:!1,message:`Request ${d} not found`,error:"Request not found"}
|
|
997
|
+
const g=l.data,b=g.branchName||"requests/"+d
|
|
998
|
+
let I
|
|
999
|
+
if(u?.length)I=(await Promise.all(u.map((e=>f.get(`/api/projects/${y}/test-processes/${e}`))))).filter((e=>e.success&&e.data)).map((e=>e.data))
|
|
1000
|
+
else{const e=await f.get(`/api/projects/${y}/test-processes/active`)
|
|
1001
|
+
I=e.success&&e.data?Array.isArray(e.data)?e.data:[e.data]:[]}if(0===I.length)return{success:!1,message:"No test processes configured for this project. Set up testing first.",error:"No test processes"}
|
|
1002
|
+
const E=Math.max(1,Math.min(8,I[0]?.config?.poolSize??2)),C=I[0]?.config?.fingerprint||[]
|
|
836
1003
|
let S
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
if((
|
|
842
|
-
await
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
await
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1004
|
+
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)
|
|
1005
|
+
try{if((0,s.existsSync)(k))try{const e=(0,n.simpleGit)(k)
|
|
1006
|
+
await e.raw(["reset","--hard",b]),m.info("Reset pool slot to latest branch HEAD",{worktreePath:k,branchName:b,slotIndex:S.index})}catch(e){m.warn("Could not reset slot, recreating",{error:(0,i.getErrorMessage)(e)})
|
|
1007
|
+
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})
|
|
1008
|
+
try{if(!(await T.branch()).all.includes(b))return(0,c.releaseSlot)(k),{success:!1,message:`Branch ${b} not found`,error:"Branch not found"}
|
|
1009
|
+
await T.raw(["worktree","add","--detach",k,b]),m.info("Created pool slot worktree (detached)",{worktreePath:k,branchName:b,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)}
|
|
1010
|
+
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
|
|
1011
|
+
j||m.info("Pool slot fingerprint matches; skipping setup",{worktreePath:k,slotIndex:S.index,fingerprintFiles:C})
|
|
1012
|
+
const{execSync:P}=await Promise.resolve().then(t.t.bind(t,5317,23))
|
|
1013
|
+
if(j){for(const e of I){const t=e.config?.setup||[]
|
|
1014
|
+
if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length,slotIndex:S.index})
|
|
1015
|
+
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)
|
|
1016
|
+
e>0&&m.info("Pruned drift from warm slot",{worktreePath:k,slotIndex:S.index,removed:e})}let M
|
|
1017
|
+
try{const e=(0,n.simpleGit)(k)
|
|
1018
|
+
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=[]
|
|
1019
|
+
let x
|
|
1020
|
+
const $={worktreePath:k,commitSha:M,fingerprintHit:A}
|
|
1021
|
+
for(const process of I){let t
|
|
1022
|
+
const s=e.testRunIds?.[O.length]
|
|
1023
|
+
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)})
|
|
1024
|
+
continue}const o=r(g,process,0,y,p,e.commandNames),n=p?"tester":"auto-tester"
|
|
1025
|
+
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})
|
|
855
1026
|
if(!e.success)throw Error(e.error||"Spawn failed")
|
|
856
|
-
|
|
857
|
-
try{await
|
|
858
|
-
|
|
859
|
-
|
|
1027
|
+
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)})
|
|
1028
|
+
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"}
|
|
1029
|
+
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:I.length,worktreePath:k,spawnedSessionIds:D}}}catch(e){const t=(0,i.getErrorMessage)(e)
|
|
1030
|
+
return m.error("run-tests action failed",{requestId:d,error:t}),{success:!1,message:"Failed to run tests: "+t,error:t}}}
|
|
1031
|
+
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"
|
|
860
1032
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.TimerHookScheduler=void 0
|
|
861
|
-
const
|
|
862
|
-
class n{constructor(e){this.tracking=new Map,this.inFlight=new Set,this.paused=new Set,this.stopped=new Set,this.cachedHooks=[],this.lastHookFetch=0,this.lastActivityTime=0,this.agentTypeCache=new Map,this.logger=e.logger,this.hookExecutor=e.hookExecutor,this.apiClient=e.apiClient,this.getOpenCodeUrl=e.getOpenCodeUrl,this.getDefaultModel=e.getDefaultModel,this.projectId=e.projectId,this.projectPath=e.projectPath,this.conditionEvaluator=new
|
|
863
|
-
return t&&this.logger.info("Timer hook resumed",{hookId:e}),t}stopHook(e){return this.stopped.add(e),this.paused.delete(e),this.inFlight.delete(e),this.tracking.delete(e),this.logger.info("Timer hook stopped",{hookId:e}),!0}setProjectId(e){this.projectId=e}getStatus(){const e=Date.now(),t=this.cachedHooks.map((t=>{const
|
|
864
|
-
return{id:t.id,name:t.name,agent:t.agent,action:t.action||"spawn_agent",intervalSeconds:t.intervalSeconds,maxIterations:t.maxIterations,timerExpiresAfter:t.timerExpiresAfter,stopAfterInactiveMinutes:t.stopAfterInactiveMinutes,fireCount:
|
|
1033
|
+
const r=t(6175),s=t(4627),o=t(4769)
|
|
1034
|
+
class n{constructor(e){this.tracking=new Map,this.inFlight=new Set,this.paused=new Set,this.stopped=new Set,this.cachedHooks=[],this.lastHookFetch=0,this.lastActivityTime=0,this.agentTypeCache=new Map,this.logger=e.logger,this.hookExecutor=e.hookExecutor,this.apiClient=e.apiClient,this.getOpenCodeUrl=e.getOpenCodeUrl,this.getDefaultModel=e.getDefaultModel,this.projectId=e.projectId,this.projectPath=e.projectPath,this.conditionEvaluator=new r.TimerConditionEvaluator(e.logger)}start(){this.tickTimer||(this.logger.info("Timer hook scheduler starting",{tickInterval:1e4}),setTimeout((()=>this.tick().catch((()=>{}))),3e3),this.tickTimer=setInterval((()=>{this.tick().catch((()=>{}))}),1e4))}stop(){this.tickTimer&&(clearInterval(this.tickTimer),this.tickTimer=void 0),this.tracking.clear(),this.inFlight.clear(),this.paused.clear(),this.stopped.clear(),this.cachedHooks=[],this.logger.info("Timer hook scheduler stopped")}pauseHook(e){return!this.stopped.has(e)&&(this.paused.add(e),this.logger.info("Timer hook paused",{hookId:e}),!0)}resumeHook(e){const t=this.paused.delete(e)
|
|
1035
|
+
return t&&this.logger.info("Timer hook resumed",{hookId:e}),t}stopHook(e){return this.stopped.add(e),this.paused.delete(e),this.inFlight.delete(e),this.tracking.delete(e),this.logger.info("Timer hook stopped",{hookId:e}),!0}setProjectId(e){this.projectId=e}getStatus(){const e=Date.now(),t=this.cachedHooks.map((t=>{const r=this.tracking.get(t.id),s=t.stopAfterInactiveMinutes??10,o=this.paused.has(t.id),n=this.stopped.has(t.id),i=!o&&!n&&this.lastActivityTime>0&&s>0&&e-this.lastActivityTime>6e4*s,a=!(!t.maxIterations||!r||r.fireCount<t.maxIterations),c=!(!(t.timerExpiresAfter&&r&&r.firstFired>0)||(e-r.firstFired)/1e3<t.timerExpiresAfter)
|
|
1036
|
+
return{id:t.id,name:t.name,agent:t.agent,action:t.action||"spawn_agent",intervalSeconds:t.intervalSeconds,maxIterations:t.maxIterations,timerExpiresAfter:t.timerExpiresAfter,stopAfterInactiveMinutes:t.stopAfterInactiveMinutes,fireCount:r?.fireCount||0,lastFired:r?.lastFired||0,firstFired:r?.firstFired||0,status:n?"stopped":o?"paused_manual":a?"limit_reached":c?"expired":i?"paused":this.inFlight.has(t.id)?"running":"waiting"}}))
|
|
865
1037
|
return{running:!!this.tickTimer,trackedHooks:this.cachedHooks.length,inFlight:Array.from(this.inFlight),lastActivityTime:this.lastActivityTime,hooks:t}}async tick(){if(!this.projectId)return
|
|
866
1038
|
const e=this.getOpenCodeUrl()
|
|
867
1039
|
if(e)try{const t=await this.fetchTimerHooks()
|
|
868
1040
|
if(0===t.length)return
|
|
869
|
-
const
|
|
1041
|
+
const r=Date.now()
|
|
870
1042
|
await this.updateLastActivityTime(e)
|
|
871
|
-
for(const
|
|
872
|
-
for(const e of this.tracking.keys())
|
|
873
|
-
if(o&&t-o.lastFired<
|
|
1043
|
+
for(const s of t)try{await this.evaluateAndDispatch(s,r,e)}catch(e){this.logger.warn("Error evaluating timer hook",{hookId:s.id,name:s.name,error:e+""})}const s=new Set(t.map((e=>e.id)))
|
|
1044
|
+
for(const e of this.tracking.keys())s.has(e)||this.tracking.delete(e)}catch(e){this.logger.debug("Timer hook tick failed",{error:e+""})}}async evaluateAndDispatch(e,t,r){const s=1e3*(e.intervalSeconds||60),o=this.tracking.get(e.id)
|
|
1045
|
+
if(o&&t-o.lastFired<s)return
|
|
874
1046
|
if(e.maxIterations&&o&&o.fireCount>=e.maxIterations)return
|
|
875
1047
|
if(e.timerExpiresAfter&&o&&o.firstFired>0&&(t-o.firstFired)/1e3>=e.timerExpiresAfter)return
|
|
876
1048
|
if(this.paused.has(e.id)||this.stopped.has(e.id))return
|
|
877
1049
|
const n=e.stopAfterInactiveMinutes??10
|
|
878
1050
|
if(this.lastActivityTime>0&&n>0&&t-this.lastActivityTime>6e4*n)return
|
|
879
1051
|
if(this.inFlight.has(e.id))return
|
|
880
|
-
const i=this.parseConditions(e.timerCondition),a=await this.conditionEvaluator.evaluate(i,
|
|
1052
|
+
const i=this.parseConditions(e.timerCondition),a=await this.conditionEvaluator.evaluate(i,r,(e=>this.resolveAgentType(e)))
|
|
881
1053
|
if(!a.passed)return void this.logger.debug("Timer condition not met",{hook:e.name,reason:a.reason})
|
|
882
1054
|
let c=a.targetSessionId||""
|
|
883
|
-
if("inject_prompt"===e.action&&!c&&(c=await this.findSessionByAgentType(e.agent,
|
|
1055
|
+
if("inject_prompt"===e.action&&!c&&(c=await this.findSessionByAgentType(e.agent,r)||"",!c))return void this.logger.debug("Timer hook skipped: no target session found",{hook:e.name,agent:e.agent})
|
|
884
1056
|
this.logger.info("Firing timer hook",{hook:e.name,hookId:e.id,interval:e.intervalSeconds,fireCount:(o?.fireCount||0)+1,targetSessionId:c||"(spawn)"})
|
|
885
1057
|
const l={lastFired:t,firstFired:o?.firstFired||t,fireCount:(o?.fireCount||0)+1}
|
|
886
|
-
this.tracking.set(e.id,l),this.inFlight.add(e.id),this.dispatchHook(e,c,a.variables).finally((()=>{this.inFlight.delete(e.id)}))}async dispatchHook(e,t,
|
|
1058
|
+
this.tracking.set(e.id,l),this.inFlight.add(e.id),this.dispatchHook(e,c,a.variables).finally((()=>{this.inFlight.delete(e.id)}))}async dispatchHook(e,t,r){try{const n=this.tracking.get(e.id),i={...r,hookName:e.name,hookId:e.id,intervalSeconds:(e.intervalSeconds||0)+"",fireCount:(n?.fireCount||0)+"",sessionId:t||""},a=(0,s.interpolatePrompt)(e.promptTemplate,i),c={logger:this.logger,projectId:this.projectId,projectPath:this.projectPath,apiClient:this.apiClient,cachedDefaultModel:this.getDefaultModel()}
|
|
887
1059
|
"inject_prompt"===e.action&&t?await(0,o.executeSameSession)(a,t,c):await(0,o.executeBackground)(e,a,t,void 0,c),this.apiClient.post(`/api/projects/${this.projectId}/hook-triggers`,{hookId:e.id,hookName:e.name,eventType:"timer",action:e.action,target:e.target,agent:e.agent}).catch((()=>{}))}catch(t){this.logger.warn("Timer hook dispatch failed",{hook:e.name,error:t+""})}}async updateLastActivityTime(e){try{const t=await fetch(e+"/session",{signal:AbortSignal.timeout(5e3)})
|
|
888
1060
|
if(!t.ok)return
|
|
889
|
-
const
|
|
890
|
-
let
|
|
891
|
-
for(const e of
|
|
892
|
-
|
|
893
|
-
if(
|
|
894
|
-
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/event-hooks/timer`),
|
|
895
|
-
this.cachedHooks=
|
|
1061
|
+
const r=await t.json()
|
|
1062
|
+
let s=0
|
|
1063
|
+
for(const e of r)e.time.updated>s&&(s=e.time.updated)
|
|
1064
|
+
s>0&&(this.lastActivityTime=s)}catch{}}invalidateCache(){this.lastHookFetch=0}async fetchTimerHooks(){const e=Date.now()
|
|
1065
|
+
if(18e5>e-this.lastHookFetch)return this.cachedHooks
|
|
1066
|
+
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/event-hooks/timer`),r=t?.data||t||[]
|
|
1067
|
+
this.cachedHooks=r.map((e=>({id:e.id||e.hookId,name:e.name||e.hookName,eventType:"timer",agent:e.agent||e.hookAgent,promptTemplate:e.promptTemplate||e.hookPromptTemplate,condition:e.condition||e.hookCondition||{},enabled:!1!==e.enabled,action:e.action||e.hookAction||"spawn_agent",target:e.target||e.hookTarget||"independent_session",timing:"after",maxIterations:e.maxIterations||e.hookMaxIterations||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}))),this.lastHookFetch=e,this.cachedHooks.length>0&&this.logger.debug("Fetched timer hooks",{count:this.cachedHooks.length,hooks:this.cachedHooks.map((e=>e.name))})}catch(t){this.logger.debug("Failed to fetch timer hooks",{error:t+""}),this.lastHookFetch=e}return this.cachedHooks}parseConditions(e){return e&&Array.isArray(e)?e.filter((e=>e&&e.type)):[]}async resolveAgentType(e){const t=this.agentTypeCache.get(e)
|
|
896
1068
|
if(t&&Date.now()-t.time<n.AGENT_TYPE_CACHE_TTL)return t.type
|
|
897
|
-
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1069
|
+
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),r=t?.data||t,s=r?.agentType||""
|
|
1070
|
+
if(this.agentTypeCache.size>=n.AGENT_TYPE_CACHE_MAX){const e=this.agentTypeCache.keys().next().value
|
|
1071
|
+
void 0!==e&&this.agentTypeCache.delete(e)}return this.agentTypeCache.set(e,{type:s,time:Date.now()}),s}catch{return""}}async findSessionByAgentType(e,t){try{const[r,s]=await Promise.all([fetch(t+"/session",{signal:AbortSignal.timeout(5e3)}),fetch(t+"/session/status",{signal:AbortSignal.timeout(5e3)})])
|
|
1072
|
+
if(!r.ok)return this.logger.warn("findSessionByAgentType: failed to fetch sessions",{status:r.status}),null
|
|
1073
|
+
const o=await r.json(),n=s.ok?await s.json():{}
|
|
901
1074
|
if(0===o.length)return this.logger.debug("findSessionByAgentType: no sessions found"),null
|
|
902
|
-
const i=o.sort(((e,t)=>{const
|
|
903
|
-
return s
|
|
904
|
-
for(let t=0;l>t;t++){const
|
|
905
|
-
if(
|
|
906
|
-
if(c&&a(
|
|
907
|
-
return u.length>0?(this.logger.debug("findSessionByAgentType: fallback to root session",{sessionId:u[0].id,wanted:e}),u[0].id):(this.logger.debug("findSessionByAgentType: fallback to most recent session",{sessionId:i[0].id,wanted:e}),i[0].id)}catch(t){return this.logger.warn("findSessionByAgentType failed",{agentType:e,error:t+""}),null}}}exports.TimerHookScheduler=n,n.AGENT_TYPE_CACHE_TTL=6e4},6636(e,exports,t){"use strict"
|
|
1075
|
+
const i=o.sort(((e,t)=>{const r="running"===n[e.id]?.type||"idle"===n[e.id]?.type?1:0,s="running"===n[t.id]?.type||"idle"===n[t.id]?.type?1:0
|
|
1076
|
+
return r!==s?s-r:t.time.updated-e.time.updated})),a=e=>!e||"default"===e||"alchemist"===e||e.includes("alchemist"),c="default"===e||"alchemist"===e,l=Math.min(i.length,15)
|
|
1077
|
+
for(let t=0;l>t;t++){const r=i[t],s=await this.resolveAgentType(r.id)
|
|
1078
|
+
if(s===e)return this.logger.debug("findSessionByAgentType: exact match",{sessionId:r.id,agentType:s}),r.id
|
|
1079
|
+
if(c&&a(s))return this.logger.debug("findSessionByAgentType: orchestrator match",{sessionId:r.id,agentType:s,wanted:e}),r.id}const d=e=>!(!e.parentID&&!e.parentId),u=i.filter((e=>!d(e)))
|
|
1080
|
+
return u.length>0?(this.logger.debug("findSessionByAgentType: fallback to root session",{sessionId:u[0].id,wanted:e}),u[0].id):(this.logger.debug("findSessionByAgentType: fallback to most recent session",{sessionId:i[0].id,wanted:e}),i[0].id)}catch(t){return this.logger.warn("findSessionByAgentType failed",{agentType:e,error:t+""}),null}}}exports.TimerHookScheduler=n,n.AGENT_TYPE_CACHE_TTL=6e4,n.AGENT_TYPE_CACHE_MAX=500},6636(e,exports,t){"use strict"
|
|
908
1081
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRevertRequestAction=async function(e,t){const{requestId:o,reason:n}=e,{logger:i,apiClient:a,projectPath:c,projectId:l}=t
|
|
909
1082
|
if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
910
1083
|
if(!n)return{success:!1,message:"reason is required",error:"Missing reason in payload"}
|
|
@@ -913,29 +1086,29 @@ try{const e=await a.post(`/api/projects/${l}/requests/${o}/validate-revert`,{})
|
|
|
913
1086
|
if(!e.success||!e.data)return{success:!1,message:"Server validation failed: "+(e.data?.reason||"Unknown error"),error:"Validation failed"}
|
|
914
1087
|
const t=e.data
|
|
915
1088
|
if(!t.canRevert){let e="Cannot revert request: "+t.reason
|
|
916
|
-
return t.lifoViolation?.blockingRequests?.length>0&&(e+="\n\nRequests that must be reverted first:\n",t.lifoViolation.blockingRequests.forEach(((t,
|
|
1089
|
+
return t.lifoViolation?.blockingRequests?.length>0&&(e+="\n\nRequests that must be reverted first:\n",t.lifoViolation.blockingRequests.forEach(((t,r)=>{e+=`${r+1}. ${t.requestId}: ${t.title}\n`}))),{success:!1,message:e,error:t.reason}}if("cancel"===t.operationType){const e=await a.post(`/api/projects/${l}/requests/${o}/finalize-revert`,{reason:n,operationType:"cancel"})
|
|
917
1090
|
return e.success?{success:!0,message:"Successfully canceled request "+o,data:{operationType:"cancel",reason:n}}:{success:!1,message:"Failed to finalize cancel operation",error:e.error?.message||"Finalize failed"}}let d=t.mergeCommitHash
|
|
918
1091
|
if(!d){i.info("No merge commit hash from server, searching git log",{requestId:o})
|
|
919
|
-
const e=await async function(e,t){try{const
|
|
920
|
-
for(const e of o){const[t,...
|
|
921
|
-
for(const e of n)if(
|
|
1092
|
+
const e=await async function(e,t){try{const s=(0,r.simpleGit)(t),o=(await s.raw(["log","--merges","--format=%H %s","HEAD"])).trim().split("\n").filter((e=>e.trim())),n=[`Merge requests/${e}-`,`Merge requests/${e} `,"Merge branch 'requests/"+e,"requests/"+e]
|
|
1093
|
+
for(const e of o){const[t,...r]=e.split(" "),s=r.join(" ")
|
|
1094
|
+
for(const e of n)if(s.includes(e))return{found:!0,commitHash:t}}return{found:!1,message:"No merge commit found for request "+e}}catch(e){return{found:!1,message:"Error searching for merge commit: "+(0,s.getErrorMessage)(e)}}}(o,c)
|
|
922
1095
|
if(!e.found||!e.commitHash)return{success:!1,message:"Could not find merge commit for request "+o,error:"Merge commit not found"}
|
|
923
|
-
d=e.commitHash}const u=(0,
|
|
1096
|
+
d=e.commitHash}const u=(0,r.simpleGit)(c),h=await u.status(),g=(await u.branch()).all.includes("main")?"main":"master"
|
|
924
1097
|
let p
|
|
925
1098
|
h.current!==g&&(i.info("Switching to default branch",{from:h.current,to:g}),await u.checkout(g))
|
|
926
1099
|
try{i.info("Executing git revert",{mergeCommitHash:d.substring(0,8)}),await u.raw(["revert","-m","1","--no-edit",d])
|
|
927
1100
|
const e=await u.log({maxCount:1})
|
|
928
|
-
p=e.latest?.hash||"",i.info("Git revert completed",{originalCommit:d.substring(0,8),revertCommit:p.substring(0,8)})}catch(e){const t=(0,
|
|
1101
|
+
p=e.latest?.hash||"",i.info("Git revert completed",{originalCommit:d.substring(0,8),revertCommit:p.substring(0,8)})}catch(e){const t=(0,s.getErrorMessage)(e)
|
|
929
1102
|
i.error("Git revert failed",{requestId:o,error:t})
|
|
930
|
-
try{await u.raw(["revert","--abort"])}catch(e){i.error("CRITICAL: git revert --abort failed \u2014 repo may be in inconsistent state",{requestId:o,error:e instanceof Error?e.message:e+""})}return{success:!1,message:"Git revert failed: "+t,error:t}}const
|
|
931
|
-
return
|
|
1103
|
+
try{await u.raw(["revert","--abort"])}catch(e){i.error("CRITICAL: git revert --abort failed \u2014 repo may be in inconsistent state",{requestId:o,error:e instanceof Error?e.message:e+""})}return{success:!1,message:"Git revert failed: "+t,error:t}}const m=await a.post(`/api/projects/${l}/requests/${o}/finalize-revert`,{reason:n,operationType:"revert",revertCommitHash:p,originalMergeCommitHash:d})
|
|
1104
|
+
return m.success?{success:!0,message:"Successfully reverted request "+o,data:{operationType:"revert",originalMergeCommit:d,revertCommitHash:p,reason:n}}:(i.error("CRITICAL: Git revert succeeded but finalization failed",{requestId:o,revertCommitHash:p,error:m.error}),{success:!1,message:`CRITICAL: Git revert succeeded (commit: ${p.substring(0,8)}) but server finalization failed`,error:"Finalization failed"})}catch(e){const t=(0,s.getErrorMessage)(e)
|
|
932
1105
|
return i.error("Revert_request action failed",{requestId:o,error:t}),{success:!1,message:"Failed to revert request: "+t,error:t}}}
|
|
933
|
-
const
|
|
934
|
-
function
|
|
935
|
-
|
|
936
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.isPortAvailable=
|
|
937
|
-
if(await
|
|
938
|
-
const o=
|
|
1106
|
+
const r=t(3807),s=t(8824)},6800(e,exports,t){"use strict"
|
|
1107
|
+
function r(e){return new Promise((t=>{const r=o.default.createServer()
|
|
1108
|
+
r.once("error",(e=>{e.code,t(!1)})),r.once("listening",(()=>{r.close((()=>{t(!0)}))})),r.listen(e)}))}var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
1109
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.isPortAvailable=r,exports.findAvailablePort=async function(e,t=10){for(let s=0;t>s;s++){const t=e+s
|
|
1110
|
+
if(await r(t))return t}throw Error(`No available port found after ${t} attempts starting from port ${e}`)}
|
|
1111
|
+
const o=s(t(9278))},6895(module){"use strict"
|
|
939
1112
|
module.exports=require("chalk")},6928(module){"use strict"
|
|
940
1113
|
module.exports=require("path")},6982(module){"use strict"
|
|
941
1114
|
module.exports=require("crypto")},7078(e,exports,t){"use strict"
|
|
@@ -944,23 +1117,44 @@ if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId
|
|
|
944
1117
|
i.info("Starting spawn-auto-planner action",{requestId:o})
|
|
945
1118
|
try{const e=await a.get(`/api/projects/${c}/requests/${o}`)
|
|
946
1119
|
if(!e.success||!e.data)return{success:!1,message:`Request ${o} not found`,error:"Request not found"}
|
|
947
|
-
const t=e.data,
|
|
948
|
-
return h.success?(i.info("Spawned auto-planner for request",{requestId:o,sessionId:h.sessionId,pid:h.pid}),{success:!0,message:"Auto-planner spawned for request "+o,data:{sessionId:h.sessionId,pid:h.pid}}):{success:!1,message:"Failed to spawn auto-planner: "+h.error,error:h.error||"Spawn failed"}}catch(e){const t=(0,
|
|
1120
|
+
const t=e.data,r=Array.isArray(t.acceptanceCriteria)?t.acceptanceCriteria.map((e=>e.description||e)).join("\n- "):"",u=`Create a plan for request ${o}: ${t.title}\n\nDescription: ${t.description||"No description provided."}\n\n${r?"Acceptance Criteria:\n- "+r:""}\n\nResearch the codebase and create a comprehensive implementation plan.`,h=await(0,s.spawnActionAgent)({agent:"auto-planner",prompt:u,cwd:l,projectPath:l,projectId:c,title:`[${o}] Plan: ${t.title}`,requestId:o,parentSessionId:n,apiClient:a,logger:i,trackChildPid:d})
|
|
1121
|
+
return h.success?(i.info("Spawned auto-planner for request",{requestId:o,sessionId:h.sessionId,pid:h.pid}),{success:!0,message:"Auto-planner spawned for request "+o,data:{sessionId:h.sessionId,pid:h.pid}}):{success:!1,message:"Failed to spawn auto-planner: "+h.error,error:h.error||"Spawn failed"}}catch(e){const t=(0,r.getErrorMessage)(e)
|
|
949
1122
|
return i.error("spawn-auto-planner action failed",{requestId:o,error:t}),{success:!1,message:"Failed to spawn auto-planner: "+t,error:t}}}
|
|
950
|
-
const
|
|
1123
|
+
const r=t(8824),s=t(8148)},7163(e,exports){"use strict"
|
|
951
1124
|
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"
|
|
952
1125
|
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
|
|
953
|
-
var
|
|
954
|
-
Object.defineProperty(exports,"HookExecutor",{enumerable:!0,get(){return
|
|
955
|
-
var
|
|
956
|
-
Object.defineProperty(exports,"HookCache",{enumerable:!0,get(){return
|
|
1126
|
+
var r=t(3077)
|
|
1127
|
+
Object.defineProperty(exports,"HookExecutor",{enumerable:!0,get(){return r.HookExecutor}})
|
|
1128
|
+
var s=t(8740)
|
|
1129
|
+
Object.defineProperty(exports,"HookCache",{enumerable:!0,get(){return s.HookCache}})
|
|
957
1130
|
var o=t(6563)
|
|
958
1131
|
Object.defineProperty(exports,"TimerHookScheduler",{enumerable:!0,get(){return o.TimerHookScheduler}})
|
|
959
1132
|
var n=t(6175)
|
|
960
1133
|
Object.defineProperty(exports,"TimerConditionEvaluator",{enumerable:!0,get(){return n.TimerConditionEvaluator}})
|
|
961
1134
|
var i=t(4627)
|
|
962
|
-
Object.defineProperty(exports,"evaluateCondition",{enumerable:!0,get(){return i.evaluateCondition}}),Object.defineProperty(exports,"interpolatePrompt",{enumerable:!0,get(){return i.interpolatePrompt}}),Object.defineProperty(exports,"determineEventToEmit",{enumerable:!0,get(){return i.determineEventToEmit}}),Object.defineProperty(exports,"isDuplicateToolCall",{enumerable:!0,get(){return i.isDuplicateToolCall}}),Object.defineProperty(exports,"createToolCallHash",{enumerable:!0,get(){return i.createToolCallHash}}),Object.defineProperty(exports,"tryMarkMessageProcessed",{enumerable:!0,get(){return i.tryMarkMessageProcessed}})},
|
|
963
|
-
|
|
1135
|
+
Object.defineProperty(exports,"evaluateCondition",{enumerable:!0,get(){return i.evaluateCondition}}),Object.defineProperty(exports,"interpolatePrompt",{enumerable:!0,get(){return i.interpolatePrompt}}),Object.defineProperty(exports,"determineEventToEmit",{enumerable:!0,get(){return i.determineEventToEmit}}),Object.defineProperty(exports,"isDuplicateToolCall",{enumerable:!0,get(){return i.isDuplicateToolCall}}),Object.defineProperty(exports,"createToolCallHash",{enumerable:!0,get(){return i.createToolCallHash}}),Object.defineProperty(exports,"tryMarkMessageProcessed",{enumerable:!0,get(){return i.tryMarkMessageProcessed}})},7227(e,exports){"use strict"
|
|
1136
|
+
function t(e){if("string"!=typeof e)return e
|
|
1137
|
+
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
|
|
1138
|
+
if("before"===c)return[{eventType:"tool_executed",timing:"before",variables:{tool:o,toolName:o,input:JSON.stringify(i??{}).substring(0,1e3),sessionId:n||""}}]
|
|
1139
|
+
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"),b=o.includes("document_operations"),I=o.includes("query_history")
|
|
1140
|
+
if(h&&d){const e=i?.operation,t=function(e){for(const t of r){const r=e.match(t)
|
|
1141
|
+
if(r)return r[1]}return null}(d)
|
|
1142
|
+
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)
|
|
1143
|
+
return t?t[1]:null}(d)
|
|
1144
|
+
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)
|
|
1145
|
+
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)
|
|
1146
|
+
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)
|
|
1147
|
+
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)
|
|
1148
|
+
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||"*"}})
|
|
1149
|
+
else if(w){const e=t(i?.operations)||[],r=(Array.isArray(e)?e:[]).map((e=>e.operationType)).filter(Boolean)
|
|
1150
|
+
l.push({eventType:"manage_files",timing:"after",variables:{...u,operationTypes:[...new Set(r)].join(", ")}})}else b?l.push({eventType:"document_operations",timing:"after",variables:u}):I&&l.push({eventType:"query_history",timing:"after",variables:u})
|
|
1151
|
+
return l.push({eventType:"tool_executed",timing:"after",variables:{tool:o,toolName:o,output:d,sessionId:n||""}}),l}
|
|
1152
|
+
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"
|
|
1153
|
+
module.exports=require("express")},7388(e,exports,t){"use strict"
|
|
1154
|
+
var r=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r)
|
|
1155
|
+
var o=Object.getOwnPropertyDescriptor(t,r)
|
|
1156
|
+
o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get(){return t[r]}}),Object.defineProperty(e,s,o)}:function(e,t,r,s){void 0===s&&(s=r),e[s]=t[r]}),s=this&&this.__exportStar||function(e,exports){for(var t in e)"default"===t||{}.hasOwnProperty.call(exports,t)||r(exports,e,t)}
|
|
1157
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),s(t(441),exports),s(t(270),exports)},7453(e,exports,t){"use strict"
|
|
964
1158
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleCompleteRequestAction=async function(e,t){const{requestId:o,reason:n}=e,{logger:i,apiClient:a,projectPath:c,projectId:l}=t
|
|
965
1159
|
if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
966
1160
|
i.info("Starting complete_request action",{requestId:o,reason:n})
|
|
@@ -968,23 +1162,160 @@ try{const e=await a.post(`/api/projects/${l}/requests/${o}/validate-completion`,
|
|
|
968
1162
|
if(!e.success||!e.data)return{success:!1,message:"Server validation failed: "+(e.data?.reason||"Unknown error"),error:"Validation failed"}
|
|
969
1163
|
const c=e.data
|
|
970
1164
|
if(!c.canComplete){let e="Cannot complete request: "+c.reason
|
|
971
|
-
return c.lifoViolation?.blockingRequests?.length>0&&(e+="\n\nRequests that must be re-completed first:\n",c.lifoViolation.blockingRequests.forEach(((t,
|
|
1165
|
+
return c.lifoViolation?.blockingRequests?.length>0&&(e+="\n\nRequests that must be re-completed first:\n",c.lifoViolation.blockingRequests.forEach(((t,r)=>{e+=`${r+1}. ${t.requestId}: ${t.title}\n`}))),c.blockedByTasks?.length>0&&(e+="\n\nTasks that must be completed first:\n",c.blockedByTasks.forEach(((t,r)=>{e+=`${r+1}. ${t.taskId}: ${t.title} (${t.status})\n`}))),{success:!1,message:e,error:c.reason}}if("revert-removal"!==c.mergeStrategy)return{success:!1,message:"Action server only handles reverted request re-completion. Got merge strategy: "+c.mergeStrategy,error:"Unsupported merge strategy: "+c.mergeStrategy}
|
|
972
1166
|
if(!c.revertInfo?.revertCommitHash)return{success:!1,message:"Server validated but did not provide revert commit hash",error:"Missing revert commit hash"}
|
|
973
1167
|
const d=await async function(e,t,o){const{logger:n,projectPath:i}=o
|
|
974
1168
|
n.info("Performing revert removal",{requestId:e,revertCommitHash:t.substring(0,8)})
|
|
975
|
-
const a=(0,
|
|
1169
|
+
const a=(0,r.simpleGit)(i),c=(await a.branch()).all.includes("main")?"main":"master",l=await a.status()
|
|
976
1170
|
if(l.current!==c&&(n.info("Switching to default branch",{from:l.current,to:c}),await a.checkout(c)),(await a.status()).files.length>0)return{success:!1,message:"Cannot re-complete request: working directory has uncommitted changes",error:"Uncommitted changes present"}
|
|
977
|
-
if((await a.revparse(["HEAD"])).trim()!==t){const e=await a.raw(["rev-list","--count",t+"..HEAD"]),
|
|
978
|
-
if(
|
|
979
|
-
await a.reset(["--hard",
|
|
980
|
-
const
|
|
981
|
-
if(
|
|
982
|
-
return n.info("Revert removal succeeded",{requestId:e,removedCommit:t.substring(0,8),restoredTo:
|
|
983
|
-
return n.error("Revert removal failed",{requestId:e,error:
|
|
1171
|
+
if((await a.revparse(["HEAD"])).trim()!==t){const e=await a.raw(["rev-list","--count",t+"..HEAD"]),r=parseInt(e.trim(),10)
|
|
1172
|
+
if(r>0)return{success:!1,message:`Cannot re-complete: main branch has ${r} commit(s) since the revert. Create a new request instead.`,error:"Main branch has changed since revert"}}try{const r=(await a.raw(["rev-parse",t+"^"])).trim()
|
|
1173
|
+
await a.reset(["--hard",r])
|
|
1174
|
+
const s=await a.revparse(["HEAD"])
|
|
1175
|
+
if(s.trim()!==r)throw Error(`Git reset verification failed: expected ${r.substring(0,8)}, got ${s.substring(0,8)}`)
|
|
1176
|
+
return n.info("Revert removal succeeded",{requestId:e,removedCommit:t.substring(0,8),restoredTo:r.substring(0,8)}),{success:!0,message:"Successfully re-completed request "+e,data:{operationType:"revert-removal",removedRevertCommit:t,restoredToCommit:r}}}catch(t){const r=(0,s.getErrorMessage)(t)
|
|
1177
|
+
return n.error("Revert removal failed",{requestId:e,error:r}),{success:!1,message:"Failed to remove revert commit: "+r,error:r}}}(o,c.revertInfo.revertCommitHash,t)
|
|
984
1178
|
if(!d.success)return d
|
|
985
|
-
try{await a.post(`/api/projects/${l}/requests/${o}/finalize-completion`,{summary:n||"Re-completed from UI",acceptanceEvidence:[{evidence:"Re-completed reverted request via GUI action"}],mergeResult:{mergeCommitHash:d.data.restoredToCommit,method:"revert-removal"}})}catch(e){return i.error("Merge succeeded but finalization failed",{requestId:o,error:(0,
|
|
1179
|
+
try{await a.post(`/api/projects/${l}/requests/${o}/finalize-completion`,{summary:n||"Re-completed from UI",acceptanceEvidence:[{evidence:"Re-completed reverted request via GUI action"}],mergeResult:{mergeCommitHash:d.data.restoredToCommit,method:"revert-removal"}})}catch(e){return i.error("Merge succeeded but finalization failed",{requestId:o,error:(0,s.getErrorMessage)(e)}),{success:!1,message:"Git revert removal succeeded but server finalization failed: "+(0,s.getErrorMessage)(e),error:"Finalization failed"}}return d}catch(e){const t=(0,s.getErrorMessage)(e)
|
|
986
1180
|
return i.error("Complete_request action failed",{requestId:o,error:t}),{success:!1,message:"Failed to complete request: "+t,error:t}}}
|
|
987
|
-
const
|
|
1181
|
+
const r=t(3807),s=t(8824)},7478(e,exports,t){"use strict"
|
|
1182
|
+
async function r(e,t,r){const s=r.startTimer("git_get_current_branch_duration")
|
|
1183
|
+
try{const o=(await e.status()).current
|
|
1184
|
+
if(!o)throw new g.ValidationError("Repository is in detached HEAD state or branch detection failed")
|
|
1185
|
+
return t.debug("Retrieved current branch",{branch:o}),r.incrementCounter("git_get_current_branch_success",1),s({success:!0}),o}catch(e){const o=(0,m.getErrorMessage)(e)
|
|
1186
|
+
throw t.error("Failed to get current branch",{error:o}),r.incrementCounter("git_get_current_branch_failure",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Failed to get current branch: "+o)}}async function s(e,t,r,s){const o=s.startTimer("git_branch_exists_duration")
|
|
1187
|
+
try{const n=(await e.branch(["-l"])).all.includes(t)
|
|
1188
|
+
return r.debug("Checked branch existence",{branchName:t,exists:n}),s.incrementCounter("git_branch_exists_check",1,{exists:n.toString()}),o({success:!0}),n}catch(e){const n=(0,m.getErrorMessage)(e)
|
|
1189
|
+
return r.error("Failed to check branch existence",{branchName:t,error:n}),s.incrementCounter("git_branch_exists_failure",1),o({success:!1,error:e instanceof Error?e.name:"unknown"}),!1}}async function o(e,t,o,n){const i=n.startTimer("git_switch_branch_duration")
|
|
1190
|
+
try{const a=await r(e,o,n)
|
|
1191
|
+
if(a===t)return o.debug("Already on target branch",{branchName:t}),n.incrementCounter("git_switch_branch_noop",1),i({success:!0,reason:"already_on_branch"}),{success:!0,branchName:t,previousBranch:a,message:`Already on branch '${t}'`}
|
|
1192
|
+
if(!await s(e,t,o,n)){const e=`Branch '${t}' does not exist`
|
|
1193
|
+
throw o.error(e),n.incrementCounter("git_switch_branch_not_found",1),i({success:!1,reason:"branch_not_found"}),new g.ValidationError(e)}await e.checkout(t)
|
|
1194
|
+
const c=await e.revparse(["HEAD"])
|
|
1195
|
+
return o.info("Switched to branch",{branchName:t,previousBranch:a,commitHash:c.substring(0,8)}),n.incrementCounter("git_switch_branch_success",1),i({success:!0}),{success:!0,branchName:t,previousBranch:a,commitHash:c,message:`Switched from '${a}' to '${t}'`}}catch(e){const r=(0,m.getErrorMessage)(e)
|
|
1196
|
+
throw o.error("Failed to switch branch",{branchName:t,error:r}),n.incrementCounter("git_switch_branch_failure",1),i({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError(`Failed to switch to branch '${t}': ${r}`)}}async function n(e,t,r){const s=r.startTimer("git_repository_status_duration")
|
|
1197
|
+
try{let o,n=!0
|
|
1198
|
+
try{o=await e.status()}catch(e){return n=!1,t.warn("Not a git repository or git not available"),r.incrementCounter("git_repository_status_not_git",1),s({success:!0,reason:"not_git_repo"}),{isGitRepository:!1,currentBranch:"",isClean:!1,hasMainBranch:!1,hasMasterBranch:!1,defaultBranch:"",uncommittedChanges:[],branches:[]}}const i=o.current||"",a=0===o.files.length,c=o.files.map((e=>e.path)),l=await e.branch(["-l"]),d=l.all.map((e=>({name:e,isCurrentBranch:e===i}))),u=l.all.includes("main"),h=l.all.includes("master")
|
|
1199
|
+
let g=""
|
|
1200
|
+
return u?g="main":h?g="master":d.length>0&&(g=d[0].name),t.debug("Retrieved repository status",{currentBranch:i,isClean:a,hasMainBranch:u,hasMasterBranch:h,defaultBranch:g,branchCount:d.length,uncommittedCount:c.length}),r.incrementCounter("git_repository_status_success",1),s({success:!0}),{isGitRepository:n,currentBranch:i,isClean:a,hasMainBranch:u,hasMasterBranch:h,defaultBranch:g,uncommittedChanges:c,branches:d}}catch(e){const o=(0,m.getErrorMessage)(e)
|
|
1201
|
+
throw t.error("Failed to get repository status",{error:o}),r.incrementCounter("git_repository_status_failure",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Failed to get repository status: "+o)}}function i(e){return"requests/"+e}async function a(e,t,r){const s=r.startTimer("git_uncommitted_changes_check_duration")
|
|
1202
|
+
try{t.debug("Starting uncommitted changes check"),t.debug("Checking for uncommitted changes before merge"),Date.now()
|
|
1203
|
+
const o=await e.status()
|
|
1204
|
+
Date.now(),t.debug("Git status result",{files:o.files.map((e=>({path:e.path,index:e.index,working_dir:e.working_dir}))),totalFiles:o.files.length})
|
|
1205
|
+
const n=[],i=[],a=[]
|
|
1206
|
+
o.files.forEach((e=>{t.debug("Processing file",{path:e.path,index:e.index,workingDir:e.working_dir})," "!==e.index&&"?"!==e.index&&(n.push(e.path),t.debug("Added to staged",{path:e.path}))," "!==e.working_dir&&"?"!==e.working_dir&&(i.push(e.path),t.debug("Added to unstaged",{path:e.path})),"?"!==e.working_dir&&"?"!==e.index||(a.push(e.path),t.debug("Added to untracked",{path:e.path}))}))
|
|
1207
|
+
const c=n.length+i.length+a.length,l=c>0
|
|
1208
|
+
t.debug("Uncommitted changes summary",{hasChanges:l,totalFiles:c,staged:n.length,unstaged:i.length,untracked:a.length,stagedFiles:n,unstagedFiles:i,untrackedFiles:a})
|
|
1209
|
+
let d=""
|
|
1210
|
+
if(l){const e=[]
|
|
1211
|
+
n.length>0&&e.push(n.length+" staged file(s)"),i.length>0&&e.push(i.length+" unstaged file(s)"),a.length>0&&e.push(a.length+" untracked file(s)"),d=`Found ${c} uncommitted change(s): ${e.join(", ")}`}else d="No uncommitted changes detected"
|
|
1212
|
+
return(0,h.isDebugToolsEnabled)()&&t.debug("Summary",{summary:d}),t.debug("Uncommitted changes check completed",{hasChanges:l,staged:n.length,unstaged:i.length,untracked:a.length,total:c,summary:d}),r.incrementCounter("git_uncommitted_changes_check_success",1),r.recordValue("git_uncommitted_changes_total_files",c),s({success:!0}),{hasChanges:l,stagedFiles:n,unstagedFiles:i,untrackedFiles:a,totalFiles:c,summary:d}}catch(e){const o=(0,m.getErrorMessage)(e)
|
|
1213
|
+
throw t.error("Failed to check uncommitted changes",{error:o}),r.incrementCounter("git_uncommitted_changes_check_failure",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Failed to check uncommitted changes: "+o)}}var c,l=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r)
|
|
1214
|
+
var o=Object.getOwnPropertyDescriptor(t,r)
|
|
1215
|
+
o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get(){return t[r]}}),Object.defineProperty(e,s,o)}:function(e,t,r,s){void 0===s&&(s=r),e[s]=t[r]}),d=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),u=this&&this.__importStar||(c=function(e){return c=Object.getOwnPropertyNames||function(e){var t=[]
|
|
1216
|
+
for(var r in e)({}).hasOwnProperty.call(e,r)&&(t[t.length]=r)
|
|
1217
|
+
return t},c(e)},function(e){if(e&&e.__esModule)return e
|
|
1218
|
+
var t={}
|
|
1219
|
+
if(null!=e)for(var r=c(e),s=0;s<r.length;s++)"default"!==r[s]&&l(t,e,r[s])
|
|
1220
|
+
return d(t,e),t})
|
|
1221
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.getCurrentBranch=r,exports.branchExists=s,exports.createBranch=async function(e,t,o="HEAD",n,i,a){const c=i.startTimer("git_create_branch_duration")
|
|
1222
|
+
try{if(await s(e,t,n,i)){const e=`Branch '${t}' already exists`
|
|
1223
|
+
return n.warn(e),i.incrementCounter("git_create_branch_already_exists",1),c({success:!1,reason:"already_exists"}),{success:!1,branchName:t,message:e}}const l=await r(e,n,i)
|
|
1224
|
+
await e.checkoutBranch(t,o)
|
|
1225
|
+
const d=await e.revparse(["HEAD"]),u=a||l
|
|
1226
|
+
return await e.checkout(u),n.info("Created new branch",{branchName:t,fromBranch:o,currentBranch:l,returnedToBranch:u,commitHash:d.substring(0,8)}),i.incrementCounter("git_create_branch_success",1),c({success:!0}),{success:!0,branchName:t,previousBranch:l,commitHash:d,message:`Created branch '${t}' from '${o}'`}}catch(e){const r=(0,m.getErrorMessage)(e)
|
|
1227
|
+
throw n.error("Failed to create branch",{branchName:t,fromBranch:o,error:r}),i.incrementCounter("git_create_branch_failure",1),c({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError(`Failed to create branch '${t}': ${r}`)}},exports.switchToBranch=o,exports.mergeBranch=async function(e,t,n,i,a,c){const l=a.startTimer("git_merge_branch_duration")
|
|
1228
|
+
try{const d=await r(e,i,a)
|
|
1229
|
+
if(d!==n&&await o(e,n,i,a),!await s(e,t,i,a)){const e=`Source branch '${t}' does not exist`
|
|
1230
|
+
throw i.error(e),a.incrementCounter("git_merge_branch_source_not_found",1),l({success:!1,reason:"source_not_found"}),new g.ValidationError(e)}const u=c?["--no-ff","-m",c,t]:["--no-ff",t],h=(await e.merge(u),await e.revparse(["HEAD"]))
|
|
1231
|
+
return i.info("Successfully merged branch",{sourceBranch:t,targetBranch:n,commitHash:h.substring(0,8),mergeMessage:c||"default merge message"}),a.incrementCounter("git_merge_branch_success",1),l({success:!0}),{success:!0,branchName:n,previousBranch:d,commitHash:h,message:`Merged '${t}' into '${n}'`}}catch(e){const r=(0,m.getErrorMessage)(e)
|
|
1232
|
+
throw i.error("Failed to merge branch",{sourceBranch:t,targetBranch:n,error:r}),a.incrementCounter("git_merge_branch_failure",1),l({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError(`Failed to merge '${t}' into '${n}': ${r}`)}},exports.deleteBranch=async function(e,t,o=!1,n,i){const a=i.startTimer("git_delete_branch_duration")
|
|
1233
|
+
try{const c=await r(e,n,i)
|
|
1234
|
+
if(c===t){const e=`Cannot delete current branch '${t}'`
|
|
1235
|
+
throw n.error(e),i.incrementCounter("git_delete_branch_current_branch",1),a({success:!1,reason:"current_branch"}),new g.ValidationError(e)}if("main"===t||"master"===t){const e=`Cannot delete protected branch '${t}'`
|
|
1236
|
+
throw n.error(e),i.incrementCounter("git_delete_branch_protected",1),a({success:!1,reason:"protected_branch"}),new g.ValidationError(e)}if(!await s(e,t,n,i)){const e=`Branch '${t}' does not exist`
|
|
1237
|
+
return n.warn(e),i.incrementCounter("git_delete_branch_not_found",1),a({success:!1,reason:"not_found"}),{success:!1,branchName:t,message:e}}const l=o?["-D",t]:["-d",t]
|
|
1238
|
+
return await e.branch(l),n.info("Deleted branch",{branchName:t,force:o,currentBranch:c}),i.incrementCounter("git_delete_branch_success",1),a({success:!0}),{success:!0,branchName:t,previousBranch:c,message:`Deleted branch '${t}'${o?" (forced)":""}`}}catch(e){const r=(0,m.getErrorMessage)(e)
|
|
1239
|
+
throw n.error("Failed to delete branch",{branchName:t,force:o,error:r}),i.incrementCounter("git_delete_branch_failure",1),a({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError(`Failed to delete branch '${t}': ${r}`)}},exports.getRepositoryStatus=n,exports.validateGitRepository=async function(e,t,r){const s=r.startTimer("git_validate_repository_duration")
|
|
1240
|
+
try{const o=await n(e,t,r)
|
|
1241
|
+
return o.isGitRepository?(o.currentBranch||(t.warn("Git repository in detached HEAD state"),r.incrementCounter("git_validate_repository_detached_head",1)),o.isClean||(t.warn("Git repository has uncommitted changes",{count:o.uncommittedChanges.length,files:o.uncommittedChanges.slice(0,5)}),r.incrementCounter("git_validate_repository_dirty",1)),o.defaultBranch||(t.warn("No default branch found in git repository"),r.incrementCounter("git_validate_repository_no_default_branch",1)),t.info("Git repository validation completed",{currentBranch:o.currentBranch,isClean:o.isClean,defaultBranch:o.defaultBranch,branchCount:o.branches.length}),r.incrementCounter("git_validate_repository_success",1),s({success:!0}),o):(t.warn("Git repository validation failed: not a git repository"),r.incrementCounter("git_validate_repository_not_git",1),s({success:!1,reason:"not_git_repo"}),o)}catch(e){const o=(0,m.getErrorMessage)(e)
|
|
1242
|
+
throw t.error("Git repository validation failed",{error:o}),r.incrementCounter("git_validate_repository_failure",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Git repository validation failed: "+o)}},exports.sanitizeBranchName=function(e){return e.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").substring(0,50)},exports.generateRequestBranchName=i,exports.generateMergeMessagePatterns=function(e){const t=i(e)
|
|
1243
|
+
return[`Merge ${t}-`,"Merge "+t,`Merge ${t}-.*\\(resolved conflicts\\)`,`Merge ${t} \\(resolved conflicts\\)`]},exports.parseRequestIdFromBranchName=function(e){const t=e.match(/^feature\/(req\d{6})-/)
|
|
1244
|
+
return t?t[1]:null},exports.switchToDefaultBranch=async function(e,t,r){const s=r.startTimer("git_switch_to_default_duration")
|
|
1245
|
+
try{const i=await n(e,t,r)
|
|
1246
|
+
if(!i.defaultBranch){const e="No default branch found"
|
|
1247
|
+
return t.warn(e),r.incrementCounter("git_switch_to_default_no_branch",1),s({success:!1,reason:"no_default_branch"}),{success:!1,branchName:"",message:e}}const a=await o(e,i.defaultBranch,t,r)
|
|
1248
|
+
return a.success?(t.info("Switched to default branch",{defaultBranch:i.defaultBranch,previousBranch:a.previousBranch}),r.incrementCounter("git_switch_to_default_success",1),s({success:!0})):(r.incrementCounter("git_switch_to_default_failed",1),s({success:!1,reason:"switch_failed"})),a}catch(e){const o=(0,m.getErrorMessage)(e)
|
|
1249
|
+
throw t.error("Failed to switch to default branch",{error:o}),r.incrementCounter("git_switch_to_default_error",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Failed to switch to default branch: "+o)}},exports.ensureRepositoryReady=async function(e,t,r){const s=r.startTimer("git_ensure_repository_ready_duration")
|
|
1250
|
+
try{const o=await n(e,t,r),i=[]
|
|
1251
|
+
o.isGitRepository||i.push("Not a git repository"),o.currentBranch||i.push("Repository is in detached HEAD state"),o.defaultBranch||i.push("No default branch (main/master) found")
|
|
1252
|
+
const a=0===i.length
|
|
1253
|
+
return t.debug("Repository readiness check completed",{ready:a,issueCount:i.length,issues:i}),r.incrementCounter("git_ensure_repository_ready_check",1,{ready:a.toString()}),s({success:!0,ready:a.toString()}),{ready:a,status:o,issues:i}}catch(e){const o=(0,m.getErrorMessage)(e)
|
|
1254
|
+
return t.error("Failed to check repository readiness",{error:o}),r.incrementCounter("git_ensure_repository_ready_failure",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),{ready:!1,status:{isGitRepository:!1,currentBranch:"",isClean:!1,hasMainBranch:!1,hasMasterBranch:!1,defaultBranch:"",uncommittedChanges:[],branches:[]},issues:["Repository check failed: "+o]}}},exports.hasMainChangedSinceRevert=async function(e,t,r,s){const o=s.startTimer("git_check_main_changed_since_revert")
|
|
1255
|
+
try{r.debug("Starting main branch change detection",{providedRevertCommitHash:t.substring(0,8),fullRevertCommitHash:t})
|
|
1256
|
+
const s=await e.revparse(["HEAD"])
|
|
1257
|
+
r.debug("Git state captured",{currentMainHash:s.substring(0,8),fullCurrentMainHash:s,revertCommitHash:t.substring(0,8),fullRevertCommitHash:t,hashesMatch:s===t})
|
|
1258
|
+
try{const t=await e.revparse(["--abbrev-ref","HEAD"]),s=await e.status(),o=await e.log(["--oneline","-5"])
|
|
1259
|
+
r.debug("Additional git context",{currentBranch:t,gitStatus:{ahead:s.ahead,behind:s.behind,current:s.current,tracking:s.tracking,staged:s.staged.length,modified:s.modified.length},recentCommits:o.all.map((e=>({hash:e.hash.substring(0,8),message:e.message})))})}catch(e){r.warn("Failed to gather additional git context",{error:e instanceof Error?e.message:e+""})}const n=await(0,p.validateCommitHash)(e,t,r,"hasMainChangedSinceRevert-revert-validation")
|
|
1260
|
+
if(!n.valid)throw r.error("Revert commit validation failed",{revertCommitHash:t.substring(0,8),error:n.error}),Error(`Revert commit ${t.substring(0,8)} not found or not reachable: ${n.error}`)
|
|
1261
|
+
const i=await(0,p.validateCommitHash)(e,s,r,"hasMainChangedSinceRevert-head-validation")
|
|
1262
|
+
if(!i.valid)throw r.error("Current HEAD validation failed",{currentMainHash:s.substring(0,8),error:i.error}),Error(`Current HEAD ${s.substring(0,8)} validation failed: ${i.error}`)
|
|
1263
|
+
if(s===t)return r.info("Main branch unchanged since revert - exact hash match",{currentMainHash:s.substring(0,8),revertCommitHash:t.substring(0,8),fullHashMatch:!0}),o({success:!0,changed:"false"}),{hasChanged:!1,currentMainHash:s,revertCommitHash:t,commitsSinceRevert:0,message:"Main branch has not changed since revert"}
|
|
1264
|
+
r.debug("Hash comparison shows difference, checking ancestor relationship",{currentMainHash:s.substring(0,8),revertCommitHash:t.substring(0,8),hashesEqual:!1})
|
|
1265
|
+
let a=!1,c=0,l=null
|
|
1266
|
+
for(let s=1;3>=s;s++)try{r.debug("Checking ancestor relationship with merge-base",{revertCommitHash:t.substring(0,8),currentHead:"HEAD",attempt:s}),s>1&&(await new Promise((e=>setTimeout(e,100))),r.debug("Retry attempt after delay",{attempt:s,delay:100})),await e.raw(["merge-base","--is-ancestor",t,"HEAD"]),r.debug("Revert commit is ancestor of HEAD, counting commits between them",{attempt:s})
|
|
1267
|
+
const o=await e.raw(["rev-list","--count",t+"..HEAD"])
|
|
1268
|
+
c=parseInt(o.trim(),10),a=!0
|
|
1269
|
+
break}catch(e){l=e,r.debug("Ancestor check attempt failed",{attempt:s,error:(0,m.getErrorMessage)(e)}),3===s&&r.debug("All ancestor check attempts failed, treating as non-ancestor")}if(a){try{const s=await e.raw(["rev-list","--oneline",t+"..HEAD"])
|
|
1270
|
+
r.debug("Commits found between revert and HEAD",{count:c,commits:s.trim().split("\n").filter((e=>e.length>0))})}catch(e){r.warn("Could not list commits between revert and HEAD",{error:e instanceof Error?e.message:e+""})}return r.info("Main branch has changed since revert - ancestor relationship confirmed",{currentMainHash:s.substring(0,8),revertCommitHash:t.substring(0,8),commitsSinceRevert:c,isAncestor:!0}),o({success:!0,changed:"true"}),{hasChanged:!0,currentMainHash:s,revertCommitHash:t,commitsSinceRevert:c,message:`Main branch has ${c} new commit${c>1?"s":""} since revert`}}r.warn("Revert commit is not an ancestor of current HEAD after retries - investigating relationship",{currentMainHash:s.substring(0,8),revertCommitHash:t.substring(0,8),lastAncestorError:l instanceof Error?l.message:l+"",attempts:3})
|
|
1271
|
+
try{const o=await e.raw(["merge-base",t,"HEAD"])
|
|
1272
|
+
r.debug("Merge base found between revert commit and HEAD",{mergeBase:o.trim().substring(0,8),revertCommitHash:t.substring(0,8),currentMainHash:s.substring(0,8)})}catch(e){r.warn("Could not find merge base",{error:e instanceof Error?e.message:e+""})}try{await e.raw(["merge-base","--is-ancestor","HEAD",t]),r.error("CRITICAL: Current HEAD is ancestor of revert commit - possible git state corruption or incorrect revert hash",{currentMainHash:s.substring(0,8),revertCommitHash:t.substring(0,8),implication:"This suggests the stored revert commit hash may be incorrect or from a different branch state"})}catch(e){r.debug("HEAD is not ancestor of revert commit either - commits may be on divergent paths",{error:e instanceof Error?e.message:e+""})}return o({success:!0,changed:"unknown"}),{hasChanged:!0,currentMainHash:s,revertCommitHash:t,commitsSinceRevert:-1,message:"Cannot determine relationship between revert commit and current HEAD - assuming changed for safety. This may indicate a stored revert commit hash issue."}}catch(e){const s=(0,m.getErrorMessage)(e)
|
|
1273
|
+
throw r.error("Failed to check if main changed since revert",{revertCommitHash:t.substring(0,8),error:s}),o({success:!1,error:e instanceof Error?e.name:"unknown"}),Error("Failed to check main branch changes: "+s)}},exports.findRevertCommitHash=async function(e,t,r){try{r.debug("Searching for revert commit hash in tool records",{requestId:e})
|
|
1274
|
+
const s=await t.getToolRecordsByRequestId(e,"revert-request")
|
|
1275
|
+
if(s.sort(((e,t)=>new Date(t.createdAt).getTime()-new Date(e.createdAt).getTime())),!s||0===s.length)return r.debug("No revert tool records found for request",{requestId:e}),{found:!1,message:"No revert operations found for this request"}
|
|
1276
|
+
r.debug("Searching through tool records for revert commit hash",{requestId:e,totalRecords:s.length,recordTypes:s.map((e=>e.type)),recordStatuses:s.map((e=>e.metadata?.status))})
|
|
1277
|
+
for(const t of s)if(r.debug("Examining tool record",{requestId:e,recordId:t.id,recordType:t.type,status:t.metadata?.status,hasRevertCommitHash:!!t.metadata?.revertCommitHash,revertCommitHash:t.metadata?.revertCommitHash?.substring(0,8),fullRevertCommitHash:t.metadata?.revertCommitHash,createdAt:t.createdAt}),"completed"===t.metadata?.status&&t.metadata?.revertCommitHash){const s="simple",o=t.metadata.revertCommitHash
|
|
1278
|
+
r.debug("Found potential revert commit hash, validating...",{requestId:e,revertCommitHash:o.substring(0,8),fullRevertCommitHash:o,revertType:s,recordId:t.id,revertedAt:t.createdAt})
|
|
1279
|
+
try{if(!/^[a-f0-9]{40}$/i.test(o)){r.warn("Invalid commit hash format found in tool record",{requestId:e,invalidHash:o,recordId:t.id,format:"Expected 40-character hex string"})
|
|
1280
|
+
continue}return r.info("Found valid revert commit hash from tool records",{requestId:e,revertCommitHash:o.substring(0,8),fullRevertCommitHash:o,revertType:s,recordId:t.id,revertedAt:t.createdAt,hashFormat:"valid"}),{found:!0,revertCommitHash:o,revertType:s,revertedAt:t.createdAt,message:`Found ${s} revert commit from ${t.createdAt}`}}catch(s){r.warn("Revert commit hash validation failed, trying next record",{requestId:e,revertCommitHash:o.substring(0,8),recordId:t.id,error:s instanceof Error?s.message:s+""})
|
|
1281
|
+
continue}}return r.warn("Found revert tool records but none have completed status with commit hash",{requestId:e,recordCount:s.length,records:s.map((e=>({type:e.type,status:e.metadata?.status,hasCommitHash:!!e.metadata?.revertCommitHash})))}),{found:!1,message:"Found revert records but none were completed successfully"}}catch(t){const s=(0,m.getErrorMessage)(t)
|
|
1282
|
+
return r.error("Failed to find revert commit hash from tool records",{requestId:e,error:s}),{found:!1,message:"Failed to search tool records: "+s}}},exports.checkUncommittedChanges=a,exports.commitUserChanges=async function(e,t,r,s,o){const n=o.startTimer("git_commit_user_changes_duration")
|
|
1283
|
+
try{if(!r.hasChanges)throw new g.ValidationError("No uncommitted changes to commit")
|
|
1284
|
+
s.debug("Starting user changes commit process",{staged:r.stagedFiles.length,unstaged:r.unstagedFiles.length,untracked:r.untrackedFiles.length,total:r.totalFiles}),s.info("Committing user changes before merge",{requestId:t,staged:r.stagedFiles.length,unstaged:r.unstagedFiles.length,untracked:r.untrackedFiles.length,total:r.totalFiles})
|
|
1285
|
+
const i=[...r.unstagedFiles,...r.untrackedFiles]
|
|
1286
|
+
i.length>0?(s.debug("Staging files",{files:i.slice(0,10),total:i.length}),await e.add(i),s.debug("Staged uncommitted files",{files:i.slice(0,10),total:i.length}),s.debug("Successfully staged files")):s.debug("No additional files to stage (already staged)")
|
|
1287
|
+
const a=`chore: commit user changes before merging request ${t}\n\n${r.summary}\n\nThis commit preserves user modifications made during the request lifecycle\nbefore proceeding with the branch merge operation.`
|
|
1288
|
+
s.debug("Commit message",{message:a.split("\n")[0]}),s.debug("Creating commit"),await e.commit(a)
|
|
1289
|
+
const c=await e.revparse(["HEAD"])
|
|
1290
|
+
return s.debug("Commit created successfully",{commitHash:c.substring(0,8),filesCommitted:r.totalFiles}),s.info("Successfully committed user changes",{requestId:t,commitHash:c.substring(0,8),filesCommitted:r.totalFiles,message:a.split("\n")[0]}),o.incrementCounter("git_commit_user_changes_success",1),o.recordValue("git_commit_user_changes_files",r.totalFiles),n({success:!0}),{commitHash:c,message:a,filesCommitted:r.totalFiles}}catch(e){const i=(0,m.getErrorMessage)(e)
|
|
1291
|
+
throw s.error("Failed to commit user changes",{requestId:t,error:i,uncommittedFiles:r.totalFiles}),o.incrementCounter("git_commit_user_changes_failure",1),n({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Failed to commit user changes: "+i)}},exports.commitUserChangesBeforeToolOperation=async function(e,r,s,o,n){const i=n.startTimer("git_pre_tool_user_changes_duration")
|
|
1292
|
+
try{let c=Date.now()
|
|
1293
|
+
o.debug("Checking for user changes before tool operation",{toolName:r})
|
|
1294
|
+
let l=e
|
|
1295
|
+
if(s.workspaceManager){const e=s.workspaceManager.getCurrentContext(s.currentSessionId)
|
|
1296
|
+
if("multi-agent"===e.mode&&e.requestId){const{simpleGit}=t(3807)
|
|
1297
|
+
l=simpleGit(e.basePath),o.debug("Using worktree git instance for user changes",{requestId:e.requestId,basePath:e.basePath,toolName:r})}}if(Date.now(),s.workspaceManager?.consumeWorkspaceKnownClean(s.currentSessionId))return o.debug("Skipping uncommitted changes check - workspace known to be clean",{toolName:r,requestId:s.requestId}),i({success:!0,reason:"known_clean"}),{userChangesCommitted:!1}
|
|
1298
|
+
c=Date.now()
|
|
1299
|
+
try{(await l.revparse(["--show-toplevel"])).trim()}catch(e){}const d=await a(l,o,n)
|
|
1300
|
+
if(Date.now(),!d.hasChanges)return o.debug("No user changes detected, proceeding with tool",{toolName:r}),o.debug("No user changes detected before tool operation",{toolName:r,requestId:s.requestId,taskId:s.taskId}),i({success:!0,reason:"no_changes"}),{userChangesCommitted:!1}
|
|
1301
|
+
let u
|
|
1302
|
+
o.debug("Found user changes before tool, committing them first",{toolName:r}),o.info("Found user changes before tool operation, committing separately",{toolName:r,requestId:s.requestId,taskId:s.taskId,totalFiles:d.totalFiles,summary:d.summary}),u=s.requestId&&s.taskId?`chore: commit user changes before ${r} (task ${s.taskId})`:s.requestId?`chore: commit user changes before ${r} (request ${s.requestId})`:`chore: commit user changes before ${r} operation`
|
|
1303
|
+
const h=`${u}\n\n${{...d,summary:`User changes detected before ${r}: ${d.summary.replace("Found ","").toLowerCase()}`}.summary}\n\nThis commit preserves user modifications made before the ${r} tool operation\nto maintain clear separation between user changes and tool changes.`
|
|
1304
|
+
o.debug("Committing user changes with message",{firstLine:h.split("\n")[0]})
|
|
1305
|
+
const g=[...d.unstagedFiles,...d.untrackedFiles]
|
|
1306
|
+
c=Date.now(),g.length>0&&(o.debug("Staging files before tool operation",{files:g.slice(0,10),total:g.length}),await l.add(g)),Date.now(),c=Date.now(),await l.commit(h),Date.now()
|
|
1307
|
+
const p=await l.revparse(["HEAD"])
|
|
1308
|
+
return o.debug("User changes committed successfully before tool",{commitHash:p.substring(0,8),filesCommitted:d.totalFiles,toolName:r}),o.info("Successfully committed user changes before tool operation",{toolName:r,requestId:s.requestId,taskId:s.taskId,commitHash:p.substring(0,8),filesCommitted:d.totalFiles}),n.incrementCounter("git_pre_tool_user_changes_committed",1),n.recordValue("git_pre_tool_user_changes_files",d.totalFiles),i({success:!0,reason:"changes_committed"}),{userChangesCommitted:!0,commitHash:p,filesCommitted:d.totalFiles}}catch(e){const t=(0,m.getErrorMessage)(e)
|
|
1309
|
+
return o.debug("Failed to commit user changes before tool",{toolName:r,error:t}),o.warn("Failed to commit user changes before tool operation",{toolName:r,requestId:s.requestId,taskId:s.taskId,error:t}),n.incrementCounter("git_pre_tool_user_changes_failed",1),i({success:!1,error:e instanceof Error?e.name:"unknown"}),{userChangesCommitted:!1}}},exports.findLargeUntrackedFiles=async function(e,r=104857600){const s=[]
|
|
1310
|
+
try{const o=t(3807).simpleGit(e),n=(await o.status()).not_added||[]
|
|
1311
|
+
for(const t of n)try{const o=y.join(e,t),n=f.statSync(o)
|
|
1312
|
+
n.isFile()&&n.size>=r&&s.push({path:t,sizeBytes:n.size,sizeMB:Math.round(n.size/1048576*10)/10})}catch{}}catch{}return s},exports.formatLargeFileWarning=function(e){if(0===e.length)return""
|
|
1313
|
+
const t=e.reduce(((e,t)=>e+t.sizeMB),0)
|
|
1314
|
+
let r=`\n\n\u26a0\ufe0f **Performance Warning**: Large untracked files detected (${Math.round(t)} MB total) that may slow git operations:\n`
|
|
1315
|
+
for(const t of e.slice(0,5))r+=`- \`${t.path}\` (${t.sizeMB} MB)\n`
|
|
1316
|
+
return e.length>5&&(r+=`- ... and ${e.length-5} more large files\n`),r+="\nConsider adding these to `.gitignore` or moving them outside the repository.",r}
|
|
1317
|
+
const h=t(3444),g=t(9860),p=t(3922),m=t(8824),f=u(t(9896)),y=u(t(6928))
|
|
1318
|
+
t(857)},7767(e,exports,t){"use strict"
|
|
988
1319
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleApproveAndWorkRequestAction=async function(e,t){const{requestId:o}=e,{logger:n,apiClient:i,projectId:a,projectPath:c,trackChildPid:l}=t
|
|
989
1320
|
if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
990
1321
|
n.info("Starting approve-and-work-request action",{requestId:o})
|
|
@@ -994,327 +1325,387 @@ const d=e.data
|
|
|
994
1325
|
if("pending_approval"!==d.status)return{success:!1,message:`Cannot approve request with status '${d.status}'. Only requests in 'pending_approval' status can be approved.`,error:"Invalid status: "+d.status}
|
|
995
1326
|
if(!(await i.patch(`/api/projects/${a}/requests/${o}`,{status:"open"})).success)return{success:!1,message:"Failed to update request status",error:"Update failed"}
|
|
996
1327
|
const u=Array.isArray(d.acceptanceCriteria)?d.acceptanceCriteria.map((e=>e.description||e)).join("\n- "):"",h=`Work on request ${o}: ${d.title}\n\nDescription: ${d.description}\n\n${u?"Acceptance Criteria:\n- "+u:""}\n\nThis request has just been approved. Please begin implementation.`
|
|
997
|
-
let g,p=!1,
|
|
998
|
-
if(
|
|
999
|
-
e.success&&e.data?.parent?.externalSessionId&&(g=e.data.parent.externalSessionId,
|
|
1328
|
+
let g,p=!1,m=!1,f=d.cliOrigin?.sessionId||null
|
|
1329
|
+
if(f)try{const e=await i.get(`/api/projects/${a}/cli-sessions/resolve/${f}`)
|
|
1330
|
+
e.success&&e.data?.parent?.externalSessionId&&(g=e.data.parent.externalSessionId,f=g,n.info("Resolved parent session for notification",{cliOriginSession:d.cliOrigin.sessionId,parentSession:f}))}catch(e){n.warn("Failed to resolve session parent, using cliOrigin directly",{error:(0,r.getErrorMessage)(e)})}if(!f)try{const e=await i.get(`/api/projects/${a}/session-request-associations/by-request/${o}?activeOnly=true`)
|
|
1000
1331
|
if(e.success&&Array.isArray(e.data)&&e.data.length>0){const t=e.data[0].opencodeSessionId
|
|
1001
1332
|
try{const e=await i.get(`/api/projects/${a}/cli-sessions/resolve/${t}`)
|
|
1002
|
-
e.data?.parent?.externalSessionId?(
|
|
1003
|
-
try{const e=(0,
|
|
1004
|
-
if(e){const
|
|
1005
|
-
return{model:{providerID:e,modelID:
|
|
1006
|
-
i.ok?n.info("Pushed notification to CLI session",{sessionId:
|
|
1007
|
-
|
|
1333
|
+
e.data?.parent?.externalSessionId?(f=e.data.parent.externalSessionId,g=f):f=t}catch{f=t}}}catch{}if(f)try{await i.post(`/api/projects/${a}/session-messages/reports`,{fromSessionId:"gui-approval",toSessionId:f,status:"success",summary:`Request ${o} "${d.title}" has been approved. Please begin implementation.`,details:h}),p=!0,n.info("Stored session report for target session",{requestId:o,sessionId:f})
|
|
1334
|
+
try{const e=(0,s.getOpenCodeServerUrl)(c,n)
|
|
1335
|
+
if(e){const r=`\u2705 [Request Approved]\n\nRequest ${o} "${d.title}" has been approved.\nPlease begin implementation.\n\nDetails:\n${h}`,s=`${e}/session/${f}/prompt_async`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:r}],...t.cachedDefaultModel?(()=>{const[e,...r]=t.cachedDefaultModel.split("/")
|
|
1336
|
+
return{model:{providerID:e,modelID:r.join("/")}}})():{}})})
|
|
1337
|
+
i.ok?n.info("Pushed notification to CLI session",{sessionId:f}):n.warn("Failed to push notification to CLI session",{status:i.status})}}catch(e){n.warn("Could not push notification to CLI session (may be offline)",{error:(0,r.getErrorMessage)(e)})}}catch(e){n.warn("Failed to notify target session, spawning work agent instead",{requestId:o,error:(0,r.getErrorMessage)(e)})}if(!p)try{const e=await(0,s.spawnActionAgent)({agent:"work",prompt:h,cwd:c,projectPath:c,projectId:a,title:`[${o}] ${d.title}`,requestId:o,parentSessionId:g,apiClient:i,logger:n,trackChildPid:l})
|
|
1338
|
+
m=e.success,m?n.info("Spawned work agent for request",{requestId:o,sessionId:e.sessionId}):n.warn("Failed to spawn work agent",{requestId:o,error:e.error})}catch(e){n.warn("Failed to spawn work agent",{requestId:o,error:(0,r.getErrorMessage)(e)})}return n.info("Approve-and-work-request action completed",{requestId:o,notifiedSession:p,agentSpawned:m}),{success:!0,message:`Successfully approved request ${o}: ${d.title}${p?" \u2014 creating session notified":m?" \u2014 work agent spawned":""}`,data:{operationType:"approve-and-work",previousStatus:"pending_approval",newStatus:"open",notifiedSession:p,agentSpawned:m}}}catch(e){const t=(0,r.getErrorMessage)(e)
|
|
1008
1339
|
return n.error("approve-and-work-request action failed",{requestId:o,error:t}),{success:!1,message:"Failed to approve request: "+t,error:t}}}
|
|
1009
|
-
const
|
|
1340
|
+
const r=t(8824),s=t(8148)},7904(e,exports){"use strict"
|
|
1341
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.REPORT_STATUS_EMOJI=void 0,exports.formatReportNotification=function(e){const{messageCode:t,status:r,summary:s,details:o,fromSessionId:n,senderAgent:i}=e,a=[`${exports.REPORT_STATUS_EMOJI[r]||"\u{1f4cb}"} **Report \u2014 ${r}**`]
|
|
1342
|
+
return t&&a.push("**Report Code:** "+t),i&&a.push(n?`from: ${i} (${n})`:"from: "+i),a.push("",s||"Task completed"),o&&a.push("",o),t&&a.push("",`Use \`session_message_operations\` with \`reply\` operation and message code \`${t}\` to acknowledge.`),a.join("\n")},exports.REPORT_STATUS_EMOJI={success:"\u2705",partial:"\u26a0\ufe0f",failure:"\u274c",blocked:"\u{1f6ab}"}},7960(e,exports){"use strict"
|
|
1010
1343
|
function t(){const e=process.argv[1]||""
|
|
1011
1344
|
if(e.includes("/dist-dev/")||e.includes("\\dist-dev\\"))return!0
|
|
1012
1345
|
if(e.includes("/dist/")||e.includes("\\dist\\"))return!1
|
|
1013
1346
|
const t=process.cwd()
|
|
1014
1347
|
return!(!t.includes("/dist-dev")&&!t.includes("\\dist-dev"))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.isDevBuild=t,exports.getBuildMode=function(){return t()?"development":"production"},exports.enableDebugMode=function(){return"true"===process.env.ENABLE_DEBUG_TOOLS&&(process.env.DEBUG_LOGGING="true",!0)}},8035(e,exports,t){"use strict"
|
|
1015
|
-
function
|
|
1016
|
-
try{const o={...process.env
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
const I=(0,
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1348
|
+
function r(e,t,r){try{const s=`[${(new Date).toISOString()}] [detached-subagent] ${t}${r?" "+JSON.stringify(r):""}\n`,o=(0,i.join)(e||process.cwd(),".alchemy_lab","spawn-debug.log");(0,n.appendFileSync)(o,s)}catch{}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.spawnDetachedSubagent=function(e){const{agent:t,prompt:d,cwd:u,sessionId:h,model:g,initialBranch:p,serverUrl:m,onSpawn:f}=e
|
|
1349
|
+
try{const e=u||process.cwd(),o={...process.env,...(0,c.getOpencodeSpawnEnv)(e,(0,l.isDevMode)())}
|
|
1350
|
+
let y
|
|
1351
|
+
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))
|
|
1352
|
+
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})
|
|
1353
|
+
const v=(0,i.join)(u||process.cwd(),".alchemy_lab"),w=(0,i.join)(v,"spawn-stderr.log")
|
|
1354
|
+
let b
|
|
1355
|
+
try{b=(0,n.openSync)(w,"a")}catch{}const I=(0,a.resolveRunCommand)(),E=(0,s.spawn)(I.command,[...I.prependArgs,...y],{detached:!0,stdio:["ignore","ignore",b??"ignore"],cwd:u||process.cwd(),env:o})
|
|
1356
|
+
return E.unref(),f&&f(E.pid),{success:!0,pid:E.pid}}catch(e){return{success:!1,error:(0,o.getErrorMessage)(e)}}}
|
|
1357
|
+
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"
|
|
1358
|
+
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
|
|
1359
|
+
let f,y,v,w=null
|
|
1360
|
+
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})
|
|
1361
|
+
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 b=(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)}})
|
|
1362
|
+
return b.success?(p.info("Spawned action agent",{agent:t,sessionId:w,pid:b.pid,requestId:d}),{success:!0,sessionId:w,pid:b.pid}):{success:!1,sessionId:w,error:b.error||"Spawn failed"}}
|
|
1363
|
+
const s=t(8035),o=t(8824),n=t(833),i=t(1292)},8463(e,exports,t){"use strict"
|
|
1364
|
+
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)
|
|
1365
|
+
if(!(0,s.existsSync)(i))return
|
|
1366
|
+
const c=(0,n.simpleGit)(e)
|
|
1367
|
+
let l=[]
|
|
1368
|
+
try{l=(0,s.readdirSync)(i)}catch(e){return void t.warn("Setup-worktrees sweep: cannot read base dir",{error:(0,a.getErrorMessage)(e)})}for(const e of l){const r=(0,o.join)(i,e)
|
|
1369
|
+
try{await c.raw(["worktree","remove","--force",r])}catch{}try{(0,s.rmSync)(r,{recursive:!0,force:!0})}catch(e){t.debug("Setup-worktrees sweep: rm failed (best-effort)",{worktreePath:r,error:(0,a.getErrorMessage)(e)})}}try{await c.raw(["worktree","prune"])}catch(e){t.debug("Setup-worktrees sweep: prune failed (best-effort)",{error:(0,a.getErrorMessage)(e)})}},exports.generateSetupId=function(){return(0,i.randomBytes)(6).toString("hex")},exports.resolveSetupBranch=async function(e,t,r,s,o,i){if(r)return r
|
|
1370
|
+
if(s)try{const r=await e.get(`/api/projects/${t}/requests/${s}`)
|
|
1371
|
+
if(r?.success&&r?.data?.branchName)return r.data.branchName
|
|
1372
|
+
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()
|
|
1373
|
+
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})
|
|
1374
|
+
const a=(0,n.simpleGit)(e)
|
|
1375
|
+
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]
|
|
1376
|
+
return t&&r.push("Triggered from request: "+t),r.push(""),r.push("1. Inspect manifests (package.json, pyproject.toml, Cargo.toml, go.mod, Makefile, CI configs) to identify build/test/lint commands."),r.push("2. Use `testing_reference` for per-language patterns."),r.push("3. Propose a draft to the user (name, setup commands, test commands with timeouts) as a single message."),r.push("4. Iterate until the user confirms."),r.push("5. Call `testing_process_create` with the agreed configuration."),r.push("6. Send a brief report back to your parent via `session_message_operations` (type: report) summarizing what you registered, then end your session."),r.push(""),r.push("You are read-only with respect to project files. Do not modify source code or run tests."),r.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]
|
|
1377
|
+
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")}
|
|
1378
|
+
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"
|
|
1379
|
+
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
1035
1380
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.CLIBridgeWebSocket=void 0,exports.setCLIBridge=function(e){n=e},exports.getCLIBridge=function(){return n}
|
|
1036
|
-
const
|
|
1037
|
-
exports.CLIBridgeWebSocket=class{constructor(e,t=3e4){this.wss=null,this.uiClient=null,this.pendingCommands=new Map,this.heartbeatInterval=null,this.logger=e,this.defaultTimeout=t}attach(e){this.wss=new
|
|
1381
|
+
const s=t(5086),o=r(t(6982))
|
|
1382
|
+
exports.CLIBridgeWebSocket=class{constructor(e,t=3e4){this.wss=null,this.uiClient=null,this.pendingCommands=new Map,this.heartbeatInterval=null,this.logger=e,this.defaultTimeout=t}attach(e){this.wss=new s.WebSocketServer({server:e,path:"/ws/cli-bridge"}),this.wss.on("connection",(e=>{this.logger.info("[CLIBridge] UI client connected"),this.uiClient&&(this.logger.warn("[CLIBridge] Replacing existing UI client connection"),this.uiClient.close(1e3,"Replaced by new connection")),this.uiClient=e,this.startHeartbeat(),e.on("message",(e=>{try{const t=JSON.parse(e.toString())
|
|
1038
1383
|
this.handleResponse(t)}catch(e){this.logger.error("[CLIBridge] Failed to parse message from UI",{error:e instanceof Error?e.message:e+""})}})),e.on("close",(()=>{this.logger.info("[CLIBridge] UI client disconnected"),this.uiClient===e&&(this.uiClient=null),this.stopHeartbeat()
|
|
1039
|
-
for(const[e,t]of this.pendingCommands)clearTimeout(t.timer),t.resolve({id:e,success:!1,error:"UI client disconnected during command. The command may have been partially executed."}),this.pendingCommands.delete(e)})),e.on("error",(e=>{this.logger.error("[CLIBridge] WebSocket error",{error:e.message})}))})),this.logger.info("[CLIBridge] WebSocket server attached at /ws/cli-bridge")}isUIConnected(){return null!==this.uiClient&&this.uiClient.readyState===
|
|
1040
|
-
const
|
|
1041
|
-
return new Promise(((t,o)=>{const i=setTimeout((()=>{this.pendingCommands.delete(
|
|
1042
|
-
this.pendingCommands.set(
|
|
1043
|
-
try{this.uiClient.send(JSON.stringify(n))}catch(e){clearTimeout(i),this.pendingCommands.delete(
|
|
1044
|
-
t?(clearTimeout(t.timer),this.pendingCommands.delete(e.id),t.resolve(e)):this.logger.warn("[CLIBridge] Received response for unknown command",{id:e.id})}startHeartbeat(){this.stopHeartbeat(),this.heartbeatInterval=setInterval((()=>{this.uiClient&&this.uiClient.readyState===
|
|
1384
|
+
for(const[e,t]of this.pendingCommands)clearTimeout(t.timer),t.resolve({id:e,success:!1,error:"UI client disconnected during command. The command may have been partially executed."}),this.pendingCommands.delete(e)})),e.on("error",(e=>{this.logger.error("[CLIBridge] WebSocket error",{error:e.message})}))})),this.logger.info("[CLIBridge] WebSocket server attached at /ws/cli-bridge")}isUIConnected(){return null!==this.uiClient&&this.uiClient.readyState===s.WebSocket.OPEN}async sendCommand(e,t={},r){if(!this.isUIConnected())return{id:"",success:!1,error:"alchemy-ui is not connected. Please open alchemy-ui in your browser first."}
|
|
1385
|
+
const s=o.default.randomUUID(),n={id:s,action:e,params:t}
|
|
1386
|
+
return new Promise(((t,o)=>{const i=setTimeout((()=>{this.pendingCommands.delete(s),t({id:s,success:!1,error:`Command '${e}' timed out after ${(r||this.defaultTimeout)/1e3}s`})}),r||this.defaultTimeout)
|
|
1387
|
+
this.pendingCommands.set(s,{resolve:t,reject:o,timer:i})
|
|
1388
|
+
try{this.uiClient.send(JSON.stringify(n))}catch(e){clearTimeout(i),this.pendingCommands.delete(s),t({id:s,success:!1,error:"Failed to send command: "+(e instanceof Error?e.message:e+"")})}}))}handleResponse(e){const t=this.pendingCommands.get(e.id)
|
|
1389
|
+
t?(clearTimeout(t.timer),this.pendingCommands.delete(e.id),t.resolve(e)):this.logger.warn("[CLIBridge] Received response for unknown command",{id:e.id})}startHeartbeat(){this.stopHeartbeat(),this.heartbeatInterval=setInterval((()=>{this.uiClient&&this.uiClient.readyState===s.WebSocket.OPEN&&this.uiClient.ping()}),3e4)}stopHeartbeat(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null)}close(){this.stopHeartbeat()
|
|
1045
1390
|
for(const[e,t]of this.pendingCommands)clearTimeout(t.timer),t.reject(Error("Bridge shutting down")),this.pendingCommands.delete(e)
|
|
1046
1391
|
this.uiClient&&(this.uiClient.close(1e3,"Server shutting down"),this.uiClient=null),this.wss&&(this.wss.close(),this.wss=null)}}
|
|
1047
1392
|
let n=null},8577(module){"use strict"
|
|
1048
|
-
module.exports=require("cors")},
|
|
1049
|
-
|
|
1050
|
-
if(
|
|
1051
|
-
|
|
1052
|
-
return
|
|
1053
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.
|
|
1054
|
-
|
|
1393
|
+
module.exports=require("cors")},8625(e,exports,t){"use strict"
|
|
1394
|
+
function r(e,t,r){const s=exports.ERROR_TYPE_PROPERTIES[e]||exports.ERROR_TYPE_PROPERTIES[i.INTERNAL_ERROR]
|
|
1395
|
+
return{success:!1,error:{code:e,message:t||s.defaultMessage,details:r,retryable:s.retryable,statusCode:s.statusCode}}}function s(e){return"object"==typeof e&&null!==e&&"code"in e&&"statusCode"in e&&"details"in e&&"retryable"in e&&"string"==typeof e.code&&"number"==typeof e.statusCode}function o(e){if(s(e)){const t=r(e.code,e.message,e.details)
|
|
1396
|
+
return{statusCode:e.statusCode,body:t}}if(e instanceof Error&&"ConnectionError"===e.name){const t=e
|
|
1397
|
+
return{statusCode:503,body:r("CONNECTION_ERROR",t.message||"Connection to remote server failed",{operation:t.operation||"unknown",lastConnectionAttempt:t.lastConnectionAttempt||null,suggestions:["Check your network connection","Verify that the remote server is running","Try again in a few moments"],isConnectionIssue:!0})}}const t=e instanceof Error?e:Error(e+""),o=r(i.INTERNAL_ERROR,t.message||"An unexpected error occurred")
|
|
1398
|
+
return{statusCode:o.error.statusCode,body:o}}function n(e){return!s(e)||e.isRetryable()}Object.defineProperty(exports,"__esModule",{value:!0}),exports.UnauthorizedError=exports.McpProtocolError=exports.FileOperationError=exports.ServiceError=exports.DatabaseError=exports.AuthorizationError=exports.AuthenticationError=exports.ValidationError=exports.NotFoundError=exports.AppError=exports.ErrorUtils=exports.ERROR_TYPE_PROPERTIES=void 0,exports.createErrorResponse=r,exports.createAppError=function(e,t="INTERNAL_ERROR",r=500,s){return new a(e,t,r,s)},exports.createNotFoundError=function(e="Resource not found",t){return new c(e,t)},exports.createValidationError=function(e="Invalid input",t){return new l(e,t)},exports.createAuthenticationError=function(e="Authentication required",t){return new d(e,t)},exports.createAuthorizationError=function(e="Not authorized",t){return new u(e,t)},exports.createDatabaseError=function(e="Database error",t){return new h(e,t)},exports.createServiceError=function(e="Service unavailable",t){return new g(e,t)},exports.createFileOperationError=function(e="File operation error",t){return new p(e,t)},exports.createMcpProtocolError=function(e="MCP protocol error",t){return new m(e,t)},exports.isAppError=s,exports.errorToResponse=o,exports.isRetryableError=n
|
|
1399
|
+
const i=t(7388)
|
|
1400
|
+
exports.ERROR_TYPE_PROPERTIES={[i.TASK_NOT_FOUND]:{statusCode:404,retryable:!1,defaultMessage:"Task ID does not exist"},[i.TASK_ID_INVALID_FORMAT]:{statusCode:400,retryable:!1,defaultMessage:"Task ID format is invalid"},[i.TASK_VALIDATION_FAILED]:{statusCode:400,retryable:!1,defaultMessage:"Task validation failed"},[i.NETWORK_ERROR]:{statusCode:503,retryable:!0,defaultMessage:"Network error occurred"},[i.SERVER_ERROR]:{statusCode:500,retryable:!0,defaultMessage:"Server error occurred"},[i.CONNECTION_TIMEOUT]:{statusCode:504,retryable:!0,defaultMessage:"Connection timed out"},[i.SERVICE_UNAVAILABLE]:{statusCode:503,retryable:!0,defaultMessage:"Service is currently unavailable"},[i.CONNECTION_ERROR]:{statusCode:503,retryable:!0,defaultMessage:"Connection to remote server failed"},[i.REQUEST_NOT_FOUND]:{statusCode:404,retryable:!1,defaultMessage:"Request ID does not exist"},[i.REQUEST_ID_INVALID_FORMAT]:{statusCode:400,retryable:!1,defaultMessage:"Request ID format is invalid"},[i.REQUEST_VALIDATION_FAILED]:{statusCode:400,retryable:!1,defaultMessage:"Request validation failed"},[i.VALIDATION_ERROR]:{statusCode:400,retryable:!1,defaultMessage:"Validation error"},[i.UNAUTHORIZED]:{statusCode:401,retryable:!1,defaultMessage:"Authentication required"},[i.FORBIDDEN]:{statusCode:403,retryable:!1,defaultMessage:"Access forbidden"},[i.NOT_FOUND]:{statusCode:404,retryable:!1,defaultMessage:"Resource not found"},[i.INTERNAL_ERROR]:{statusCode:500,retryable:!0,defaultMessage:"Internal server error"},[i.BAD_REQUEST]:{statusCode:400,retryable:!1,defaultMessage:"Bad request"},[i.MCP_PROTOCOL_ERROR]:{statusCode:500,retryable:!0,defaultMessage:"MCP protocol error"}},exports.ErrorUtils={isAppError:s,errorToResponse:o,isRetryableError:n,createErrorResponse:r}
|
|
1401
|
+
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)
|
|
1402
|
+
const o=exports.ERROR_TYPE_PROPERTIES[this.code]
|
|
1403
|
+
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
|
|
1404
|
+
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},8740(e,exports){"use strict"
|
|
1405
|
+
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)
|
|
1406
|
+
if(!r){const e=this.cache.get(s)
|
|
1407
|
+
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})))
|
|
1408
|
+
return this.cache.set(s,{hooks:n,time:Date.now()}),n.filter((e=>e.enabled))}catch(t){return this.logger.warn("Failed to fetch hooks",{eventType:e,error:t+""}),[]}}preWarm(e,t){this.getHooksForEvent(e,t).catch((()=>{}))}clear(){this.cache.clear()}}},8824(e,exports){"use strict"
|
|
1409
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.getErrorMessage=function(e){return e instanceof Error?e.message:"string"==typeof e?e:null===e?"null error":void 0===e?"undefined error":e+""},exports.getErrorStack=function(e){return e instanceof Error?e.stack||e.message:e+""},exports.formatErrorForLog=function(e){return e instanceof Error?{message:e.message,stack:e.stack}:{message:e+""}},exports.isError=function(e){return e instanceof Error},exports.getStructuredErrorMessage=function(e){if(e instanceof Error)return e.message
|
|
1410
|
+
if("string"==typeof e)return e
|
|
1411
|
+
if(!e||"object"!=typeof e)return e+""
|
|
1412
|
+
const t=e
|
|
1413
|
+
if(t.message)return t.message
|
|
1414
|
+
if(t.error?.message)return t.error.message
|
|
1415
|
+
if("string"==typeof t.error)return t.error
|
|
1416
|
+
if(t.statusText&&t.status)return`HTTP ${t.status}: ${t.statusText}`
|
|
1417
|
+
if("string"==typeof t.body){try{const e=JSON.parse(t.body)
|
|
1418
|
+
if(e.error?.message)return e.error.message
|
|
1419
|
+
if(e.message)return e.message}catch{}return t.body.substring(0,200)}for(const e of Object.keys(t)){const r=t[e]
|
|
1420
|
+
if("string"==typeof r&&r.length>0)return`${e}: ${r}`}return"Unknown error (no readable properties)"}},8955(e,exports){"use strict"
|
|
1421
|
+
function t(){return n||s}function r(){return t().map((e=>e.name))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.REQUEST_SCOPED_AGENTS=void 0,exports.initializeAgentRegistry=async function(e,t){return a||(a=(async()=>{try{if(!e)return t.debug("No API client available, using default agents"),n=s,void(i=!0)
|
|
1055
1422
|
t.info("Fetching available agents from API")
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
return t?{preferredProvider:t.preferredProvider||"inherit",preferredModelTier:t.preferredModelTier||"same",requestContextMode:t.requestContextMode}:null},exports.getAllowedSubagents=function(e){return o[e]||null},exports.resetAgentRegistry=function(){n=null,i=!1,a=null}
|
|
1059
|
-
const
|
|
1423
|
+
const r=await e.proxyGet("/api/agents/templates")
|
|
1424
|
+
r&&Array.isArray(r)&&r.length>0?(n=r.map((e=>({id:e.id||e.name,name:e.name,description:e.description,mode:e.mode,builtIn:e.builtIn}))),t.info("Agent registry initialized",{agentCount:n.length})):(t.warn("No agents returned from API, using defaults"),n=s),i=!0}catch(e){t.warn("Failed to fetch agents from API, using defaults",{error:e instanceof Error?e.message:e+""}),n=s,i=!0}})(),a)},exports.getAvailableAgents=t,exports.getAgentNames=r,exports.isValidAgent=function(e){return r().includes(e)},exports.getDefaultAgent=function(){return"work"},exports.formatAgentsForDescription=function(){return t().map((e=>`'${e.name}'${e.description?" - "+e.description:""}`)).join(", ")},exports.isAgentRegistryInitialized=function(){return i},exports.getBuiltInAgentConfig=function(e){const t=s.find((t=>t.name===e))
|
|
1425
|
+
return t?{preferredProvider:t.preferredProvider||"inherit",preferredModelTier:t.preferredModelTier||"same",requestContextMode:t.requestContextMode}:null},exports.getAllowedSubagents=function(e){return o[e]||null},exports.resetAgentRegistry=function(){n=null,i=!1,a=null},exports.REQUEST_SCOPED_AGENTS=["coder","tester","test-architect"]
|
|
1426
|
+
const s=[{id:"coder",name:"coder",description:"Development and implementation agent",builtIn:!0,preferredModelTier:"same",requestContextMode:"required"},{id:"research",name:"research",description:"Codebase research and exploration agent",builtIn:!0,preferredModelTier:"two_down",requestContextMode:"none"},{id:"journal",name:"journal",description:"Journal generation agent",builtIn:!0,preferredModelTier:"one_down"},{id:"planner",name:"planner",description:"Planning and architecture agent",builtIn:!0,preferredModelTier:"same"},{id:"specialist",name:"specialist",description:"Domain specialist for development tasks",builtIn:!0,preferredModelTier:"same"},{id:"auto-planner",name:"auto-planner",description:"Autonomous background planner",builtIn:!0,preferredModelTier:"same"},{id:"alchemist",name:"alchemist",description:"Persistent orchestrator",builtIn:!0,preferredModelTier:"same"}],o={alchemist:["specialist","research","planner"],specialist:["coder","research","auto-planner","planner"],tester:["research"],"auto-tester":["research"]}
|
|
1060
1427
|
let n=null,i=!1,a=null},8984(e,exports,t){"use strict"
|
|
1061
1428
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.MCPClientConfigurer=void 0
|
|
1062
|
-
const
|
|
1063
|
-
class o{constructor(e,t,
|
|
1429
|
+
const r=t(8824),s=t(5671)
|
|
1430
|
+
class o{constructor(e,t,r){this.logger=e,this.remoteServerUrl=t||process.env.REMOTE_SERVER_URL,this.authToken=r}async seedAgentsFromRemote(e){const t=this.remoteServerUrl
|
|
1064
1431
|
if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot initialize agents"),!1
|
|
1065
|
-
try{const
|
|
1066
|
-
this.logger.info("Ensuring built-in agents exist",{url:
|
|
1067
|
-
const
|
|
1068
|
-
this.authToken&&(
|
|
1069
|
-
const o=await fetch(
|
|
1432
|
+
try{const r=t+"/api/agents/seed"
|
|
1433
|
+
this.logger.info("Ensuring built-in agents exist",{url:r})
|
|
1434
|
+
const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service","X-Service-Secret":process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"}
|
|
1435
|
+
this.authToken&&(s.Authorization="Bearer "+this.authToken)
|
|
1436
|
+
const o=await fetch(r,{method:"POST",headers:s})
|
|
1070
1437
|
o.ok||this.logger.warn("Failed to seed global agents",{status:o.status,statusText:o.statusText})
|
|
1071
1438
|
const n=`${t}/api/projects/${e}/agents/initialize`
|
|
1072
1439
|
this.logger.info("Initializing project agent activations",{url:n})
|
|
1073
|
-
const i=await fetch(n,{method:"POST",headers:
|
|
1440
|
+
const i=await fetch(n,{method:"POST",headers:s})
|
|
1074
1441
|
if(!i.ok)return this.logger.warn("Failed to initialize project agents",{status:i.status,statusText:i.statusText}),!1
|
|
1075
1442
|
const a=await i.json()
|
|
1076
|
-
return this.logger.info("Initialized project agent activations",{initialized:a.data?.initialized||0}),!0}catch(e){return this.logger.warn("Error initializing agents from remote",{error:(0,
|
|
1443
|
+
return this.logger.info("Initialized project agent activations",{initialized:a.data?.initialized||0}),!0}catch(e){return this.logger.warn("Error initializing agents from remote",{error:(0,r.getErrorMessage)(e)}),!1}}async fetchPluginManifest(){const e={"opencode-claude-auth":"1.5.3"},t=this.remoteServerUrl
|
|
1444
|
+
if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, using baseline plugin manifest"),{...e}
|
|
1445
|
+
const s=t+"/api/distribution/plugin-versions"
|
|
1446
|
+
try{const t={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service","X-Service-Secret":process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"}
|
|
1447
|
+
this.authToken&&(t.Authorization="Bearer "+this.authToken)
|
|
1448
|
+
const r=await fetch(s,{method:"GET",headers:t})
|
|
1449
|
+
if(!r.ok)return this.logger.warn("Plugin manifest fetch returned non-OK; using baseline",{status:r.status}),{...e}
|
|
1450
|
+
const o=await r.json(),n=o?.data?.plugins??o?.plugins??{},i={...e}
|
|
1451
|
+
for(const[e,t]of Object.entries(n)){const r=t?.version
|
|
1452
|
+
"string"==typeof r&&r.trim()&&(i[e]=r.trim())}return this.logger.info("Fetched plugin manifest",{manifest:i}),i}catch(t){return this.logger.warn("Plugin manifest fetch failed; using baseline",{error:(0,r.getErrorMessage)(t)}),{...e}}}async sweepLegacyAuthPluginFiles(e){const r=await Promise.resolve().then(t.t.bind(t,1943,23)),s=await Promise.resolve().then(t.t.bind(t,6928,23)),o=(await Promise.resolve().then(t.t.bind(t,857,23))).homedir(),n=new Set
|
|
1453
|
+
let i=e
|
|
1454
|
+
for(let e=0;6>e;e++){n.add(s.join(i,".opencode","plugin","anthropic-auth.js"))
|
|
1455
|
+
const e=s.dirname(i)
|
|
1456
|
+
if(e===i)break
|
|
1457
|
+
i=e}n.add(s.join(o,".opencode","plugin","anthropic-auth.js")),n.add(s.join(o,".config","opencode","plugin","anthropic-auth.js")),n.add(s.join(o,".alchemy_lab","opencode-dev","plugin","anthropic-auth.js"))
|
|
1458
|
+
const a=(new Date).toISOString().slice(0,10)
|
|
1459
|
+
await Promise.all(Array.from(n).map((async e=>{try{const t=await r.readFile(e,"utf-8").catch((()=>null))
|
|
1460
|
+
if(null===t)return
|
|
1461
|
+
if(!(t.includes("Alchemy Anthropic Auth Plugin")||t.includes("opencode-anthropic-auth@0.0.5")||t.includes("Transforming tool names to Title Case")))return void this.logger.debug("anthropic-auth.js signature did not match legacy plugin, leaving in place",{path:e})
|
|
1462
|
+
const s=`${e}.removed-by-alchemy-${a}`
|
|
1463
|
+
await r.rename(e,s),this.logger.warn("Removed legacy Alchemy Anthropic Auth Plugin file",{from:e,movedTo:s,reason:"shadows opencode-claude-auth and causes 400 'out of extra usage' responses from Anthropic"})}catch(t){this.logger.debug("sweepLegacyAuthPluginFiles candidate failed",{candidate:e,error:t instanceof Error?t.message:t+""})}})))}applyAuthPluginPin(e,t){const r="opencode-claude-auth@"+t,s=e.slice(),o=e.filter((e=>!(e=>"string"==typeof e&&/^opencode-claude-auth(@|$)/.test(e))(e)))
|
|
1464
|
+
if(e.length=0,e.push(r,...o),s.length!==e.length)return!0
|
|
1465
|
+
for(let t=0;t<s.length;t++)if(s[t]!==e[t])return!0
|
|
1466
|
+
return!1}async fetchAgentsFromRemote(e){const t=this.remoteServerUrl
|
|
1077
1467
|
if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot fetch agents from remote"),null
|
|
1078
|
-
try{const
|
|
1079
|
-
this.logger.info("Fetching agents from remote",{url:
|
|
1080
|
-
const
|
|
1081
|
-
this.authToken&&(
|
|
1082
|
-
const o=await fetch(
|
|
1468
|
+
try{const r=`${t}/api/projects/${e}/agents/enabled`
|
|
1469
|
+
this.logger.info("Fetching agents from remote",{url:r})
|
|
1470
|
+
const s={"Content-Type":"application/json"}
|
|
1471
|
+
this.authToken&&(s.Authorization="Bearer "+this.authToken)
|
|
1472
|
+
const o=await fetch(r,{method:"GET",headers:s})
|
|
1083
1473
|
if(!o.ok)return this.logger.warn("Failed to fetch agents from remote",{status:o.status,statusText:o.statusText}),null
|
|
1084
1474
|
const n=await o.json(),i=n.data||n
|
|
1085
|
-
return this.logger.info("Fetched agents from remote",{count:i.length}),i}catch(e){return this.logger.warn("Error fetching agents from remote",{error:(0,
|
|
1475
|
+
return this.logger.info("Fetched agents from remote",{count:i.length}),i}catch(e){return this.logger.warn("Error fetching agents from remote",{error:(0,r.getErrorMessage)(e)}),null}}async fetchProjectFromRemote(e){const t=this.remoteServerUrl
|
|
1086
1476
|
if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot fetch project from remote"),null
|
|
1087
|
-
try{const
|
|
1088
|
-
if(!
|
|
1089
|
-
const
|
|
1090
|
-
return
|
|
1091
|
-
if(e.preferredModelTier&&"same"!==e.preferredModelTier){const t=(0,
|
|
1092
|
-
t.resolved&&(i=t.fullModel)}const a=Object.entries(e.tools||{}).map((([e,
|
|
1093
|
-
return` ${o.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${
|
|
1477
|
+
try{const r=await fetch(`${t}/api/projects/${e}`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:"Bearer "+this.authToken}:{}}})
|
|
1478
|
+
if(!r.ok)return this.logger.warn("Failed to fetch project from remote",{projectId:e,status:r.status,statusText:r.statusText}),null
|
|
1479
|
+
const s=await r.json()
|
|
1480
|
+
return s.data||s}catch(t){return this.logger.error("Error fetching project from remote",{projectId:e,error:(0,r.getErrorMessage)(t)}),null}}generateAgentMarkdown(e,t,r,n){let i=r
|
|
1481
|
+
if(e.preferredModelTier&&"same"!==e.preferredModelTier){const t=(0,s.resolveModelTier)(r,e.preferredProvider||"inherit",e.preferredModelTier,{anthropic:!0,openai:!0})
|
|
1482
|
+
t.resolved&&(i=t.fullModel)}const a=Object.entries(e.tools||{}).map((([e,r])=>{const s=e.replace(/-/g,"_")
|
|
1483
|
+
return` ${o.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${s}`}: ${r}`})).join("\n")
|
|
1094
1484
|
let c=`---\ndescription: "${e.description||e.name}"\nmode: "${"utility"===e.mode?"primary":e.mode||"primary"}"\nmodel: "${i}"\n`
|
|
1095
1485
|
if(e.color&&(c+=`color: "${e.color}"\n`),c+=`tools:\n${a}\n`,e.opencodePermission&&Object.keys(e.opencodePermission).length>0){c+="permission:\n"
|
|
1096
|
-
for(const[t,
|
|
1097
|
-
for(const[e,t]of Object.entries(
|
|
1098
|
-
this.logger.info("Configuring OpenCode",{isDevMode:l,serverName:d,scriptPath:u,projectPath:
|
|
1099
|
-
const h=c.join(
|
|
1100
|
-
let g=null,p=null,
|
|
1101
|
-
const w=c.join(
|
|
1102
|
-
if(!i){try{const e=await
|
|
1103
|
-
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
|
|
1104
|
-
t?.mode?.alchemy?.tools&&(
|
|
1105
|
-
i?.openCodeModel?
|
|
1106
|
-
const C=c.join(
|
|
1107
|
-
await
|
|
1108
|
-
const
|
|
1109
|
-
let
|
|
1110
|
-
if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),
|
|
1111
|
-
for(const e of
|
|
1112
|
-
const t=this.generateAgentMarkdown(e,d,
|
|
1113
|
-
await
|
|
1486
|
+
for(const[t,r]of Object.entries(e.opencodePermission))if("object"==typeof r&&null!==r){c+=` ${t}:\n`
|
|
1487
|
+
for(const[e,t]of Object.entries(r))c+=` ${e}: ${t}\n`}else c+=` ${t}: ${r}\n`}return void 0!==e.temperature&&(c+=`temperature: ${e.temperature}\n`),(e.name.endsWith("-thinking")||e.thinkingModeDefault)&&(c+="thinking:\n type: enabled\n budgetTokens: 10000\n"),"journal"===e.name&&(c+=`mcp:\n ${t}:\n env:\n ALCHEMY_MODE: "journal"\n`),c+="---\n\n",n&&(c+=n.trim()+"\n\n"),e.prompt&&(c+=e.prompt),e.promptAddition&&(c+="\n\n"+e.promptAddition),c}async configureOpenCode(e,s,n="node",i,a){try{const r=await Promise.resolve().then(t.t.bind(t,1943,23)),c=await Promise.resolve().then(t.t.bind(t,6928,23)),l=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),d="alchemy",u=c.join(e,"mcp-direct.js")
|
|
1488
|
+
this.logger.info("Configuring OpenCode",{isDevMode:l,serverName:d,scriptPath:u,projectPath:s}),await this.sweepLegacyAuthPluginFiles(s)
|
|
1489
|
+
const h=c.join(s,"opencode.json")
|
|
1490
|
+
let g=null,p=null,m=null,f=null,y=!1,v=!1
|
|
1491
|
+
const w=c.join(s,".alchemy_lab","config.json")
|
|
1492
|
+
if(!i){try{const e=await r.readFile(w,"utf-8"),t=JSON.parse(e)
|
|
1493
|
+
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)
|
|
1494
|
+
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 b=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},I={tools:b},E=i?.openCodeModel||p||f||"anthropic/claude-opus-4-5"
|
|
1495
|
+
i?.openCodeModel?I.model=i.openCodeModel:p?I.model=p:f&&(I.model=f)
|
|
1496
|
+
const C=c.join(s,".opencode","agent")
|
|
1497
|
+
await r.mkdir(C,{recursive:!0})
|
|
1498
|
+
const S=E||"anthropic/claude-opus-4-5-20251101"
|
|
1499
|
+
let k,T=null,_=null
|
|
1500
|
+
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})
|
|
1501
|
+
for(const e of T){if(!e.enabled)continue
|
|
1502
|
+
const t=this.generateAgentMarkdown(e,d,S,k),s=c.join(C,e.name+".md")
|
|
1503
|
+
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")
|
|
1114
1504
|
else this.logger.warn("No projectId provided - agents must be configured via database")
|
|
1115
|
-
const
|
|
1116
|
-
if(l&&process.env.REMOTE_SERVER_URL&&(
|
|
1117
|
-
for(const e of
|
|
1505
|
+
const j={$schema:"https://opencode.ai/config.json",model:E,mcp:{[d]:{type:"local",command:[n,u,s],enabled:!0}}}
|
|
1506
|
+
if(l&&process.env.REMOTE_SERVER_URL&&(j.mcp[d].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL}),T&&T.length>0){j.agent={}
|
|
1507
|
+
for(const e of T){if(!e.enabled)continue
|
|
1118
1508
|
const t={}
|
|
1119
|
-
for(const[s
|
|
1120
|
-
t[o.OPENCODE_BUILTIN_TOOLS.has(
|
|
1121
|
-
e.name in
|
|
1122
|
-
let
|
|
1123
|
-
|
|
1124
|
-
const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${
|
|
1125
|
-
await
|
|
1126
|
-
P.
|
|
1127
|
-
try{const e=await Promise.resolve().then(t.t.bind(t,857,23)),
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
let
|
|
1131
|
-
try{
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=I,await s.writeFile(w,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:w}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPlugins:s}=await t.e(939).then(t.t.bind(t,9939,23)),o=await s(r,l??!1,this.logger,e)
|
|
1135
|
-
o.synced.length>0&&this.logger.info("Synced plugins during OpenCode config",{synced:o.synced}),o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors})}catch(e){this.logger.warn("Plugin sync failed during OpenCode config",{error:e instanceof Error?e.message:"Unknown error"})}return{success:!0,configPath:h}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,s.getErrorMessage)(e)}),{success:!1,error:(0,s.getErrorMessage)(e)}}}}exports.MCPClientConfigurer=o,o.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","fetch","agent","task","question","todoread","todowrite"])},9023(module){"use strict"
|
|
1509
|
+
for(const[r,s]of Object.entries(e.tools||{})){const e=s,n=r.replace(/-/g,"_")
|
|
1510
|
+
t[o.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${d}_${n}`]=e}const r=_?.metadata?.bashToolAgents||{}
|
|
1511
|
+
e.name in r&&(t.bash=!!r[e.name]),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name)&&void 0===t.task&&(t.task=!1)
|
|
1512
|
+
let s=""
|
|
1513
|
+
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}}
|
|
1514
|
+
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`
|
|
1515
|
+
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))):[]
|
|
1516
|
+
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})
|
|
1517
|
+
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")
|
|
1518
|
+
let n=null
|
|
1519
|
+
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)))
|
|
1520
|
+
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={}
|
|
1521
|
+
try{const t=await r.readFile(w,"utf-8")
|
|
1522
|
+
e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=b,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)
|
|
1523
|
+
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"
|
|
1136
1524
|
module.exports=require("util")},9067(e,exports,t){"use strict"
|
|
1137
|
-
var
|
|
1525
|
+
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
1138
1526
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.ExternalAuthFlow=exports.PollingStatus=void 0
|
|
1139
|
-
const r
|
|
1527
|
+
const s=r(t(6982)),o=t(8824)
|
|
1140
1528
|
var n
|
|
1141
|
-
!function(e){e.NOT_STARTED="not_started",e.POLLING="polling",e.AUTHENTICATED="authenticated",e.TIMEOUT="timeout",e.ERROR="error"}(n||(exports.PollingStatus=n={})),exports.ExternalAuthFlow=class{constructor(e,t,
|
|
1142
|
-
|
|
1143
|
-
const
|
|
1144
|
-
return this.logger.info("Generated auth URL",{url:o,frontendUrl:
|
|
1529
|
+
!function(e){e.NOT_STARTED="not_started",e.POLLING="polling",e.AUTHENTICATED="authenticated",e.TIMEOUT="timeout",e.ERROR="error"}(n||(exports.PollingStatus=n={})),exports.ExternalAuthFlow=class{constructor(e,t,r=5e3,s=9e5){this.pollingStatus=n.NOT_STARTED,this.pollingStartTime=0,this.pollingIntervalId=null,this.tokenResult=null,this.pollingError=null,this.logger=e.child({component:"auth-flow"}),this.remoteServerUrl=t,this.pollingIntervalMs=r,this.maxPollingTimeMs=s}getAuthUrl(e){let r=this.remoteServerUrl
|
|
1530
|
+
r=r.includes("api.alchemylabcode.com")?r.replace("api.alchemylabcode.com","alchemylabcode.com"):r.match(/localhost:\d+/)?r.replace(/localhost:\d+/,"localhost:3000"):r.replace(/\/\/api\./g,"//")
|
|
1531
|
+
const s=t(6928).basename(process.cwd()),o=`${r}/login?mcp_code=${e}&project=${encodeURIComponent(s)}`
|
|
1532
|
+
return this.logger.info("Generated auth URL",{url:o,frontendUrl:r,backendUrl:this.remoteServerUrl}),o}getPollingUrl(e){const t=`${this.remoteServerUrl}/auth/check-auth?code=${e}`
|
|
1145
1533
|
return this.logger.info("Generated polling URL",{url:t}),t}async requestTemporaryCode(){const e=this.remoteServerUrl+"/auth/generate-code"
|
|
1146
1534
|
this.logger.info("Requesting temporary code from remote server",{url:e})
|
|
1147
1535
|
try{const t=await fetch(e)
|
|
1148
1536
|
if(!t.ok){const e=await t.text().catch((()=>"Failed to read error response body"))
|
|
1149
|
-
throw Error(`Error requesting temporary code: ${t.status} ${t.statusText}\nResponse: ${e}`)}const
|
|
1150
|
-
if(!
|
|
1151
|
-
const r
|
|
1152
|
-
return this.logger.info("Received temporary code from server",{code:
|
|
1153
|
-
return this.logger.info("Starting external authentication flow",{tempCode:e,authUrl:t,pollingUrl:
|
|
1154
|
-
const i=async()=>{try{const
|
|
1155
|
-
if(
|
|
1537
|
+
throw Error(`Error requesting temporary code: ${t.status} ${t.statusText}\nResponse: ${e}`)}const r=await t.json()
|
|
1538
|
+
if(!r.success||!r.code)throw Error("Invalid response from server: "+JSON.stringify(r))
|
|
1539
|
+
const s=r.code
|
|
1540
|
+
return this.logger.info("Received temporary code from server",{code:s}),s}catch(e){throw this.logger.error("Failed to request temporary code",{error:(0,o.getErrorMessage)(e)}),e}}async startAuthFlow(){const e=await this.requestTemporaryCode(),t=this.getAuthUrl(e),r=this.getPollingUrl(e)
|
|
1541
|
+
return this.logger.info("Starting external authentication flow",{tempCode:e,authUrl:t,pollingUrl:r,remoteServerUrl:this.remoteServerUrl}),{tempCode:e,authUrl:t,pollingUrl:r}}parseTokenResponse(e){try{return e&&e.token&&e.expiresAt?{token:e.token,userId:e.userId||"unknown",username:e.username||"unknown",role:e.role||"user",expiresAt:e.expiresAt}:null}catch(e){return this.logger.error("Error parsing token response",{error:(0,o.getErrorMessage)(e)}),null}}startPolling(e,t,r,s){this.pollingStatus=n.POLLING,this.pollingStartTime=Date.now(),this.tokenResult=null,this.pollingError=null,this.stopPolling(),this.logger.info("Starting polling for authentication",{tempCode:e.substring(0,4)+"***",interval:this.pollingIntervalMs,maxTime:this.maxPollingTimeMs})
|
|
1542
|
+
const i=async()=>{try{const s=Date.now()-this.pollingStartTime
|
|
1543
|
+
if(s>this.maxPollingTimeMs)return this.logger.warn("Authentication polling timed out",{tempCode:e.substring(0,4)+"***",elapsedTime:s,maxTime:this.maxPollingTimeMs}),this.pollingStatus=n.TIMEOUT,this.stopPolling(),void(r&&r())
|
|
1156
1544
|
const i=this.getPollingUrl(e)
|
|
1157
1545
|
this.logger.debug("Polling for authentication status",{tempCode:e,url:i})
|
|
1158
|
-
try{const
|
|
1159
|
-
if(!
|
|
1160
|
-
throw Error(`Error polling for authentication: ${
|
|
1161
|
-
if(this.logger.debug("Polling response",{data:
|
|
1162
|
-
const
|
|
1163
|
-
if(!
|
|
1164
|
-
this.pollingStatus=n.AUTHENTICATED,this.tokenResult=
|
|
1165
|
-
throw Error(`Error during polling: ${t}${/fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network/i.test(t)?" (remote may be unreachable \u2014 check your connection and try again)":""}`)}}catch(t){const
|
|
1166
|
-
this.logger.error("Error polling for authentication",{tempCode:e.substring(0,4)+"***",error:
|
|
1546
|
+
try{const r=await fetch(i)
|
|
1547
|
+
if(!r.ok){const e=await r.text().catch((()=>"Failed to read error response body"))
|
|
1548
|
+
throw Error(`Error polling for authentication: ${r.status} ${r.statusText}\nResponse: ${e}`)}const s=await r.json()
|
|
1549
|
+
if(this.logger.debug("Polling response",{data:s}),s.authenticated&&s.token){this.logger.info("Authentication successful",{tempCode:e.substring(0,4)+"***",userId:s.userId})
|
|
1550
|
+
const r=this.parseTokenResponse(s)
|
|
1551
|
+
if(!r)throw Error("Invalid token response from server")
|
|
1552
|
+
this.pollingStatus=n.AUTHENTICATED,this.tokenResult=r,this.stopPolling(),t&&t(r)}else this.logger.debug("Not authenticated yet, continuing to poll",{tempCode:e.substring(0,4)+"***"})}catch(e){const t=(0,o.getErrorMessage)(e)
|
|
1553
|
+
throw Error(`Error during polling: ${t}${/fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network/i.test(t)?" (remote may be unreachable \u2014 check your connection and try again)":""}`)}}catch(t){const r=(0,o.getErrorMessage)(t)
|
|
1554
|
+
this.logger.error("Error polling for authentication",{tempCode:e.substring(0,4)+"***",error:r}),this.pollingStatus=n.ERROR,this.pollingError=r,this.stopPolling(),s&&s(r)}}
|
|
1167
1555
|
return i(),this.pollingIntervalId=setInterval(i,this.pollingIntervalMs),()=>this.stopPolling()}stopPolling(){this.pollingIntervalId&&(clearInterval(this.pollingIntervalId),this.pollingIntervalId=null,this.logger.debug("Stopped polling for authentication",{status:this.pollingStatus}))}getPollingStatus(){const e={status:this.pollingStatus}
|
|
1168
1556
|
return this.pollingStatus===n.AUTHENTICATED&&this.tokenResult?e.tokenInfo=this.tokenResult:this.pollingStatus===n.ERROR&&this.pollingError&&(e.error=this.pollingError),this.pollingStartTime>0&&(e.elapsedTimeMs=Date.now()-this.pollingStartTime),e}resetPollingState(){this.stopPolling(),this.pollingStatus=n.NOT_STARTED,this.pollingStartTime=0,this.tokenResult=null,this.pollingError=null}async startGoogleOAuthFlow(){this.logger.info("Starting Google OAuth authentication flow")
|
|
1169
|
-
try{const e=
|
|
1170
|
-
if(!
|
|
1171
|
-
throw Error(`Error getting Google OAuth URL: ${
|
|
1557
|
+
try{const e=s.default.randomBytes(16).toString("hex"),t=`${this.remoteServerUrl}/api/auth/google/auth-url?state=${e}`,r=await fetch(t)
|
|
1558
|
+
if(!r.ok){const e=await r.text().catch((()=>"Failed to read error response body"))
|
|
1559
|
+
throw Error(`Error getting Google OAuth URL: ${r.status} ${r.statusText}\nResponse: ${e}`)}const o=await r.json()
|
|
1172
1560
|
if(!o.success||!o.data||!o.data.authUrl)throw Error("Invalid response from server: "+JSON.stringify(o))
|
|
1173
1561
|
const n=o.data.authUrl
|
|
1174
1562
|
return this.logger.info("Received Google OAuth URL from server",{authUrl:n,state:e}),{authUrl:n,state:e}}catch(e){throw this.logger.error("Failed to start Google OAuth flow",{error:(0,o.getErrorMessage)(e)}),e}}async isGoogleOAuthAvailable(){try{const e=this.remoteServerUrl+"/api/auth/google/status",t=await fetch(e)
|
|
1175
1563
|
if(!t.ok)return!1
|
|
1176
|
-
const
|
|
1177
|
-
return
|
|
1564
|
+
const r=await t.json()
|
|
1565
|
+
return r.success&&r.data&&!0===r.data.enabled}catch(e){return this.logger.debug("Error checking Google OAuth availability",{error:(0,o.getErrorMessage)(e)}),!1}}async startGoogleOAuthPolling(e,t,r,s,i){return this.logger.info("Starting Google OAuth polling",{state:t}),this.pollingStatus=n.POLLING,this.pollingStartTime=Date.now(),this.tokenResult=null,this.pollingError=null,this.logger.info("Google OAuth authentication URLs ready for user interaction"),this.pollingIntervalId=setInterval((async()=>{try{if(Date.now()-this.pollingStartTime>this.maxPollingTimeMs)return this.logger.info("Google OAuth polling timed out"),this.pollingStatus=n.TIMEOUT,this.stopPolling(),void(s&&s())
|
|
1178
1566
|
const e=`${this.remoteServerUrl}/api/auth/google/check-status?state=${t}`,o=await fetch(e)
|
|
1179
1567
|
if(!o.ok)return void this.logger.debug("Polling response not ok",{status:o.status})
|
|
1180
1568
|
const i=await o.json()
|
|
1181
1569
|
if(this.logger.debug("Google OAuth polling response",{data:i}),i.success&&i.data&&i.data.authenticated&&i.data.token){this.logger.info("Google OAuth authentication completed")
|
|
1182
1570
|
const e=this.parseTokenResponse(i.data)
|
|
1183
|
-
e&&(this.pollingStatus=n.AUTHENTICATED,this.tokenResult=e,this.stopPolling(),
|
|
1184
|
-
function
|
|
1571
|
+
e&&(this.pollingStatus=n.AUTHENTICATED,this.tokenResult=e,this.stopPolling(),r&&r(e))}else this.logger.debug("Not authenticated yet, continuing to poll",{hasSuccess:!!i.success,hasData:!!i.data,authenticated:i.data?.authenticated})}catch(e){this.logger.error("Error during Google OAuth polling",{error:(0,o.getErrorMessage)(e)}),this.pollingStatus=n.ERROR,this.pollingError=(0,o.getErrorMessage)(e),this.stopPolling(),i&&i(this.pollingError)}}),this.pollingIntervalMs),{authUrl:e,state:t}}}},9068(e,exports,t){"use strict"
|
|
1572
|
+
function r(){const e=process.env.ALCHEMY_OPENCODE_DIR
|
|
1185
1573
|
if(e)return e
|
|
1186
1574
|
try{const e=("undefined"!=typeof require?require:t(4245)).resolve("alchemylab-opencode/package.json")
|
|
1187
|
-
return(0,n.dirname)(e)}catch{}}function
|
|
1188
|
-
if(
|
|
1189
|
-
if((0,i.existsSync)(t))try{const e=(0,i.readdirSync)(t).filter((e=>
|
|
1190
|
-
return parseInt(t.replace("v","").split(".")[0],10)-
|
|
1191
|
-
if(e.length>0){const
|
|
1192
|
-
if((0,i.existsSync)(
|
|
1193
|
-
return e?o((0,n.join)(e,"dist"),"serve.js"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=
|
|
1575
|
+
return(0,n.dirname)(e)}catch{}}function s(e){return 22===e||e>=24}function o(e,t){return{command:function(){try{const e=(0,a.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
|
|
1576
|
+
if(s(parseInt(e.replace("v","").split(".")[0],10)))return"node"}catch{}const e=process.env.NVM_DIR||(0,n.join)(process.env.HOME||"",".nvm"),t=(0,n.join)(e,"versions","node")
|
|
1577
|
+
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)
|
|
1578
|
+
return parseInt(t.replace("v","").split(".")[0],10)-r}))
|
|
1579
|
+
if(e.length>0){const r=(0,n.join)(t,e[0],"bin","node")
|
|
1580
|
+
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()
|
|
1581
|
+
return e?o((0,n.join)(e,"dist"),"serve.js"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
|
|
1194
1582
|
return e?o((0,n.join)(e,"dist"),"run.js"):{command:"opencode",prependArgs:[]}}
|
|
1195
1583
|
const n=t(6928),i=t(9896),a=t(5317)},9266(e,exports,t){"use strict"
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
try{
|
|
1202
|
-
if(
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
if("GET"!==s.toUpperCase()&&"HEAD"!==s.toUpperCase()&&void 0!==a){let e=a
|
|
1206
|
-
if("object"==typeof a&&null!==a&&"_alchemyAgentType"in a){const{_alchemyAgentType:t,...s}=a
|
|
1207
|
-
e=s}r.body="string"==typeof e?e:JSON.stringify(e)}const o=await fetch(d,r),l=o.headers.get("content-type")||""
|
|
1584
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleOpenCodeProxyAction=async function(e,t){const{method:o,path:n,body:i,headers:a}=e
|
|
1585
|
+
if(!o||!n)return{success:!1,message:"method and path are required in proxy payload"}
|
|
1586
|
+
const c=(0,s.getOpenCodeUrlFromConfig)(t.projectPath)
|
|
1587
|
+
if(!c)return{success:!1,message:"OpenCode not running (no opencode.json found)"}
|
|
1588
|
+
const l=`${c}${n.startsWith("/")?n:"/"+n}`
|
|
1589
|
+
try{const e={"Content-Type":"application/json",...a},s={method:o.toUpperCase(),headers:e}
|
|
1590
|
+
if("GET"!==o.toUpperCase()&&"HEAD"!==o.toUpperCase()&&void 0!==i){let e=i
|
|
1591
|
+
if("object"==typeof i&&null!==i&&"_alchemyAgentType"in i){const{_alchemyAgentType:t,...r}=i
|
|
1592
|
+
e=r}s.body="string"==typeof e?e:JSON.stringify(e)}const c=await fetch(l,s),d=c.headers.get("content-type")||""
|
|
1208
1593
|
let u
|
|
1209
|
-
if(u=
|
|
1210
|
-
await t.apiClient.post(`/api/projects/${t.projectId}/cli-sessions`,{externalSessionId:e,providerId:"opencode",...
|
|
1211
|
-
const r=
|
|
1594
|
+
if(u=d.includes("application/json")?await c.json():await c.text(),"POST"===o.toUpperCase()&&"/session"===n&&c.status>=200&&300>c.status&&u?.id)try{const e=u.id,r=i?.parentID
|
|
1595
|
+
await t.apiClient.post(`/api/projects/${t.projectId}/cli-sessions`,{externalSessionId:e,providerId:"opencode",...r?{parentExternalSessionId:r}:{},agentType:i?._alchemyAgentType||"unknown",status:"active"}),t.logger.info("Registered proxy-created session in DB",{sessionId:e,parentSessionId:r})}catch(e){t.logger.warn("Failed to register proxy-created session",{error:(0,r.getErrorMessage)(e)})}return{success:!0,message:"Proxy request completed",data:{status:c.status,contentType:d,body:u}}}catch(e){return{success:!1,message:"Proxy request failed: "+(0,r.getErrorMessage)(e),error:(0,r.getErrorMessage)(e)}}}
|
|
1596
|
+
const r=t(8824),s=t(833)},9278(module){"use strict"
|
|
1212
1597
|
module.exports=require("net")},9735(e,exports,t){"use strict"
|
|
1213
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleApproveRequestAction=async function(e,t){const{requestId:
|
|
1214
|
-
if(!
|
|
1215
|
-
o.info("Starting approve-request action",{requestId:
|
|
1216
|
-
try{const e=await n.get(`/api/projects/${i}/requests/${
|
|
1217
|
-
if(!e.success||!e.data)return{success:!1,message:`Request ${
|
|
1598
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleApproveRequestAction=async function(e,t){const{requestId:s}=e,{logger:o,apiClient:n,projectId:i}=t
|
|
1599
|
+
if(!s)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
1600
|
+
o.info("Starting approve-request action",{requestId:s})
|
|
1601
|
+
try{const e=await n.get(`/api/projects/${i}/requests/${s}`)
|
|
1602
|
+
if(!e.success||!e.data)return{success:!1,message:`Request ${s} not found`,error:"Request not found"}
|
|
1218
1603
|
const t=e.data
|
|
1219
|
-
return"pending_approval"!==t.status?{success:!1,message:`Cannot approve request with status '${t.status}'. Only requests in 'pending_approval' status can be approved.`,error:"Invalid status: "+t.status}:(await n.patch(`/api/projects/${i}/requests/${
|
|
1220
|
-
return o.error("approve-request action failed",{requestId:
|
|
1221
|
-
const
|
|
1604
|
+
return"pending_approval"!==t.status?{success:!1,message:`Cannot approve request with status '${t.status}'. Only requests in 'pending_approval' status can be approved.`,error:"Invalid status: "+t.status}:(await n.patch(`/api/projects/${i}/requests/${s}`,{status:"open"})).success?(o.info("Approve-request action completed",{requestId:s}),{success:!0,message:`Successfully approved request ${s}: ${t.title}`,data:{operationType:"approve",previousStatus:"pending_approval",newStatus:"open"}}):{success:!1,message:"Failed to update request status",error:"Update failed"}}catch(e){const t=(0,r.getErrorMessage)(e)
|
|
1605
|
+
return o.error("approve-request action failed",{requestId:s,error:t}),{success:!1,message:"Failed to approve request: "+t,error:t}}}
|
|
1606
|
+
const r=t(8824)},9860(e,exports,t){"use strict"
|
|
1607
|
+
var r=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r)
|
|
1608
|
+
var o=Object.getOwnPropertyDescriptor(t,r)
|
|
1609
|
+
o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get(){return t[r]}}),Object.defineProperty(e,s,o)}:function(e,t,r,s){void 0===s&&(s=r),e[s]=t[r]}),s=this&&this.__exportStar||function(e,exports){for(var t in e)"default"===t||{}.hasOwnProperty.call(exports,t)||r(exports,e,t)}
|
|
1610
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.UnauthorizedError=exports.McpProtocolError=exports.FileOperationError=exports.ServiceError=exports.DatabaseError=exports.AuthorizationError=exports.AuthenticationError=exports.ValidationError=exports.NotFoundError=exports.AppError=exports.ErrorUtils=exports.ERROR_TYPE_PROPERTIES=exports.isRetryableError=exports.errorToResponse=exports.isAppError=exports.createMcpProtocolError=exports.createFileOperationError=exports.createServiceError=exports.createDatabaseError=exports.createAuthorizationError=exports.createAuthenticationError=exports.createValidationError=exports.createNotFoundError=exports.createAppError=exports.createErrorResponse=void 0
|
|
1611
|
+
const o=t(8625)
|
|
1612
|
+
Object.defineProperty(exports,"createErrorResponse",{enumerable:!0,get(){return o.createErrorResponse}}),Object.defineProperty(exports,"createAppError",{enumerable:!0,get(){return o.createAppError}}),Object.defineProperty(exports,"createNotFoundError",{enumerable:!0,get(){return o.createNotFoundError}}),Object.defineProperty(exports,"createValidationError",{enumerable:!0,get(){return o.createValidationError}}),Object.defineProperty(exports,"createAuthenticationError",{enumerable:!0,get(){return o.createAuthenticationError}}),Object.defineProperty(exports,"createAuthorizationError",{enumerable:!0,get(){return o.createAuthorizationError}}),Object.defineProperty(exports,"createDatabaseError",{enumerable:!0,get(){return o.createDatabaseError}}),Object.defineProperty(exports,"createServiceError",{enumerable:!0,get(){return o.createServiceError}}),Object.defineProperty(exports,"createFileOperationError",{enumerable:!0,get(){return o.createFileOperationError}}),Object.defineProperty(exports,"createMcpProtocolError",{enumerable:!0,get(){return o.createMcpProtocolError}}),Object.defineProperty(exports,"isAppError",{enumerable:!0,get(){return o.isAppError}}),Object.defineProperty(exports,"errorToResponse",{enumerable:!0,get(){return o.errorToResponse}}),Object.defineProperty(exports,"isRetryableError",{enumerable:!0,get(){return o.isRetryableError}}),Object.defineProperty(exports,"ERROR_TYPE_PROPERTIES",{enumerable:!0,get(){return o.ERROR_TYPE_PROPERTIES}}),Object.defineProperty(exports,"ErrorUtils",{enumerable:!0,get(){return o.ErrorUtils}}),Object.defineProperty(exports,"AppError",{enumerable:!0,get(){}}),Object.defineProperty(exports,"NotFoundError",{enumerable:!0,get(){return o.NotFoundError}}),Object.defineProperty(exports,"ValidationError",{enumerable:!0,get(){return o.ValidationError}}),Object.defineProperty(exports,"AuthenticationError",{enumerable:!0,get(){return o.AuthenticationError}}),Object.defineProperty(exports,"AuthorizationError",{enumerable:!0,get(){return o.AuthorizationError}}),Object.defineProperty(exports,"DatabaseError",{enumerable:!0,get(){return o.DatabaseError}}),Object.defineProperty(exports,"ServiceError",{enumerable:!0,get(){return o.ServiceError}}),Object.defineProperty(exports,"FileOperationError",{enumerable:!0,get(){return o.FileOperationError}}),Object.defineProperty(exports,"McpProtocolError",{enumerable:!0,get(){return o.McpProtocolError}}),Object.defineProperty(exports,"UnauthorizedError",{enumerable:!0,get(){return o.UnauthorizedError}}),s(t(441),exports)},9896(module){"use strict"
|
|
1222
1613
|
module.exports=require("fs")}},n={}
|
|
1223
|
-
e.m=o,
|
|
1224
|
-
if("object"==typeof
|
|
1225
|
-
if(16&o&&"function"==typeof
|
|
1614
|
+
e.m=o,r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,e.t=function(s,o){if(1&o&&(s=this(s)),8&o)return s
|
|
1615
|
+
if("object"==typeof s&&s){if(4&o&&s.__esModule)return s
|
|
1616
|
+
if(16&o&&"function"==typeof s.then)return s}var n=Object.create(null)
|
|
1226
1617
|
e.r(n)
|
|
1227
1618
|
var i={}
|
|
1228
|
-
t=t||[null,
|
|
1229
|
-
for(var a=2&o&&
|
|
1230
|
-
return i.default=()=>
|
|
1231
|
-
for(var i in
|
|
1619
|
+
t=t||[null,r({}),r([]),r(r)]
|
|
1620
|
+
for(var a=2&o&&s;"object"==typeof a&&!~t.indexOf(a);a=r(a))Object.getOwnPropertyNames(a).forEach((e=>i[e]=()=>s[e]))
|
|
1621
|
+
return i.default=()=>s,e.d(n,i),n},e.d=(exports,t)=>{for(var r in t)e.o(t,r)&&!e.o(exports,r)&&Object.defineProperty(exports,r,{enumerable:!0,get:t[r]})},e.f={},e.e=t=>Promise.all(Object.keys(e.f).reduce(((r,s)=>(e.f[s](t,r),r)),[])),e.u=e=>e+".js",e.o=(e,t)=>({}.hasOwnProperty.call(e,t)),e.r=exports=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},s={27:1},e.f.require=t=>{s[t]||(t=>{var r=t.modules,o=t.ids,n=t.runtime
|
|
1622
|
+
for(var i in r)e.o(r,i)&&(e.m[i]=r[i])
|
|
1232
1623
|
n&&n(e)
|
|
1233
|
-
for(var a=0;a<o.length;a++)
|
|
1624
|
+
for(var a=0;a<o.length;a++)s[o[a]]=1})(require("./"+e.u(t)))}
|
|
1234
1625
|
var i={};(()=>{"use strict"
|
|
1235
1626
|
function t(){const e=[(0,h.join)(__dirname,"..","package.json"),(0,h.join)(__dirname,"..","..","package.json")]
|
|
1236
1627
|
for(const t of e)try{const e=JSON.parse((0,d.readFileSync)(t,"utf-8"))
|
|
1237
|
-
if(e.version)return e.version}catch{}return"unknown"}function
|
|
1238
|
-
console.log(`\nAlchemyLab CLI v${e}\n\nUSAGE:\n alchemy [command] [options]\n\nCOMMANDS:\n (none) Start action server + launch OpenCode (default)\n serve Start the action server only (for GUI-triggered actions)\n logout Log out and remove stored auth token\n\nOPTIONS:\n --port <n> Action server port (default: auto)\n --help, -h Show this help message\n --version, -v Show version information\n\nEXAMPLES:\n alchemy # Start server + OpenCode (default)\n alchemy serve # Start action server only\n alchemy logout # Log out\nFor more information, visit: https://alchemylabcode.com/\n`)}function
|
|
1239
|
-
return(0,d.existsSync)(t)}function o(e){const t=(0,p.platform)(),
|
|
1240
|
-
setTimeout((()=>{clearInterval(
|
|
1241
|
-
try{const{createConfigManager:
|
|
1242
|
-
return(await
|
|
1628
|
+
if(e.version)return e.version}catch{}return"unknown"}function r(){const e=t()
|
|
1629
|
+
console.log(`\nAlchemyLab CLI v${e}\n\nUSAGE:\n alchemy [command] [options]\n\nCOMMANDS:\n (none) Start action server + launch OpenCode (default)\n serve Start the action server only (for GUI-triggered actions)\n logout Log out and remove stored auth token\n\nOPTIONS:\n --port <n> Action server port (default: auto)\n --help, -h Show this help message\n --version, -v Show version information\n\nEXAMPLES:\n alchemy # Start server + OpenCode (default)\n alchemy serve # Start action server only\n alchemy logout # Log out\nFor more information, visit: https://alchemylabcode.com/\n`)}function s(e){const t=(0,h.join)(e,E.ALCHEMY_DIR_NAME,"project.json")
|
|
1630
|
+
return(0,d.existsSync)(t)}function o(e){const t=(0,p.platform)(),r="darwin"===t?"open":"win32"===t?"cmd":"xdg-open",s="win32"===t?["/c","start","",e]:[e];(0,g.spawn)(r,s,{detached:!0,stdio:"ignore"}).unref()}async function n(e,t=3e5){return new Promise((r=>{const s=setInterval((async()=>{await e.hasValidToken()&&(clearInterval(s),r(!0))}),2e3)
|
|
1631
|
+
setTimeout((()=>{clearInterval(s),r(!1)}),t)}))}async function a(t){if(process.env.REMOTE_SERVER_URL)return process.env.REMOTE_SERVER_URL
|
|
1632
|
+
try{const{createConfigManager:r}=await e.e(60).then(e.t.bind(e,9060,23)),s=r(t)
|
|
1633
|
+
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}})})
|
|
1243
1634
|
if(i.ok){const e=await i.json()
|
|
1244
|
-
return e.data?.id||e.id}
|
|
1245
|
-
const l=e(3444),d=e(9896),u=e(1943),h=e(6928),g=e(5317),p=e(857),
|
|
1635
|
+
return e.data?.id||e.id}s.info(`Project registration failed (HTTP ${i.status})`)}catch(e){s.info("Project registration failed",{error:(0,b.getErrorMessage)(e)})}}Object.defineProperty(i,"__esModule",{value:!0})
|
|
1636
|
+
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),b=e(8824),I=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))
|
|
1246
1637
|
const i=process.argv.slice(2)
|
|
1247
1638
|
let C
|
|
1248
|
-
const
|
|
1249
|
-
const
|
|
1250
|
-
if(0===
|
|
1639
|
+
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)))
|
|
1640
|
+
const k=i.filter(((e,t)=>"--port"!==e&&(-1===S||t!==S+1)))
|
|
1641
|
+
if(0===k.length)return void await async function(r={}){const i=process.cwd(),l=(0,h.join)(i,E.ALCHEMY_DIR_NAME)
|
|
1251
1642
|
await(0,u.mkdir)(l,{recursive:!0})
|
|
1252
|
-
const C=(0,h.join)(l,"server.log"),{createLogger:
|
|
1253
|
-
await
|
|
1254
|
-
let
|
|
1255
|
-
if(!
|
|
1256
|
-
const e=await
|
|
1257
|
-
console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log(""),console.log("\u{1f446} Browser window opened. Complete login there."),console.log("\u23f1\ufe0f Waiting for authentication..."),await n(
|
|
1258
|
-
await async function(e,t,
|
|
1643
|
+
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})
|
|
1644
|
+
await _.initialize()
|
|
1645
|
+
let j=await _.getToken()
|
|
1646
|
+
if(!j?.token){console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
|
|
1647
|
+
const e=await _.startAuthenticationFlow()
|
|
1648
|
+
console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log(""),console.log("\u{1f446} Browser window opened. Complete login there."),console.log("\u23f1\ufe0f Waiting for authentication..."),await n(_)||(console.error("\u274c Authentication timed out. Run `alchemy` again to retry."),process.exit(1)),j=await _.getToken(),console.log(`\u2705 Welcome, ${j?.username||"user"}!`),console.log("")}let A=j?.token
|
|
1649
|
+
await async function(e,t,r,o){if(s(e))return
|
|
1259
1650
|
console.log("\u{1f4c1} Initializing AlchemyLab project..."),console.log("")
|
|
1260
|
-
const n=(0,h.join)(e,
|
|
1651
|
+
const n=(0,h.join)(e,E.ALCHEMY_DIR_NAME)
|
|
1261
1652
|
let i
|
|
1262
|
-
await(0,u.mkdir)(n,{recursive:!0}),
|
|
1263
|
-
const a=(0,h.join)(n,"project.json");(0,d.writeFileSync)(a,JSON.stringify({id:i||null,name:(0,h.basename)(e)||"Local Project",status:"active",registeredRemotely:!!i,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:n}},null,2),"utf-8"),console.log("")}(i,
|
|
1264
|
-
const
|
|
1265
|
-
let
|
|
1266
|
-
try{const e=JSON.parse((0,d.readFileSync)(
|
|
1267
|
-
if(
|
|
1268
|
-
t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(
|
|
1269
|
-
try{const e=(0,h.join)(t,
|
|
1653
|
+
await(0,u.mkdir)(n,{recursive:!0}),r?(console.log(`\u{1f4cb} Registering project "${(0,h.basename)(e)||"Local Project"}"...`),i=await c(t,r,e,o),console.log(i?"\u2705 Project registered: "+i:"\u26a0\ufe0f Project registration failed (will retry next run)")):console.log("\u26a0\ufe0f No auth token \u2014 skipping project registration")
|
|
1654
|
+
const a=(0,h.join)(n,"project.json");(0,d.writeFileSync)(a,JSON.stringify({id:i||null,name:(0,h.basename)(e)||"Local Project",status:"active",registeredRemotely:!!i,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:n}},null,2),"utf-8"),console.log("")}(i,T,A,k)
|
|
1655
|
+
const P=(0,h.join)(i,E.ALCHEMY_DIR_NAME,"project.json")
|
|
1656
|
+
let M
|
|
1657
|
+
try{const e=JSON.parse((0,d.readFileSync)(P,"utf-8"))
|
|
1658
|
+
if(M=e.id||void 0,!1===e.registeredRemotely&&A){const t=await c(T,A,i,k,"cli-init-retry")
|
|
1659
|
+
t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(P,JSON.stringify(e,null,2),"utf-8"),M=t,console.log("\u2705 Project registered: "+t))}}catch{k.warn("Could not read project.json")}await async function(t){try{return void(0,g.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{}let r="latest"
|
|
1660
|
+
try{const e=(0,h.join)(t,E.ALCHEMY_DIR_NAME,"config.json")
|
|
1270
1661
|
if((0,d.existsSync)(e)){const t=JSON.parse((0,d.readFileSync)(e,"utf-8"))
|
|
1271
|
-
t.opencodeVersion&&(
|
|
1272
|
-
const
|
|
1273
|
-
try{(0,g.execFileSync)("which",["go"],{stdio:"pipe"}),console.log(" Running: go install github.com/sst/opencode@"+
|
|
1274
|
-
try{return(0,g.execFileSync)("which",["opencode"],{stdio:"pipe"}),void console.log("\u2705 OpenCode installed successfully via go install")}catch{console.log("\u26a0\ufe0f go install completed but opencode not found in PATH"),console.log(" Ensure $(go env GOPATH)/bin is in your PATH")}}catch{}try{const{platform:t,arch:
|
|
1275
|
-
if(i&&a){const e=`opencode_${i}_${a}`,t="latest"===
|
|
1662
|
+
t.opencodeVersion&&(r=t.opencodeVersion)}}catch{}console.log(""),console.log("\u{1f4e6} OpenCode not found in PATH. Installing...")
|
|
1663
|
+
const s="latest"===r?"latest":"v"+r
|
|
1664
|
+
try{(0,g.execFileSync)("which",["go"],{stdio:"pipe"}),console.log(" Running: go install github.com/sst/opencode@"+s),(0,g.execFileSync)("go",["install","github.com/sst/opencode@"+s],{stdio:"inherit",timeout:12e4})
|
|
1665
|
+
try{return(0,g.execFileSync)("which",["opencode"],{stdio:"pipe"}),void console.log("\u2705 OpenCode installed successfully via go install")}catch{console.log("\u26a0\ufe0f go install completed but opencode not found in PATH"),console.log(" Ensure $(go env GOPATH)/bin is in your PATH")}}catch{}try{const{platform:t,arch:s}=await Promise.resolve().then(e.t.bind(e,857,23)),o=t(),n=s(),i="darwin"===o?"darwin":"linux"===o?"linux":null,a="arm64"===n?"arm64":"x64"===n?"amd64":null
|
|
1666
|
+
if(i&&a){const e=`opencode_${i}_${a}`,t="latest"===r?"https://github.com/sst/opencode/releases/latest/download/"+e:`https://github.com/sst/opencode/releases/download/v${r}/${e}`
|
|
1276
1667
|
console.log(" Downloading from: "+t)
|
|
1277
|
-
const
|
|
1278
|
-
await(0,u.mkdir)(
|
|
1279
|
-
const o=(0,h.join)(
|
|
1280
|
-
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(
|
|
1668
|
+
const s=(0,h.join)((0,p.homedir)(),".local","bin")
|
|
1669
|
+
await(0,u.mkdir)(s,{recursive:!0})
|
|
1670
|
+
const o=(0,h.join)(s,"opencode")
|
|
1671
|
+
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,b.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)
|
|
1281
1672
|
const O=t()
|
|
1282
1673
|
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)
|
|
1283
|
-
const
|
|
1284
|
-
try{const t=(0,v.getInstallContext)(),
|
|
1285
|
-
if(
|
|
1286
|
-
try{const e=await fetch(
|
|
1674
|
+
const R=r.port||await(0,I.findAvailablePort)(3456),D=new y.AlchemyActionServer(k,{projectPath:i,remoteServerUrl:T,port:R,authToken:A,tokenManager:_})
|
|
1675
|
+
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
|
|
1676
|
+
if(T.includes("api.alchemylabcode.com"))return"https://alchemylabcode.com"
|
|
1677
|
+
try{const e=await fetch(T+"/health")
|
|
1287
1678
|
if(e.ok){const t=await e.json()
|
|
1288
|
-
if(t?.data?.frontendUrl)return t.data.frontendUrl}}catch{}return
|
|
1289
|
-
if(console.log(""),console.log(" Action server .... port "
|
|
1290
|
-
const e=(0,h.join)(i,
|
|
1679
|
+
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,b.getErrorMessage)(e)}),{success:!1,error:(0,b.getErrorMessage)(e)})))])
|
|
1680
|
+
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)
|
|
1681
|
+
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)")
|
|
1291
1682
|
console.log(""),console.log(` \u2192 Alchemy UI: \x1b]8;;${a}\x07${a}\x1b]8;;\x07`),console.log("")
|
|
1292
1683
|
let l=!1,u=null
|
|
1293
|
-
const g=async()=>{l||(l=!0,u&&clearInterval(u),console.log(""),console.log("Shutting down..."),await
|
|
1684
|
+
const g=async()=>{l||(l=!0,u&&clearInterval(u),console.log(""),console.log("Shutting down..."),await D.stop(),process.exit(0))}
|
|
1294
1685
|
process.on("SIGTERM",g),process.on("SIGINT",g)
|
|
1295
|
-
const{InteractiveShell:p}=await e.e(994).then(e.t.bind(e,5994,23)),
|
|
1686
|
+
const{InteractiveShell:p}=await e.e(994).then(e.t.bind(e,5994,23)),m=new p({projectPath:i,logger:k,remoteServerUrl:T,authToken:A,projectId:M})
|
|
1296
1687
|
let y=!1
|
|
1297
|
-
u=setInterval((async()=>{if(!y)try{if(await
|
|
1688
|
+
u=setInterval((async()=>{if(!y)try{if(await _.hasValidToken())return
|
|
1298
1689
|
y=!0,console.log(""),console.log("\u26a0\ufe0f Auth token expired. Opening browser to re-authenticate...")
|
|
1299
|
-
const e=await
|
|
1300
|
-
if(console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(
|
|
1301
|
-
e?.token&&(
|
|
1302
|
-
const
|
|
1303
|
-
switch("--help"!==
|
|
1304
|
-
r
|
|
1305
|
-
const o=(0,l.createEnvLogger)({serviceName:"alchemy-server",console:!0,baseDir:
|
|
1306
|
-
console.log(`\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551 AlchemyLab Server v${n.padEnd(10)} \u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n`),console.log("\u{1f4c1} Project: "+
|
|
1307
|
-
const i=await a(o),c=new
|
|
1690
|
+
const e=await _.startAuthenticationFlow()
|
|
1691
|
+
if(console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(_)){const e=await _.getToken()
|
|
1692
|
+
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,b.getErrorMessage)(e)})}finally{y=!1}}),3e5),await m.start(g)}catch(e){k.error("Failed to start server",{error:(0,b.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,b.getErrorMessage)(e)),process.exit(1)}}({port:C})
|
|
1693
|
+
const T=k[0].toLowerCase()
|
|
1694
|
+
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()
|
|
1695
|
+
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))
|
|
1696
|
+
const o=(0,l.createEnvLogger)({serviceName:"alchemy-server",console:!0,baseDir:r}),n=t()
|
|
1697
|
+
console.log(`\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551 AlchemyLab Server v${n.padEnd(10)} \u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n`),console.log("\u{1f4c1} Project: "+r)
|
|
1698
|
+
const i=await a(o),c=new m.TokenManager(o,r,{remoteServerUrl:i,allowInteractiveAuth:!1})
|
|
1308
1699
|
await c.initialize()
|
|
1309
1700
|
const d=await c.getToken(),u=d?.token
|
|
1310
1701
|
u||console.log("\u26a0\ufe0f No auth token. Some features may be limited. Run `alchemy` to authenticate.")
|
|
1311
|
-
const h=e.port||await(0,
|
|
1702
|
+
const h=e.port||await(0,I.findAvailablePort)(3456),g=new y.AlchemyActionServer(o,{projectPath:r,remoteServerUrl:i,port:h,authToken:u,tokenManager:c})
|
|
1312
1703
|
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("")
|
|
1313
1704
|
let e=!1
|
|
1314
1705
|
const t=async()=>{e||(e=!0,console.log(""),console.log("Shutting down action server..."),await g.stop(),process.exit(0))}
|
|
1315
|
-
process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){o.error("Failed to start server",{error:(0,
|
|
1706
|
+
process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){o.error("Failed to start server",{error:(0,b.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,b.getErrorMessage)(e)),process.exit(1)}}({port:C})
|
|
1316
1707
|
break
|
|
1317
|
-
case"logout":await async function(){const e=process.cwd(),t=(0,l.createEnvLogger)({serviceName:"alchemy-cli",console:!1,baseDir:e}),
|
|
1318
|
-
await
|
|
1708
|
+
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})
|
|
1709
|
+
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.")}()
|
|
1319
1710
|
break
|
|
1320
|
-
default:console.error("\u274c Unknown command: "+
|
|
1711
|
+
default:console.error("\u274c Unknown command: "+T),console.log(""),r(),process.exit(1)}})().catch((e=>{console.error("\u{1f4a5} Unexpected error:",e),process.exit(1)}))})(),module.exports=i})()
|