alchemylab 3.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +177 -180
- package/dist/http-server.js +34 -32
- package/dist/index.js +34 -32
- package/dist/mcp-direct.js +34 -32
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -641,12 +641,12 @@ if(!t||!r)continue
|
|
|
641
641
|
const n=`alchemy-pv-${c.id}-${t.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,o=e.service&&String(e.service).trim()
|
|
642
642
|
await s(n,!o),o?W.push({volume:n,target:r,service:o}):H.push({volume:n,target:r})}O.hostExec||(0,$.sweepBoxGit)((0,k.workDir)(e.projectPath,"box-git"))
|
|
643
643
|
const G=()=>O.hostExec?new x.HostExperiment({worktreePath:q,sandbox:O.hostExec,seed:P.seed,env:w.connEnv&&"object"==typeof w.connEnv?w.connEnv:void 0}):new I.ConfinedExperiment(q,P,{projectName:`alchemy-exp-${D}`,projectId:e.projectId,readOnlySource:!N,boxGitBaseDir:(0,k.workDir)(e.projectPath,"box-git"),egress:O.egress,egressAllowlist:Array.isArray(w.egressAllowlist)?w.egressAllowlist:void 0,boxMounts:H.length?H:void 0,serviceMounts:W.length?W:void 0,session:{memory:B.memory,cpus:B.cpus,size:(0,p.boxSize)(t.size)??(0,p.boxSize)(B.size),pidsLimit:B.pidsLimit,scratchSizeMb:B.scratchMb,shmSizeMb:B.shmMb,addCaps:O.allowCaps&&Array.isArray(w.addCaps)?w.addCaps:void 0,sysctls:O.allowCaps&&w.sysctls&&"object"==typeof w.sysctls?w.sysctls:void 0,runAsRoot:O.allowRoot&&!0===w.runAsRoot||O.allowCaps&&Array.isArray(w.addCaps)&&w.addCaps.length>0||O.allowDockerSocket&&!0===w.dockerSocket,dockerSocket:O.allowDockerSocket&&!0===w.dockerSocket}}),K={"alchemy.purpose":"experiment","alchemy.session":t.sessionId,"alchemy.request":t.requestId,"alchemy.environment":c?.name}
|
|
644
|
-
let
|
|
645
|
-
if(!
|
|
646
|
-
const Y=c?.id?String(c.id):d?(0,u.defaultBoxIdentity)({image:w.image,install:P.install,artifacts:P.artifacts}):void 0,J=Array.from(new Set((0,_.resolveArtifacts)(P).flatMap(e=>e.manifests??[]))),X=Y&&!N?{box:Y,manifests:J,depsFrom:await(0,h.computeFingerprint)(q,J)}:void 0,Q=["policy","agent","human"],Z=Array.isArray(w.commandGate)?w.commandGate.filter(e=>Q.includes(e)):O.commandGate,ee=o.register({projectId:e.projectId,sessionId:t.sessionId,environmentId:c?.id,environmentName:c?.name,commandGate:Z,confinement:w.confinement||"confined",...!N&&t.requestId?{liveWorktree:{path:q,requestId:String(t.requestId),...X?{proves:X}:{}}}:{},...N&&L?{snapshot:{path:q,branch:L}}:{},exp:
|
|
644
|
+
let z=G(),V=await(0,C.withContainerLabels)(K,()=>z.start())
|
|
645
|
+
if(!V.ok&&d&&(P.install||P.artifacts?.length)&&(e.logger?.warn?.("experiment: the default install failed; opening the box without it",{error:V.error}),P.install=void 0,P.artifacts=void 0,z=G(),V=await(0,C.withContainerLabels)(K,()=>z.start())),!V.ok)throw N&&await r(e.projectPath,q),new Error(V.error||"experiment failed to start")
|
|
646
|
+
const Y=c?.id?String(c.id):d?(0,u.defaultBoxIdentity)({image:w.image,install:P.install,artifacts:P.artifacts}):void 0,J=Array.from(new Set((0,_.resolveArtifacts)(P).flatMap(e=>e.manifests??[]))),X=Y&&!N?{box:Y,manifests:J,depsFrom:await(0,h.computeFingerprint)(q,J)}:void 0,Q=["policy","agent","human"],Z=Array.isArray(w.commandGate)?w.commandGate.filter(e=>Q.includes(e)):O.commandGate,ee=o.register({projectId:e.projectId,sessionId:t.sessionId,environmentId:c?.id,environmentName:c?.name,commandGate:Z,confinement:w.confinement||"confined",...!N&&t.requestId?{liveWorktree:{path:q,requestId:String(t.requestId),...X?{proves:X}:{}}}:{},...N&&L?{snapshot:{path:q,branch:L}}:{},exp:z,onDispose:async()=>{try{const r=z.getBlockedEgress,s="function"==typeof r?r.call(z):[],n=z.getPrepareEgress,o="function"==typeof n?n.call(z):[],i=[...s.map(e=>({host:e.host,method:e.method,ts:e.t,phase:"run",verdict:"blocked"})),...o.map(e=>({host:e.host,method:e.method,ts:e.t,phase:"prepare",verdict:e.allowed?"observed":"observed-would-block"}))]
|
|
647
647
|
i.length&&(await e.apiClient.post(`/api/projects/${e.projectId}/egress-attempts`,{sessionId:t.sessionId,environmentId:c?.id,confinement:w.confinement||"confined",attempts:i}).catch(t=>e.logger?.warn?.("egress-attempts persist failed",{error:t?.message})),o.length&&e.logger?.info?.("prepare-phase egress observed",{environmentName:c?.name,hosts:o.map(e=>e.host)}))}catch{}N&&await r(e.projectPath,q)}})
|
|
648
648
|
if(!O.hostExec){const e=(0,A.previewSelfBaseUrl)()
|
|
649
|
-
e&&(0,A.registerPreviewHandle)(ee.handle,`${e}/api/experiments/${ee.handle}/live-frame`,c?.name||void 0,{awaitingFrame:!0})}return b({handle:ee.handle,environmentId:c?.id,environmentName:c?.name,experimentName:i?.name,image:w.image||"node:20-alpine",hasServices:!!w.composeFile,workdir:O.hostExec?q:"/work",hostTier:!!O.hostExec,hasGit:!O.hostExec&&!0===
|
|
649
|
+
e&&(0,A.registerPreviewHandle)(ee.handle,`${e}/api/experiments/${ee.handle}/live-frame`,c?.name||void 0,{awaitingFrame:!0})}return b({handle:ee.handle,environmentId:c?.id,environmentName:c?.name,experimentName:i?.name,image:w.image||"node:20-alpine",hasServices:!!w.composeFile,workdir:O.hostExec?q:"/work",hostTier:!!O.hostExec,hasGit:!O.hostExec&&!0===z.hasGit,...O.hostExec||N||!t.requestId?{}:{sourceWritable:!0},...O.hostExec||"boolean"!=typeof z.hasGitBinary?{}:{gitBinary:z.hasGitBinary}})}(e,t)
|
|
650
650
|
L.set(a,l)
|
|
651
651
|
try{return await l}finally{L.get(a)===l&&L.delete(a)}},exports.runGatedExec=o,exports.boxDestination=i,exports.insertResultIntoExperiment=async function(e){const r=(0,E.experimentRegistry)().use(e.handle)
|
|
652
652
|
if(!r){const t=(0,E.experimentRegistry)().whyGone(e.handle)
|
|
@@ -743,7 +743,8 @@ if(t){const r=await n(M,P,l,x)
|
|
|
743
743
|
if("settle"===(0,d.kindProfile)(D?.type).finalize){x.info("Empty branch on a drive request -- completing without merge",{requestId:l})
|
|
744
744
|
try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,{summary:"string"==typeof D?.completionSummary&&D.completionSummary.trim()||`The drive of ${l} finished.`,acceptanceEvidence:[{evidence:"Drive request: verification receipt lives in the completion summary; no code changes by design."}],mergeResult:{mergeCommitHash:"unknown",cleanedUp:!1,method:"drive-completion",diffStats:{additions:0,deletions:0}}})}catch(e){return{success:!1,message:`Drive completion failed: ${(0,f.getErrorMessage)(e)}`,error:(0,f.getErrorMessage)(e)}}return{success:!0,message:`Drive ${l} completed (empty branch, nothing to merge)`}}if("artifact-only"===r.shape){x.info("Empty branch with artifact-only work -- completing without merge",{requestId:l,detail:r.detail})
|
|
745
745
|
try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,{summary:`Finished ${l} with records only -- ${r.detail}. It changed no files, so it was completed with nothing new in your project.`,acceptanceEvidence:[{evidence:`Artifact-only request: ${r.detail}. Persisted outside git; the branch had no code commits to merge.`}],mergeResult:{mergeCommitHash:"unknown",cleanedUp:!1,method:"artifact-only-completion",diffStats:{additions:0,deletions:0}}})}catch(e){return{success:!1,message:`Artifact-only completion failed: ${(0,f.getErrorMessage)(e)}`,error:(0,f.getErrorMessage)(e)}}return(0,h.maybePublishReceiptOnMerge)(l),{success:!0,message:`Request ${l} completed without a merge: artifact-only work (${r.detail}), already persisted; the branch had no code commits.`,data:{artifactOnly:!0}}}if(e>0){const t=(0,m.alreadyInMainSummary)(l,B),r="string"==typeof D?.completionSummary?D.completionSummary.trim():""
|
|
746
|
-
try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,{summary:r?`${r}\n\n${t}`:t,acceptanceEvidence:[{evidence:`The branch requests/${l} has the same content as ${B}: its change is already there.`}],mergeResult:{mergeCommitHash:"unknown",cleanedUp:!1,method:"already-in-main",diffStats:{additions:0,deletions:0,filesChanged:0}}})}catch(e){return{success:!1,message:`Finishing ${l} as already in ${B} failed: ${(0,f.getErrorMessage)(e)}`,error:(0,f.getErrorMessage)(e)}}return x.info("Branch content-identical to the default branch -- finished as already in main",{requestId:l,aheadCount:e}),{success:!0,message:`${l} is finished: ${t}`,data:{alreadyInMain:!0}}}
|
|
746
|
+
try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,{summary:r?`${r}\n\n${t}`:t,acceptanceEvidence:[{evidence:`The branch requests/${l} has the same content as ${B}: its change is already there.`}],mergeResult:{mergeCommitHash:"unknown",cleanedUp:!1,method:"already-in-main",diffStats:{additions:0,deletions:0,filesChanged:0}}})}catch(e){return{success:!1,message:`Finishing ${l} as already in ${B} failed: ${(0,f.getErrorMessage)(e)}`,error:(0,f.getErrorMessage)(e)}}return x.info("Branch content-identical to the default branch -- finished as already in main",{requestId:l,aheadCount:e}),{success:!0,message:`${l} is finished: ${t}`,data:{alreadyInMain:!0}}}if(0===e&&"string"==typeof D?.roundOf&&D.roundOf){const e="string"==typeof D?.completionSummary?D.completionSummary.trim():""
|
|
747
|
+
try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,{summary:e||"Nothing was changed.",acceptanceEvidence:[{evidence:"This round changed nothing: its branch has no commit of its own and no record writes into the project."}],mergeResult:{mergeCommitHash:"unknown",cleanedUp:!1,method:"answer-only",diffStats:{additions:0,deletions:0,filesChanged:0}}})}catch(e){return{success:!1,message:`Finishing ${l} as an answer failed: ${(0,f.getErrorMessage)(e)}`,error:(0,f.getErrorMessage)(e)}}return x.info("A round that changed nothing -- finished as an answer",{requestId:l}),{success:!0,message:`${l} is finished: it changed nothing.`,data:{answerOnly:!0}}}return{success:!1,message:t,error:"Empty branch (nothing to merge)"}}}catch(e){x.warn("Could not compute ahead count; proceeding with merge",{requestId:l,error:(0,f.getErrorMessage)(e)})}let z,V,Y
|
|
747
748
|
{const e=await(0,S.runMergeQueueGate)({projectPath:R,requestId:l,branchName:F,defaultBranch:B,requestTitle:D?.title,criteria:D?.acceptanceCriteria??D?.acceptance_criteria,acceptEnvironmentGap:u,acceptFailingTests:T,testGateOff:(0,c.checksOff)(D),source:"finalize-request",logger:x,fetchRequestRuns:async()=>{const e=await M.get(`/api/projects/${P}/requests/${l}/test-runs`),t=e?.data??e??[]
|
|
748
749
|
return Array.isArray(t)?t:[]},fetchActiveProcesses:async()=>{const e=await M.get(`/api/projects/${P}/test-processes/active?forRequest=${encodeURIComponent(l)}`),t=e?.data??e??[]
|
|
749
750
|
return(Array.isArray(t)?t:[t]).filter(Boolean)},evaluateMainChange:async({landingSha:e})=>{const{mainChangeAtLanding:r}=await Promise.resolve().then(()=>t(69689))
|
|
@@ -756,11 +757,11 @@ return{success:!1,message:`${o(l)}, and its tests passed: one check of a criteri
|
|
|
756
757
|
if("error"===e.action)return{success:!1,message:`Merge queue check failed: ${e.message}`,error:e.message}
|
|
757
758
|
if("sent-back"===e.action)return await(0,I.bringMainIn)({projectPath:R,requestId:l,defaultBranch:B,logger:x}),{success:!1,refused:!0,message:`${o(l)}: ${e.summary}`,error:"Held: main's newer work broke a check written for its criteria",data:{sentBack:!0}}
|
|
758
759
|
if("review-held"===e.action){const t=await(0,I.holdForMainChangeReview)({projectPath:R,projectId:P,requestId:l,defaultBranch:B,touched:e.touched,logger:x,post:(e,t)=>M.post(e,t)})
|
|
759
|
-
return{success:!1,refused:!0,message:`${o(l,!0)}: ${e.summary} ${t.reviewing?"A reviewer is checking its criteria against that change; it is completed after that if nothing needs fixing.":"A review of that change could not be started; send it back to rework or sign off again."}`,error:"Held: a reviewer checks main's change against the criteria first",data:{reviewHeld:!0,reviewing:t.reviewing}}}"land"===e.action&&(
|
|
760
|
+
return{success:!1,refused:!0,message:`${o(l,!0)}: ${e.summary} ${t.reviewing?"A reviewer is checking its criteria against that change; it is completed after that if nothing needs fixing.":"A review of that change could not be started; send it back to rework or sign off again."}`,error:"Held: a reviewer checks main's change against the criteria first",data:{reviewHeld:!0,reviewing:t.reviewing}}}"land"===e.action&&(z=e.certifiedCommitSha)}try{V=(await L.raw(["merge-base",B,F])).trim()||void 0}catch{}const J=!1,X=z??(await L.revparse([F])).trim(),Q=await(0,v.findCommittedMarkers)(L,X)
|
|
760
761
|
if(!Q.determined||Q.files.length>0){const e=Q.files??[]
|
|
761
|
-
return{success:!1,message:Q.determined?`Cannot merge ${l}: committed conflict markers remain in ${e.join(", ")} (the branch was freshened while this merge was queued). Its worker must resolve them and finish again first.`:`Cannot merge ${l}: the conflict-marker check could not run on the landing commit. Nothing was merged.`,error:"Unresolved conflict markers",data:{hasConflicts:!0,markerFiles:e}}}if(
|
|
762
|
+
return{success:!1,message:Q.determined?`Cannot merge ${l}: committed conflict markers remain in ${e.join(", ")} (the branch was freshened while this merge was queued). Its worker must resolve them and finish again first.`:`Cannot merge ${l}: the conflict-marker check could not run on the landing commit. Nothing was merged.`,error:"Unresolved conflict markers",data:{hasConflicts:!0,markerFiles:e}}}if(z){const e=await(0,$.landCertifiedMerge)(R,{defaultBranch:B,mergeCommitSha:z})
|
|
762
763
|
if(!e.landed)return{success:!1,message:"overlap"===e.refusal||"busy"===e.refusal?`${o(l)}: ${e.reason}`:`${o(l)}: ${e.reason}. Try again, and its checks run again first.`,error:"Certified landing refused",data:{landingRefused:!0,reason:e.refusal,files:e.files??[]}}
|
|
763
|
-
Y=V
|
|
764
|
+
Y=z,V=await(0,g.firstParentSha)(L,z)??V,x.info("merge-queue: landed certified catch-up commit (tested tree == merged tree)",{requestId:l,mergeCommit:Y.substring(0,8)})}else{const t=D?.title?`Merge ${F}-${D.title}`:`Merge ${F}`,n=async e=>{const r=await(0,j.buildMergeCommit)(R,{defaultBranch:B,branchSha:e,message:t})
|
|
764
765
|
return r.ok?{built:r,landed:await(0,j.landOnDefaultBranch)(R,{defaultBranch:B,commitSha:r.commitSha,expectedHead:r.baseHead})}:{built:r,landed:null}}
|
|
765
766
|
let i=await n(X)
|
|
766
767
|
if(!i.built.ok&&"conflicts"===i.built.reason&&null===(0,d.kindProfile)(D?.type).worker){const e=(0,d.kindProfile)(D?.type).label
|
|
@@ -774,7 +775,7 @@ if(i=await n(e),!i.built.ok){const e="conflicts"===i.built.reason?`it still conf
|
|
|
774
775
|
return{success:!1,message:`Merge of ${l} failed after a clean reconcile: ${e}. Nothing was merged.`,error:e}}}}if(!i.built.ok){if("nothing-to-merge"===i.built.reason)return{success:!1,message:`${o(l)}: its work holds nothing the project does not already have.`,error:"Nothing to merge"}
|
|
775
776
|
const t="error"===i.built.reason?i.built.error:"the merge commit could not be built"
|
|
776
777
|
return r?.__rediscovered||(await L.branch()).all.includes(F)?{success:!1,message:`Merge failed: ${t}`,error:t}:(x.warn("finalize: the request branch disappeared mid-merge; re-deciding as branchless",{requestId:l,errorMsg:t}),await e({...r,__rediscovered:!0},s))}if(!i.landed||!i.landed.landed){const e=i.landed&&!i.landed.landed?i.landed:null
|
|
777
|
-
return{success:!1,message:`${o(l)}: ${e?.message??"its changes could not be put into the project"}`,error:"Landing refused",data:{landingRefused:!0,reason:e?.reason,files:e?.files??[]}}}Y=i.built.commitSha,x.info("Merge landed through the door",{requestId:l,mergeCommit:Y.substring(0,8),where:i.landed.where})}const Z=(Y?await(0,g.firstParentSha)(L,Y):void 0)??
|
|
778
|
+
return{success:!1,message:`${o(l)}: ${e?.message??"its changes could not be put into the project"}`,error:"Landing refused",data:{landingRefused:!0,reason:e?.reason,files:e?.files??[]}}}Y=i.built.commitSha,x.info("Merge landed through the door",{requestId:l,mergeCommit:Y.substring(0,8),where:i.landed.where})}const Z=(Y?await(0,g.firstParentSha)(L,Y):void 0)??V,ee=await(0,g.computeDiffStats)(L,Z,Y),te=Y?await(0,v.describeAdditiveAutoMerges)(L,Z,Y):null,re=Y&&(0,c.checksOff)(D)?await(0,A.landedOnTopOf)(R,Y,l).catch(()=>[]):[],se={summary:`Finalized request ${l}`,acceptanceEvidence:[{evidence:"Finalized via GUI after review"},...T?[{evidence:'The user answered the test-cap card with "merge anyway": this merged with the tests failing, waived for this merge.'}]:[],...u?[{evidence:"The user accepted an environment gap: the box could not run the checks, and this merged without them."}]:[],...te?[{evidence:`Parts of this merge were combined automatically and not reviewed together: ${te}`}]:[]],mergeResult:{mergeCommitHash:Y||"unknown",mergeBaseSha:V,cleanedUp:J,method:"action-server-merge",diffStats:ee,...re.length?{notCheckedWith:re}:{}}}
|
|
778
779
|
try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,se)}catch(e){const t=(0,f.getErrorMessage)(e)
|
|
779
780
|
return x.error("Merge landed but the server could not record it yet; the record rides this action's result",{requestId:l,mergeCommitHash:Y,error:t}),(0,$.clearMergeCandidate)(R,l).catch(()=>{}),{success:!1,message:`Code was merged (commit: ${Y?.substring(0,8)}) but server finalization failed: ${t}`,error:t,data:{mergeCommitHash:Y,partialSuccess:!0,finalizeCompletion:se}}}return(0,h.maybePublishReceiptOnMerge)(l),(0,$.clearMergeCandidate)(R,l).catch(()=>{}),{success:!0,message:`Successfully finalized request ${l}`,data:{mergeCommitHash:Y,branchDeleted:J,...te?{autoMergeNote:te}:{},...re.length?{notCheckedWith:re}:{}}}}catch(e){const t=(0,f.getErrorMessage)(e)
|
|
780
781
|
return x.error("finalize-request action failed",{requestId:l,error:t}),{success:!1,message:`Failed to finalize request: ${t}`,error:t}}finally{if(q&&!N)try{(0,k.releaseMergeLease)(R,l)}catch{}}}
|
|
@@ -1315,9 +1316,13 @@ if(!u.ok)return{advanced:!1,clean:!0,why:u.why}
|
|
|
1315
1316
|
try{const r=await e.apiClient.patch(`/api/projects/${e.projectId}/requests/${t.requestId}`,{reviewStatus:"completed",cliOrigin:{sessionId:t.coderSessionId},headSha:i,reviewRound:{...s.reviewed?{reviewed:s.reviewed}:{},clean:!0}})
|
|
1316
1317
|
if(r&&!1===r.success)return{advanced:!1,clean:!0,why:`the finish signal was refused: ${r.error??r.message??"no reason given"}`}}catch(e){return{advanced:!1,clean:!0,why:`the finish signal failed: ${(0,f.getErrorMessage)(e)}`}}return{advanced:!0,clean:!0}}function d(e){const t="DELIVERY: ESCALATE",r=e.map(e=>`${e.content?.summary??""}\n${e.content?.details??""}`).join("\n"),s=/^\(?\s*(none|n\/a|nothing|no items?|nothing to fix)\s*\)?\.?$/i,n=[],o=r.split("\n").map(e=>e.trim())
|
|
1317
1318
|
for(let e=0;e<o.length;e++)if(/^\**must_fix\**:?$/i.test(o[e]))for(let t=e+1;t<o.length&&!/^\**(nice_to_have|out_of_scope)\**:?$/i.test(o[t]);t++){const e=o[t].match(/^[-*]\s+(.+)$/)
|
|
1318
|
-
e&&!s.test(e[1])&&n.push(e[1])}
|
|
1319
|
-
|
|
1320
|
-
|
|
1319
|
+
e&&!s.test(e[1])&&n.push(e[1])}const i=/^[\s:\u2013-]*(?:(?:is|was|would be|are)\s+)?(?:not|never)\b|^[\s:\u2013-]*(?:isn'?t|wasn'?t|unnecessary)\b/i
|
|
1320
|
+
let a=-1
|
|
1321
|
+
for(let e=r.indexOf(t);e>=0;e=r.indexOf(t,e+1)){const t=r.slice(e+18).split("\n")[0]??""
|
|
1322
|
+
if(!i.test(t)){a=e
|
|
1323
|
+
break}}if(a>=0&&0===n.length)return{escalated:!0,rework:!1,reason:r.slice(a+18).replace(/^[\s:\u2013-]+/,"").split("\n").map(e=>e.trim()).find(Boolean)??"The delivery diverges from what was asked; the reviewer could not settle it."}
|
|
1324
|
+
const c=/^\W*Match\W*:\s*no\b[\s,.:;-]*(?:because\s*)?(.*)$/im.exec(r)
|
|
1325
|
+
return e.some(e=>"failure"===String(e.content?.reportStatus??""))||n.length>0||c?{escalated:!1,rework:!0,reason:n[0]??(c?.[1]?.trim()||(e[0]?.content?.summary??"").slice(0,300))}:{escalated:!1,rework:!1,reason:(e[0]?.content?.summary??"").slice(0,800)}}async function u(e,t,r,s,n,o){const i={skipped:r,...s?{reviewed:s}:{}}
|
|
1321
1326
|
let a=!1
|
|
1322
1327
|
if("no-rule"===r)try{const r=await e.apiClient.get(`/api/projects/${e.projectId}/requests/${t.requestId}`)
|
|
1323
1328
|
a=r.success&&"ready_for_review"===r.data?.status}catch{}if("no-rule"!==r||a){try{await e.apiClient.patch(`/api/projects/${e.projectId}/requests/${t.requestId}`,{reviewStatus:"completed",cliOrigin:{sessionId:t.coderSessionId},reviewRound:i})}catch(r){e.logger.error("review-fanout: finish signal failed (gate stays pending -- reconciler surfaces it)",{requestId:t.requestId,error:(0,f.getErrorMessage)(r)})}"nothing"===r&&o&&await(0,A.injectPromptToSession)(e,t.coderSessionId,o)}else await(0,A.injectPromptToSession)(e,t.coderSessionId,o)
|
|
@@ -1353,15 +1358,15 @@ return r&&U.push(e),c.info(r?"review-fanout: a reviewer ended its turn without a
|
|
|
1353
1358
|
q()
|
|
1354
1359
|
const W=Date.now(),G=P.filter(e=>B.missingSessionIds.includes(e.sessionId)).map(e=>e.label),K=o(P,U,B.missingSessionIds)
|
|
1355
1360
|
c.info("review-fanout: barrier fired",{requestId:a.requestId,reason:B.reason,reported:D.length-G.length,missing:G.length,...K.length?{silentEnds:K}:{}})
|
|
1356
|
-
const
|
|
1357
|
-
if(v){try{const{escalated:e,rework:r,reason:s}=d(
|
|
1358
|
-
0===
|
|
1361
|
+
const z=v?(H??[]).filter(e=>P.some(t=>t.sessionId===e.fromSessionId)):[]
|
|
1362
|
+
if(v){try{const{escalated:e,rework:r,reason:s}=d(z),n=P[0]?.sessionId
|
|
1363
|
+
0===z.length?(await t.apiClient.post(`/api/projects/${t.projectId}/requests/${a.requestId}/delivery-verdict`,{escalated:!0,reviewerSessionId:n,reason:"The delivery review did not report, so nothing looked at this request before merge."}),c.warn("review-fanout: DELIVERY did not report; escalated rather than merged",{requestId:a.requestId})):(await t.apiClient.post(`/api/projects/${t.projectId}/requests/${a.requestId}/delivery-verdict`,{escalated:e,rework:r,reviewerSessionId:n,reason:s.slice(0,800)}),c.info("review-fanout: DELIVERY verdict applied",{requestId:a.requestId,escalated:e,rework:r}))}catch(e){c.warn("review-fanout: DELIVERY verdict failed to apply",{requestId:a.requestId,error:(0,f.getErrorMessage)(e)})}return{success:!0,message:`review-fanout: delivery verdict applied for ${a.requestId}`,...K.length?{data:{silentEnds:K}}:{}}}let V=!1,Y=null
|
|
1359
1364
|
try{const e=await t.apiClient.get(`/api/projects/${t.projectId}/requests/${a.requestId}`)
|
|
1360
|
-
Y=e.success?e.data:null,
|
|
1365
|
+
Y=e.success?e.data:null,V="ready_for_review"===Y?.status}catch{}if(V){try{await t.apiClient.patch(`/api/projects/${t.projectId}/requests/${a.requestId}`,{reviewStatus:"completed",cliOrigin:{sessionId:a.coderSessionId},reviewRound:b?{reviewed:b}:{}}),t.logger.info("review-fanout: parked request -- barrier signaled the review finish",{requestId:a.requestId})}catch(e){t.logger.error("review-fanout: parked finish signal failed (gate stays pending -- reconciler surfaces it)",{requestId:a.requestId,error:(0,f.getErrorMessage)(e)})}let e=!0
|
|
1361
1366
|
try{const r=await t.apiClient.get(`/api/projects/${t.projectId}/requests/${a.requestId}`)
|
|
1362
1367
|
e=!r.success||"ready_for_review"===r.data?.status}catch{}e&&await(0,A.injectPromptToSession)(t,a.coderSessionId,(G.length>0?`The review round on ${a.requestId} closed; ${G.join(", ")} did not report`:`All reviewers have reported on ${a.requestId}`)+". The request is held at ready_for_review, so do NOT re-complete it -- the server evaluates the review gate and the remaining gates advance on their own. Read the findings for context; a human decides at the next gate.")}let J=!1,X=!1
|
|
1363
|
-
if(!
|
|
1364
|
-
J=e.clean,e.advanced?(X=!0,c.info("review-fanout: clean review, the request went on without a worker step",{requestId:a.requestId,reviewers:D.length,reviewed:b,msAfterBarrier:Date.now()-W})):(J&&c.info("review-fanout: clean review, but the completion does not stand as it was; the worker completes again",{requestId:a.requestId,why:e.why}),await(0,A.injectPromptToSession)(t,a.coderSessionId,i(D,G)))}return{success:!0,message:`Review fan-out ${B.reason}: ${D.length-G.length}/${D.length} reviewers reported${O>0?` -- WARNING: ${O} requested reviewer${1===O?"":"s"} failed to spawn and that coverage never ran`:""}${
|
|
1368
|
+
if(!V){const e=await l(t,a,{expectedSessionIds:N,missing:G.length,unspawned:O,reports:H??[],reviewed:b,request:Y})
|
|
1369
|
+
J=e.clean,e.advanced?(X=!0,c.info("review-fanout: clean review, the request went on without a worker step",{requestId:a.requestId,reviewers:D.length,reviewed:b,msAfterBarrier:Date.now()-W})):(J&&c.info("review-fanout: clean review, but the completion does not stand as it was; the worker completes again",{requestId:a.requestId,why:e.why}),await(0,A.injectPromptToSession)(t,a.coderSessionId,i(D,G)))}return{success:!0,message:`Review fan-out ${B.reason}: ${D.length-G.length}/${D.length} reviewers reported${O>0?` -- WARNING: ${O} requested reviewer${1===O?"":"s"} failed to spawn and that coverage never ran`:""}${V?" (held finish signaled)":""}${X?" (clean: went on without a worker step)":""}`,data:{reason:B.reason,reviewers:D,missing:G,parked:V,unspawned:O,...b?{reviewed:b}:{},...V?{}:{clean:J,cleanAdvance:X},...K.length?{silentEnds:K}:{}}}}
|
|
1365
1370
|
const f=t(58824),m=t(7202),g=t(87775),w=t(20979),y=t(35317),v=t(39023),b=t(79896),k=(0,v.promisify)(y.execFile),S=p(t(83807)),I=t(27478),$=t(95040),j=t(76982),E=t(27838),A=t(17039),C=t(53691),_=t(52232),T=t(82928),x=e=>Math.min(3e3,200*2**e)
|
|
1366
1371
|
exports.FILE_REPORT_ASK='You ended your turn without filing your report, so your review reached nobody: what you write as a reply is not read. File it now with the report tool: session_message_operations send with type "report", and your findings as the summary. Then stop.'},76555(e,exports,t){"use strict"
|
|
1367
1372
|
async function r(e){const t=(Array.isArray(e.results?.commands)?e.results.commands:[]).filter(e=>"failed"===e?.status&&!(0,R.isKeptCommand)(e))
|
|
@@ -1412,7 +1417,7 @@ s=e?.data??e}catch{e=!0
|
|
|
1412
1417
|
continue}const n=s?.status
|
|
1413
1418
|
if("passed"!==n&&("skipped"!==n||!0!==s?.results?.scopeSkipped)&&!(0,K.failsOnlyOnMain)(s))if((0,K.waitsOnMain)(s))e=!0
|
|
1414
1419
|
else{if("failed"===n||"error"===n||"skipped"===n)return"failed"
|
|
1415
|
-
e=!0}}return e?"pending":"green"},p=async e=>{if("failed"===e){try{(0,G.releaseMergeLease)(t,s)}catch{}try{await(0,K.clearMergeCandidate)(t,s)}catch{}try{const e=await(0,Q.resolveDefaultBranch)(t),r=await(0,
|
|
1420
|
+
e=!0}}return e?"pending":"green"},p=async e=>{if("failed"===e){try{(0,G.releaseMergeLease)(t,s)}catch{}try{await(0,K.clearMergeCandidate)(t,s)}catch{}try{const e=await(0,Q.resolveDefaultBranch)(t),r=await(0,z.reconcileBranchWithMain)({projectPath:t,requestId:s,branchName:n,defaultBranch:e})
|
|
1416
1421
|
a.info("merge-queue: catch-up failed -- ticket released, branch reconciled for rework",{requestId:s,reconcile:r.outcome})}catch(e){a.warn("merge-queue: post-failure branch reconcile failed (rework proceeds without it)",{requestId:s,error:(0,j.getErrorMessage)(e)})}}else if((0,G.renewMergeLease)(t,s,{ttlMs:G.MERGE_LEASE_LAND_GRACE_TTL_MS,phase:"catchup"}),a.info("merge-queue: catch-up green -- lease held for landing",{requestId:s}),c&&l?.finalize)try{await i.post(`/api/projects/${r}/actions`,{type:"finalize-request",payload:{requestId:s,source:"merge-queue-catchup",...l.signoff?{signoff:l.signoff}:{}}}),a.info("merge-queue: catch-up green -- follow-up finalize queued automatically",{requestId:s})}catch(e){a.warn("merge-queue: failed to queue follow-up finalize; lease held with landing grace, a manual finalize will land the certified commit",{requestId:s,error:(0,j.getErrorMessage)(e)})}}
|
|
1417
1422
|
if(!c){const e=await h()
|
|
1418
1423
|
return"pending"!==e&&await p(e),e}for(;Date.now()<u;){await new Promise(e=>setTimeout(e,d))
|
|
@@ -1450,7 +1455,7 @@ try{const se=await X.get(`/api/projects/${ee}/requests/${b}`)
|
|
|
1450
1455
|
if(!se.success||!se.data)return{success:!1,message:`Request ${b} not found`,error:"Request not found"}
|
|
1451
1456
|
const oe=se.data
|
|
1452
1457
|
if("main-came-in"===Y){if("ready_for_review"!==String(oe.status))return{success:!0,message:`${b} is ${oe.status}, not waiting for sign-off; the checks after main came in are not run`}
|
|
1453
|
-
const e=`requests/${b}`,t=await(0,
|
|
1458
|
+
const e=`requests/${b}`,t=await(0,z.conflictPending)(te,oe,e)
|
|
1454
1459
|
if(t)return{success:!0,message:`not run on ${b}: ${t}`}
|
|
1455
1460
|
const r=await(0,Q.resolveDefaultBranch)(te),s=await(0,Z.mainBroughtInSinceWork)(te,e,r).catch(()=>null)
|
|
1456
1461
|
if(!s)return{success:!0,message:`nothing came into ${b} from main after its work; the checks are not run again`}
|
|
@@ -1517,7 +1522,7 @@ h=e.data,Ae.push(h.id)}else{const e=await X.post(`/api/projects/${ee}/test-runs`
|
|
|
1517
1522
|
h=e.data,Ae.push(h.id)}}catch(e){J.error("Failed to get/create test run record",{requestId:b,processId:process.id,preCreatedId:p?.id,error:(0,j.getErrorMessage)(e)})
|
|
1518
1523
|
continue}let y,v=process.config?.environment,k=process.config?.environment??null
|
|
1519
1524
|
if(process.environmentId)try{const e=await X.get(`/api/projects/${ee}/environments/${process.environmentId}`),t=e?.data||e
|
|
1520
|
-
t?.config&&(y=t.name,k=t.config,v=(0,
|
|
1525
|
+
t?.config&&(y=t.name,k=t.config,v=(0,V.gateEnvironmentFromRow)(t.config,ee,e=>J.warn("tests gate: clamped elevated environment config",{testProcessId:process.id,environmentId:t.id,environmentName:t.name,clamped:e})))}catch(e){J.warn("Failed to resolve environment_id; using embedded config",{testProcessId:process.id,error:(0,j.getErrorMessage)(e)})}if("experiment"===process.kind){try{let e="(no environment configured for this experiment; the judge has no run evidence)",r=[]
|
|
1521
1526
|
const s=f(oe,J,b),n=s.commands,o=s.withVerify
|
|
1522
1527
|
if(v){const s=[...(0,_.buildCapabilityProbes)((0,_.probeableRuntimes)(n)),...process.config?.commands||[],...n],o=await(0,U.withContainerLabels)({"alchemy.purpose":"check","alchemy.request":b,"alchemy.environment":y,"alchemy.check":process.name},()=>(0,O.runTestsGate)(we,{commands:s,environment:v,setup:(0,N.setupOfEnvironment)(de,process.environmentId?String(process.environmentId):null)},{})),i=o.results?.commands||[]
|
|
1523
1528
|
try{const e=s.filter(e=>!0===e?.required||/^verify\[/.test(String(e?.name??""))).map(e=>String(e?.command??"").trim()).filter(e=>e.length>0).sort(),t=(0,A.createHash)("sha256").update(e.join("\n")).digest("hex")
|
|
@@ -1557,34 +1562,34 @@ let t=null
|
|
|
1557
1562
|
try{t=await(0,L.decideCatchUpReuse)({runs:await E(),excludeRunId:h.id,process,landingTree:Ee,fingerprint:{kind:"all",value:e},changedBetween:(0,L.gitChangedBetween)(te)})}catch(e){J.debug("catch-up reuse lookup failed; running the gate",{error:(0,j.getErrorMessage)(e)})}if(t?.reuse){const r=(0,L.catchUpReusedRun)(t,process.pathGlobs??[],Ee)
|
|
1558
1563
|
await X.patch(`/api/projects/${ee}/test-runs/${h.id}`,{status:"passed",results:r.results,executedFingerprint:e}),_e.push(h.id),J.info("Catch-up: main's newer work did not touch this check's paths; its earlier pass is reused",{testRunId:h.id,priorRunId:t.prior.id,processName:process.name,changedSincePass:t.changedPaths.length})
|
|
1559
1564
|
continue}t&&J.info("Catch-up: the check runs again",{testRunId:h.id,processName:process.name,why:t.why})}const T=Date.now(),x={"alchemy.purpose":"check","alchemy.request":b,"alchemy.environment":y,"alchemy.check":process.name},P=(0,D.splitGateCommands)(c),F=ae.some(e=>!i(e)&&Array.isArray(e?.config?.setup)&&e.config.setup.length>0),H=Array.isArray(process.config?.setup)?process.config.setup:[],G=w&&P.verify.length>0&&!!je&&!F&&!e.commandNames?.length&&!e.skipCommandNames?.length
|
|
1560
|
-
let
|
|
1565
|
+
let z,Y=!0,Z=null
|
|
1561
1566
|
if(G){let e=null
|
|
1562
1567
|
if(P.process.length>0){const t=(0,D.groupFingerprint)(P.process)
|
|
1563
1568
|
let r=null
|
|
1564
1569
|
try{r=(0,D.findProcessEvidence)(await E(),{excludeRunId:h.id,processId:process.id,testedTree:Ee,processFingerprint:t})}catch(e){J.debug("process-evidence lookup failed; running the test commands",{error:(0,j.getErrorMessage)(e)})}let s=null
|
|
1565
|
-
if(!r&&K&&Ee)try{s=await(0,L.decideCatchUpReuse)({runs:await E(),excludeRunId:h.id,process,landingTree:Ee,fingerprint:{kind:"process",value:t},changedBetween:(0,L.gitChangedBetween)(te)})}catch(e){J.debug("catch-up reuse lookup failed; running the test commands",{error:(0,j.getErrorMessage)(e)})}r?(e=(0,D.reusedProcessResult)(r,je),Y=!1,J.info("Tests gate: the test commands already passed at this commit; only the criteria checks run",{testRunId:h.id,priorRunId:r.id,processName:process.name,commitSha:je})):s?.reuse?(e=(0,L.catchUpReusedProcess)(s,process.pathGlobs??[]),Y=!1,J.info("Catch-up: main's newer work did not touch this check's paths; its test commands are reused, the criteria checks run",{testRunId:h.id,priorRunId:s.prior.id,processName:process.name})):(s&&J.info("Catch-up: the test commands run again",{testRunId:h.id,processName:process.name,why:s.why}),e=await(0,U.withContainerLabels)(x,()=>(0,O.runTestsGate)(we,{commands:P.process,environment:v,setup:H})))}if(e?.error)
|
|
1570
|
+
if(!r&&K&&Ee)try{s=await(0,L.decideCatchUpReuse)({runs:await E(),excludeRunId:h.id,process,landingTree:Ee,fingerprint:{kind:"process",value:t},changedBetween:(0,L.gitChangedBetween)(te)})}catch(e){J.debug("catch-up reuse lookup failed; running the test commands",{error:(0,j.getErrorMessage)(e)})}r?(e=(0,D.reusedProcessResult)(r,je),Y=!1,J.info("Tests gate: the test commands already passed at this commit; only the criteria checks run",{testRunId:h.id,priorRunId:r.id,processName:process.name,commitSha:je})):s?.reuse?(e=(0,L.catchUpReusedProcess)(s,process.pathGlobs??[]),Y=!1,J.info("Catch-up: main's newer work did not touch this check's paths; its test commands are reused, the criteria checks run",{testRunId:h.id,priorRunId:s.prior.id,processName:process.name})):(s&&J.info("Catch-up: the test commands run again",{testRunId:h.id,processName:process.name,why:s.why}),e=await(0,U.withContainerLabels)(x,()=>(0,O.runTestsGate)(we,{commands:P.process,environment:v,setup:H})))}if(e?.error)z=e
|
|
1566
1571
|
else{let t=we
|
|
1567
1572
|
try{t=await(0,D.prepareVerifyTree)(te,we,(await(0,I.simpleGit)(we).revparse(["HEAD"])).trim())}catch(e){J.warn("Tests gate: no clean checkout for the criteria checks; running them in the pool slot",{testRunId:h.id,error:(0,j.getErrorMessage)(e)})}Z=t
|
|
1568
1573
|
const r=je?await(0,q.takeProofResultsAt)(b,je):null,o=r&&(await(0,I.simpleGit)(t).revparse(["HEAD^{tree}"]).catch(()=>"")).trim()||null,{settled:i,left:a}=(0,D.replayedVerify)(P.verify,r,o)
|
|
1569
1574
|
i&&J.info("Tests gate: criteria checks the completion's replay settled at this commit are taken, not run again",{testRunId:h.id,taken:(i.results.commands??[]).length,left:a.length,commitSha:je})
|
|
1570
1575
|
const c=(0,M.verifyByEnvironment)(a,p)
|
|
1571
1576
|
let l=c.checkBox.length>0?await(0,U.withContainerLabels)({...x,"alchemy.check":`${process.name} (criteria)`},()=>(0,O.runTestsGate)(t,{commands:c.checkBox,environment:v,setup:H})):null
|
|
1572
|
-
for(const[e,r]of c.pinned){const o=await s(X,ee,e),i=o?o.id===String(process.environmentId??"")?H:(0,N.setupOfEnvironment)(de,o.id):[],a=o?await(0,U.withContainerLabels)({...x,"alchemy.check":`${process.name} (criteria, ${o.name})`},()=>(0,O.runTestsGate)(t,{commands:r,environment:(0,
|
|
1573
|
-
l=(0,D.joinVerifyResults)(l,a)}l=(0,D.joinVerifyResults)(l,i),
|
|
1577
|
+
for(const[e,r]of c.pinned){const o=await s(X,ee,e),i=o?o.id===String(process.environmentId??"")?H:(0,N.setupOfEnvironment)(de,o.id):[],a=o?await(0,U.withContainerLabels)({...x,"alchemy.check":`${process.name} (criteria, ${o.name})`},()=>(0,O.runTestsGate)(t,{commands:r,environment:(0,V.gateEnvironmentFromRow)(o.config,ee),setup:i})):n(e,r)
|
|
1578
|
+
l=(0,D.joinVerifyResults)(l,a)}l=(0,D.joinVerifyResults)(l,i),z=(0,D.mergeGroupResults)(e,l)}}else z=await(0,U.withContainerLabels)(x,()=>(0,O.runTestsGate)(we,{commands:c,environment:v,setup:H},{commandNames:e.commandNames,skipCommandNames:e.skipCommandNames}))
|
|
1574
1579
|
if(!G&&w&&je&&P.verify.length>0){const e=await(0,q.takeProofResultsAt)(b,je),t=(e?.settled??[]).filter(e=>!e.ok).map(e=>({command:e.command,exit:e.exit,output:e.output}))
|
|
1575
|
-
t.length>0&&(
|
|
1580
|
+
t.length>0&&(z=(0,D.withProofFailures)(z,P.verify,t),J.info("Tests gate: proofs the completion replay found failing fail this run too",{testRunId:h.id,failed:t.length,commitSha:je}))}const re=Y?await a(we,te,J):{screenshots:[],found:0}
|
|
1576
1581
|
if(Z&&Z!==we){const e=await a(Z,te,J)
|
|
1577
1582
|
re.screenshots=[...re.screenshots,...e.screenshots],re.found+=e.found}Y&&d(te,we,J)
|
|
1578
1583
|
const se=(0,B.mainChecksOf)([process])[0]
|
|
1579
|
-
let ie={...
|
|
1580
|
-
if(t&&(ie={...ie||{},scopeVerifyOnly:!0,summary:["Process commands out of scope for this change; ran criterion verify commands only.",ie?.summary].filter(Boolean).join(" \xb7 ")}),w){const e=m(
|
|
1584
|
+
let ie={...z.results||{},...S.length?{kept:S.map(e=>({requestId:e.requestId,criterionId:e.criterionId,text:String(e.text??"").slice(0,300),commands:e.commands.map(e=>String(e).slice(0,600))}))}:{},processFingerprint:(0,D.provenProcessFingerprint)(P.process,z.results?.commands),...Ee?{testedTree:Ee}:{},...se?{checkVersion:(0,B.checkVersionOf)(se),environmentVersion:(0,B.environmentVersionOf)(k)}:{}}
|
|
1585
|
+
if(t&&(ie={...ie||{},scopeVerifyOnly:!0,summary:["Process commands out of scope for this change; ran criterion verify commands only.",ie?.summary].filter(Boolean).join(" \xb7 ")}),w){const e=m(z.results?.commands||[],{checkName:String(process.name??""),checkToolchains:(0,M.toolchainsOf)((process.config?.commands??[]).map(e=>e?.command)),uncovered:new Map(g.filter(e=>!e.covered).map(e=>[(0,C.criterionShortKey)(String(p[e.index]?.id??""),e.index),e.needs]))})
|
|
1581
1586
|
e.length&&(ie={...ie||{},acceptanceCriteria:e})}if(re.screenshots.length)ie={...ie||{},screenshots:re.screenshots}
|
|
1582
1587
|
else if(re.found>0){const e=`${re.found} screenshot(s) found but failed to upload`
|
|
1583
|
-
ie={...ie||{},summary:[ie?.summary,e].filter(Boolean).join(" \xb7 ")}}const ce=o(
|
|
1584
|
-
if("failed"===
|
|
1588
|
+
ie={...ie||{},summary:[ie?.summary,e].filter(Boolean).join(" \xb7 ")}}const ce=o(z.error)??("failed"===z.status?await r({apiClient:X,projectId:ee,requestId:b,processId:String(process.id),gateCommands:c||[],results:z.results,repairing:(0,W.environmentRepairInFlight)(b)}).catch(()=>null):null),ue=(z.results?.commands??[]).some(e=>("failed"===e?.status||"error"===e?.status)&&!(0,R.isKeptCommand)(e))
|
|
1589
|
+
if("failed"===z.status&&ue&&!z.error&&!ce&&!z.results?.faultClass&&Y&&!t){const e=(0,B.mainChecksOf)([process])[0]
|
|
1585
1590
|
if(e){const t=(0,I.simpleGit)(te),r=K?.baseSha??le?.mainSha??(je?(await t.raw(["merge-base",await(0,Q.resolveDefaultBranch)(te),je]).catch(()=>"")).trim():"");/^[0-9a-f]{40}$/.test(r)&&(ie={...ie,mainBase:{mainSha:r,checkVersion:(0,B.checkVersionOf)(e)}})}}ce&&(ie={...ie,faultClass:void 0,...ce.results,commands:ie?.commands??[]}),Array.isArray(ie?.commands)&&(ie={...ie,commands:(0,_.withVerifyCommandLines)(ie.commands,c||[])})
|
|
1586
1591
|
const fe=new Set((ie?.commands||[]).map(e=>String(e?.name??""))),me=(c||[]).filter(e=>fe.has(String(e?.name??""))).filter(e=>!(0,R.isKeptCommand)(e)).filter(e=>!0===e?.required||/^verify\[/.test(String(e?.name??""))).map(e=>String(e?.command??"").trim()).filter(e=>e.length>0).sort(),ge=(0,A.createHash)("sha256").update(me.join("\n")).digest("hex")
|
|
1587
|
-
await X.patch(`/api/projects/${ee}/test-runs/${h.id}`,{status:ce?"skipped":
|
|
1592
|
+
await X.patch(`/api/projects/${ee}/test-runs/${h.id}`,{status:ce?"skipped":z.status,results:ie,executedFingerprint:ge}),_e.push(h.id),J.info("Deterministic Tests gate completed",{testRunId:h.id,processName:process.name,status:z.status,phaseMs:z.phaseMs,beforeGateMs:T-ne,totalMs:Date.now()-ne})}catch(e){J.error("Deterministic Tests gate errored",{testRunId:h.id,error:(0,j.getErrorMessage)(e)}),await X.patch(`/api/projects/${ee}/test-runs/${h.id}`,o((0,j.getErrorMessage)(e))??{status:"error",results:{commands:[],summary:`Tests gate error: ${(0,j.getErrorMessage)(e)}`,faultClass:"fail_environment"}}).catch(()=>{}),_e.push(h.id)}continue}const S=w(oe,process,h,ee,e.commandNames,e.skipCommandNames),$="auto-tester"
|
|
1588
1593
|
try{let e
|
|
1589
1594
|
const t=await(0,T.spawnActionAgent)({getDefaultModel:()=>c.cachedDefaultModel||void 0,agent:$,prompt:S,cwd:we,projectPath:te,projectId:ee,title:`[${b}] Test: ${process.name}`,requestId:b,parentSessionId:G,allowParallel:!0,apiClient:X,logger:J,trackChildPid:re,onSessionPrepared:t=>{e=t,Me.addSession(t)}})
|
|
1590
1595
|
if(!t.success)throw e&&Me.markSessionDone(e),new Error(t.error||"Spawn failed")
|
|
@@ -1595,7 +1600,7 @@ let Pe=null
|
|
|
1595
1600
|
if(K){const e={projectPath:te,projectId:ee,requestId:b,branchName:ie,testRunIds:Ae,apiClient:X,logger:J,resume:K.resume}
|
|
1596
1601
|
0===Ce.length?(Pe=await h({...e,watch:!1}),"pending"===Pe&&h({...e,watch:!0}).catch(e=>J.warn("merge-queue: catch-up watcher crashed; lease TTL will unblock the queue",{requestId:b,error:(0,j.getErrorMessage)(e)}))):(Pe="pending",h({...e,watch:!0}).catch(e=>J.warn("merge-queue: catch-up watcher crashed; lease TTL will unblock the queue",{requestId:b,error:(0,j.getErrorMessage)(e)})))}return{success:!0,message:`Started ${Ce.length+_e.length} test run(s) for request ${b}`,data:{testRunIds:Ae,agentsSpawned:Ce.length,processCount:ae.length,worktreePath:we,spawnedSessionIds:Te,...K?{catchUp:{mergeCommitSha:K.mergeCommitSha,settled:Pe}}:{}}}}catch(e){const t=(0,j.getErrorMessage)(e)
|
|
1597
1602
|
return J.error("run-tests action failed",{requestId:b,error:t}),{success:!1,message:`Failed to run tests: ${t}`,error:t}}}
|
|
1598
|
-
const y=t(64721),v=t(75453),b=t(44600),k=t(79896),S=t(16928),I=t(83807),$=t(63905),j=t(58824),E=t(7202),A=t(76982),C=t(65454),_=t(36160),T=t(28148),x=t(67842),M=t(29418),R=t(54380),P=t(60489),O=t(41666),D=t(79075),q=t(65856),N=t(73775),F=t(87780),L=t(49987),U=t(81618),B=t(80872),H=t(22604),W=t(77238),G=t(56509),K=t(87192),
|
|
1603
|
+
const y=t(64721),v=t(75453),b=t(44600),k=t(79896),S=t(16928),I=t(83807),$=t(63905),j=t(58824),E=t(7202),A=t(76982),C=t(65454),_=t(36160),T=t(28148),x=t(67842),M=t(29418),R=t(54380),P=t(60489),O=t(41666),D=t(79075),q=t(65856),N=t(73775),F=t(87780),L=t(49987),U=t(81618),B=t(80872),H=t(22604),W=t(77238),G=t(56509),K=t(87192),z=t(35510),V=t(96302),Y=t(14139),J=t(40673),X=t(27584),Q=t(52232),Z=t(82865),ee=t(63484),te="alchemy:visual-check"
|
|
1599
1604
|
exports.rP='results: { summary, commands: [ { name: "each drive and verify[<key>] step from the evidence", status: "passed"|"failed"|"skipped"|"error", output? } ], acceptanceCriteria: [ { key: "the [key] shown next to the criterion", criterion, status: "verified"|"unverified"|"unclear", evidence } ], faultClass?: "fail_code"|"fail_evidence"|"fail_environment", missingCapabilities?: string[] }'},64306(e,exports,t){"use strict"
|
|
1600
1605
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
1601
1606
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.setupAgentInWorktree=async function(e){const{projectPath:t,projectId:r,apiClient:a,logger:c,trackChildPid:l}=e
|
|
@@ -1890,7 +1895,7 @@ if(!n)return{success:!1,message:"requestId is required",error:"Missing payload"}
|
|
|
1890
1895
|
i.info("Starting start-drive action",{requestId:n,creatorSessionId:o})
|
|
1891
1896
|
try{const e=await a.get(`/api/projects/${c}/requests/${n}`)
|
|
1892
1897
|
if(!e.success||!e.data)return{success:!1,message:`Request ${n} not found`,error:"Request not found"}
|
|
1893
|
-
const r=e.data,u=Array.isArray(r.acceptanceCriteria)&&r.acceptanceCriteria.length>0?r.acceptanceCriteria.map(e=>`- ${e?.description??e}`).join("\n"):"(none authored -- treat the description as the claim list, and say so in the receipt)",h=`Drive ${n}.\n\
|
|
1898
|
+
const r=e.data,u=Array.isArray(r.acceptanceCriteria)&&r.acceptanceCriteria.length>0?r.acceptanceCriteria.map(e=>`- ${e?.description??e}`).join("\n"):"(none authored -- treat the description as the claim list, and say so in the receipt)",h=`Drive ${n}.\n\n${function(e,t){return t?`You own this. It is a message from the user on the page of ${t}, a drive that is completed; its record is there to read (get_request_information). If it asks something that record answers, the answer is the summary you complete with, and nothing is driven. If it asks for another pass, or the record does not answer it, drive what it asks through user doors, per your instructions, and complete with the receipt. Either way end by completing (request_operations complete, requestId: "${e}"). Do not call merge; the server settles the request on your completion.`:`You own this drive request. Its acceptance criteria are CLAIMS to verify against the running system through user doors, per your instructions. Verify each claim with exact evidence, file reports for divergences beyond the criteria, and end by completing the request (request_operations complete, requestId: "${e}") with the claim -> verdict -> one-line-evidence receipt. Do not call merge; the server settles the record after your completion.`}(n,r.roundOf)}\n\n## The request\n\nTitle: ${r.title??"(untitled)"}\n\nDescription:\n${r.description||"(no description)"}\n\nAcceptance criteria (the claims):\n${u}`,p=await(0,s.spawnActionAgent)({agent:"driver",prompt:h,cwd:l,projectPath:l,projectId:c,title:/^(drive|deploy check)\b/i.test(String(r.title??""))?`[${n}] ${r.title}`.trim():`[${n}] Drive: ${r.title??""}`.trim(),requestId:n,parentSessionId:o,allowParallel:!0,skipWorktree:!0,apiClient:a,logger:i,trackChildPid:d,getDefaultModel:()=>t.cachedDefaultModel??void 0})
|
|
1894
1899
|
return p.success?(i.info("Driver spawned",{requestId:n,sessionId:p.sessionId}),{success:!0,message:`Driver spawned for ${n} (session ${p.sessionId})`}):{success:!1,message:`Failed to spawn driver for ${n}`,error:p.error}}catch(e){const t=(0,r.getErrorMessage)(e)
|
|
1895
1900
|
return i.error("start-drive action failed",{requestId:n,error:t}),{success:!1,message:`Drive dispatch failed for ${n}`,error:t}}}
|
|
1896
1901
|
const r=t(58824),s=t(28148)},93503(e,exports,t){"use strict"
|
|
@@ -1982,7 +1987,7 @@ const v=T.get(s.requestId)
|
|
|
1982
1987
|
if(v)return{success:!0,message:`${s.requestId} starts on ${v} when its current turn ends`}
|
|
1983
1988
|
try{const e=async e=>{const t=await o.get(`/api/projects/${i}/requests/${e}`)
|
|
1984
1989
|
return(t?.data??t)?.type}
|
|
1985
|
-
t.projectPath&&await(0,_.defaultBranchHasCode)(t.projectPath,e)&&(await(0,_.reportProjectHasCode)(i,!0,(e,t)=>o.patch(e,t)),await o.post(`/api/projects/${i}/requests/${s.requestId}/test-setup-offer`,{...s.creatorSessionId?{creatorSessionId:s.creatorSessionId}:{}}))}catch(e){d.warn("test-check card could not be asked for (the start goes on)",{requestId:s.requestId,error:(0,S.getErrorMessage)(e)})}const b=await(u.openDuplicates??r)(t,w,s.requestId),k=await(u.requestRecord??n)(t,s.requestId,w),I=function(e,t,r,s){const n=[`Implement ${e.requestId} ("${t?.title??""}") and own its full lifecycle. It was registered via ${e.source??"a registration door"}.`,s?"Its record, as get_request_information shows it now, is at the end of this message: start from it, and read the request again only when you need what changed since. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise.":"Read it with get_request_information first. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise."
|
|
1990
|
+
t.projectPath&&await(0,_.defaultBranchHasCode)(t.projectPath,e)&&(await(0,_.reportProjectHasCode)(i,!0,(e,t)=>o.patch(e,t)),await o.post(`/api/projects/${i}/requests/${s.requestId}/test-setup-offer`,{...s.creatorSessionId?{creatorSessionId:s.creatorSessionId}:{}}))}catch(e){d.warn("test-check card could not be asked for (the start goes on)",{requestId:s.requestId,error:(0,S.getErrorMessage)(e)})}const b=await(u.openDuplicates??r)(t,w,s.requestId),k=await(u.requestRecord??n)(t,s.requestId,w),I=function(e,t,r,s){const n=[`Implement ${e.requestId} ("${t?.title??""}") and own its full lifecycle. It was registered via ${e.source??"a registration door"}.`,s?"Its record, as get_request_information shows it now, is at the end of this message: start from it, and read the request again only when you need what changed since. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise.":"Read it with get_request_information first. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise.",...t?.roundOf?[]:[`If its acceptance criteria ALREADY hold as the code stands, do no work: complete ${e.requestId} yourself as a verification-only completion, naming exactly which criteria hold and where. Nobody else closes it: a report saying so left req000210 in progress with nobody working it.`,'Otherwise build what it asks (its own choices go in an opening brief when the pickup briefing asks for one), run the relevant validation, and complete the request with evidence. A value the request states is the value: a version number, a label, a default. Do not bump or "improve" it (two coders raised a footer version the user had just set, 2026-09-07).']]
|
|
1986
1991
|
return r.length&&n.push(`Possible duplicates already open with the same title: ${r.join(", ")}. Check before doing the work twice; if one already covers it, cancel ${e.requestId} as superseded.`),e.userNote&&n.push(`From the user: ${e.userNote}`),e.rules?.length&&n.push(function(e){const t=e.filter(e=>"told"===e.enforcement),r=e.filter(e=>"told"!==e.enforcement),s=[...t.map(e=>`- [guidance:${e.id}] ${e.text}`),...r.map(e=>`- [guidance:${e.id}] (reviewed, ${e.severity}) ${e.text}`)]
|
|
1987
1992
|
return`The workflow's guidance for this work (the user's; where an item conflicts with the brief above, the guidance wins).${r.length?" A reviewer checks the reviewed ones by id at the review gate: 'block' ones must hold, 'advise' ones are expected.":""} When the user's own words make an exception to an item for this request, record it with guidance exception (its id as listed, their words, the scope they said); when the request contradicts an item and they have not said, guidance conflict, and follow the guidance until they answer.\n${s.join("\n")}`}(e.rules)),s&&n.push(`\n# ${e.requestId}: ${t?.title??""}\n${s}`),n.join("\n")}(s,w,b,k),j="string"==typeof s.worker&&s.worker.trim()||(0,p.kindProfile)(w?.type).worker||"worker",E=(0,m.spawnToolMask)({kind:w?.type,hasTestProcess:"boolean"==typeof s.hasTestProcess?s.hasTestProcess:void 0});(0,m.hasMask)(E)&&d.info("session routing: narrowing the worker tool surface for this request",{requestId:s.requestId,worker:j,off:Object.keys(E)})
|
|
1988
1993
|
const A={p:s,req:w,worker:j,startPrompt:I,toolMask:E,deps:u}
|
|
@@ -2020,7 +2025,7 @@ for(const e of t)e?.localId&&"draft"===e?.status&&"pending"===e?.intake?.status&
|
|
|
2020
2025
|
return s},exports.sessionsHeldByRefusal=function(e){return!!E&&e.length>0&&E.heldByRefusal(e)}
|
|
2021
2026
|
const l=t(87780),d=t(64331),u=t(43218),h=t(4234),p=t(4234),f=t(60721),m=t(86116),g=t(58824),w=t(36799),y=t(4334),v=new Set(["waiting","blocksEverySession","userQuestionCodes","openCardCodes"]),b=["waitingOnSessions","pendingQuestionSessions","monitorArmedSessions","handedOverSessions"],k=new Set(["opening-brief","stalled-agent"]),S=18e5,I=new Set(["busy","running"]),$=["auto-tester","acceptance-judge"],j=["reviewer","criteria-reviewer","guidance-judge","command-judge","promise-judge","research"]
|
|
2022
2027
|
let E=null
|
|
2023
|
-
class A{heldByRefusal(e){return e.some(e=>this.refused.has(e))}constructor(e){this.settledAt=new Map,this.lastStrandedSweepAt=0,this.stallChecked=new Map,this.settledSkip=new Map,this.unconfused=new Map,this.attestedAt=new Map,this.attestReported=new Set,this.reportedKeys=new Set,this.frozenNudges=0,this.lastFrozenAction=null,this.orphanRetries=new Map,this.resumedRecently=new Set,this.capStopped=new Map,this.refused=new Map,this.judgeNow=new Map,this.resumedOn=new Map,this.liftTold=new Set,this.liftedWhy=new Map,this.checking=!1,this.recheckRequested=!1,this.stuckSessions=new Map,this.notifiedSessions=new Set,this.testInjectedIds=new Set,this.serveStartedAt=0,this.processServeStartedAt=0,this.agentListCache=null,this.lastResumeLift=new Map,E=this,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??3e5,this.maxRetries=e.maxRetries??3,this.getDefaultModel=e.getDefaultModel,this.reloadServe=e.reloadServe,this.getLiveRequestLocalIds=e.getLiveRequestLocalIds,this.getOwnedRequestForSession=e.getOwnedRequestForSession,this.orphanThresholdMs=e.orphanThresholdMs??12e4,this.maxOrphanRetries=e.maxOrphanRetries??2,this.getSessionAgentType=e.getSessionAgentType,this.getPendingQuestionAskerIds=e.getPendingQuestionAskerIds,this.getSessionSettled=e.getSessionSettled,this.reportLiveness=e.reportLiveness,this.reportFleetHealth=e.reportFleetHealth,this.recordProviderBlock=e.recordProviderBlock,this.clearCapStopped=e.clearCapStopped,this.deliver=e.deliver,this.raiseStalledAsk=e.raiseStalledAsk,this.closeStalledAsk=e.closeStalledAsk,this.frozenTurnThresholdMs=e.frozenTurnThresholdMs??6e5,this.abortSettleMs=e.abortSettleMs??1e3,this.hasLiveCommand=e.hasLiveCommand,this.stalledWorkThresholdMs=e.stalledWorkThresholdMs??24e4,this.getRequestWaitState=e.getRequestWaitState,this.getStrandedCandidates=e.getStrandedCandidates,this.fetchRecentReportDedupKeys=e.fetchRecentReportDedupKeys,this.getAgentModel=e.getAgentModel,this.isAgentModelPinned=e.isAgentModelPinned,this.resumeHubTurn=e.resumeHubTurn}turnFailed(e,t){e&&(0,h.isFatalTurnError)(t)&&(this.judgeNow.set(e,0),setTimeout(()=>{this.check().catch(()=>{})},1500).unref?.())}start(){this.intervalTimer||(this.logger.info("Stuck session watchdog starting",{checkInterval:this.checkIntervalMs,stuckThreshold:this.stuckThresholdMs,maxRetries:this.maxRetries}),this.seedReportDedup(),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.orphanRetries.clear(),this.resumedRecently.clear(),this.stallChecked.clear(),this.settledSkip.clear(),this.unconfused.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"}
|
|
2028
|
+
class A{heldByRefusal(e){return e.some(e=>this.refused.has(e))}constructor(e){this.settledAt=new Map,this.lastStrandedSweepAt=0,this.stallChecked=new Map,this.settledSkip=new Map,this.unconfused=new Map,this.attestedAt=new Map,this.attestReported=new Set,this.reportedKeys=new Set,this.frozenNudges=0,this.lastFrozenAction=null,this.orphanRetries=new Map,this.resumedRecently=new Set,this.capStopped=new Map,this.refused=new Map,this.judgeNow=new Map,this.resumedOn=new Map,this.liftTold=new Set,this.liftedWhy=new Map,this.checking=!1,this.recheckRequested=!1,this.stuckSessions=new Map,this.notifiedSessions=new Set,this.testInjectedIds=new Set,this.serveStartedAt=0,this.processServeStartedAt=0,this.agentListCache=null,this.lastResumeLift=new Map,E=this,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??3e5,this.maxRetries=e.maxRetries??3,this.getDefaultModel=e.getDefaultModel,this.reloadServe=e.reloadServe,this.getLiveRequestLocalIds=e.getLiveRequestLocalIds,this.getOwnedRequestForSession=e.getOwnedRequestForSession,this.orphanThresholdMs=e.orphanThresholdMs??12e4,this.maxOrphanRetries=e.maxOrphanRetries??2,this.getSessionAgentType=e.getSessionAgentType,this.getPendingQuestionAskerIds=e.getPendingQuestionAskerIds,this.getSessionSettled=e.getSessionSettled,this.reportLiveness=e.reportLiveness,this.reportFleetHealth=e.reportFleetHealth,this.recordProviderBlock=e.recordProviderBlock,this.clearCapStopped=e.clearCapStopped,this.deliver=e.deliver,this.raiseStalledAsk=e.raiseStalledAsk,this.closeStalledAsk=e.closeStalledAsk,this.frozenTurnThresholdMs=e.frozenTurnThresholdMs??6e5,this.abortSettleMs=e.abortSettleMs??1e3,this.hasLiveCommand=e.hasLiveCommand,this.stalledWorkThresholdMs=e.stalledWorkThresholdMs??24e4,this.awaitedReplyThresholdMs=e.awaitedReplyThresholdMs??12e4,this.isAwaitedReply=e.isAwaitedReply??m.isWatchingInboxChatTurn,this.getRequestWaitState=e.getRequestWaitState,this.getStrandedCandidates=e.getStrandedCandidates,this.fetchRecentReportDedupKeys=e.fetchRecentReportDedupKeys,this.getAgentModel=e.getAgentModel,this.isAgentModelPinned=e.isAgentModelPinned,this.resumeHubTurn=e.resumeHubTurn}turnFailed(e,t){e&&(0,h.isFatalTurnError)(t)&&(this.judgeNow.set(e,0),setTimeout(()=>{this.check().catch(()=>{})},1500).unref?.())}start(){this.intervalTimer||(this.logger.info("Stuck session watchdog starting",{checkInterval:this.checkIntervalMs,stuckThreshold:this.stuckThresholdMs,maxRetries:this.maxRetries}),this.seedReportDedup(),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.orphanRetries.clear(),this.resumedRecently.clear(),this.stallChecked.clear(),this.settledSkip.clear(),this.unconfused.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"}
|
|
2024
2029
|
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(){if(this.checking)this.recheckRequested=!0
|
|
2025
2030
|
else{this.checking=!0
|
|
2026
2031
|
try{await this.checkOnce()}finally{this.checking=!1,this.recheckRequested&&(this.recheckRequested=!1,this.check().catch(()=>{}))}}}capacityChanged(e){this.agentListCache=null,0===this.capStopped.size&&0===this.refused.size||(this.logger.info("Capacity may have changed; judging the sessions a provider cap or refusal stopped",{reason:e,stopped:this.capStopped.size,refused:this.refused.size}),this.check().catch(()=>{}))}serveStarted(e=Date.now()){this.processServeStartedAt||(this.processServeStartedAt=e),this.serveStartedAt=e,this.logger.info("The serve started; turns cut by the stop are judged now",{at:e}),this.check().catch(()=>{})}getCapStopped(){return Array.from(this.capStopped.keys())}async checkOnce(){if((0,w.isProjectPaused)())return
|
|
@@ -2052,20 +2057,21 @@ if(this.resumedRecently.has(r.id)&&!e)continue
|
|
|
2052
2057
|
const s=this.capStopped.has(r.id)||this.refused.has(r.id)||e,a=r.time?.updated??0,c=(0,y.awakeSince)(a,p),l=I.has(t[r.id]?.type)
|
|
2053
2058
|
if(l&&this.hasLiveCommand?.(r.id))continue
|
|
2054
2059
|
let u=!1
|
|
2055
|
-
|
|
2060
|
+
const f=this.isAwaitedReply(r.id)?Math.min(this.stalledWorkThresholdMs,this.awaitedReplyThresholdMs):this.stalledWorkThresholdMs
|
|
2061
|
+
if(l&&c<this.frozenTurnThresholdMs){if(c>=f)try{const e=await fetch(`${n}/session/${r.id}/message?limit=6`,{signal:AbortSignal.timeout(3e3)}),t=e.ok?await e.json():[]
|
|
2056
2062
|
let s=t.length
|
|
2057
2063
|
for(;s>0&&(0,h.isQuietDelivery)(t[s-1]);)s--
|
|
2058
2064
|
if(t.length>0&&0===s)throw new Error("only quiet deliveries in view")
|
|
2059
2065
|
const o=t[s-1]??null,i=o?.info?.role,a=(Array.isArray(o?.parts)?o.parts:[]).filter(e=>e&&("tool"===e.type||"text"===e.type||"reasoning"===e.type)),c=a[a.length-1],l=a.some(e=>"tool"===e.type&&"running"===e.state?.status)||!!c&&("tool"===c.type&&"pending"!==c.state?.status||"tool"!==c.type&&String(c.text??"").trim().length>0)
|
|
2060
2066
|
u=!o||"user"===i||"assistant"===i&&!l}catch{u=!1}if(!u){this.stuckSessions.has(r.id)&&!this.testInjectedIds.has(r.id)&&(this.stuckSessions.delete(r.id),this.notifiedSessions.delete(r.id),this.onStuckResolved?.(r.id))
|
|
2061
|
-
continue}this.logger.warn("Running turn with no streamed parts past the stalled-work threshold; treating as frozen",{sessionId:r.id,idleForMs:c})}const
|
|
2062
|
-
if(!(
|
|
2063
|
-
const
|
|
2064
|
-
if((v
|
|
2065
|
-
const
|
|
2066
|
-
if(!s&&(!
|
|
2067
|
-
const
|
|
2068
|
-
!s
|
|
2067
|
+
continue}this.logger.warn("Running turn with no streamed parts past the stalled-work threshold; treating as frozen",{sessionId:r.id,idleForMs:c})}const g=(r.title||"").match(o)?.[1],v=g?void 0:(r.title||"").match(d)?.[1]
|
|
2068
|
+
if(!(g||v||m.has(r.id)||s))continue
|
|
2069
|
+
const b=g?void 0:(r.title||"").match(i)?.[1]
|
|
2070
|
+
if((b||v)&&!s&&this.stallChecked.get(r.id)===a)continue
|
|
2071
|
+
const k=u?f:(g||b||v)&&!l?this.orphanThresholdMs:this.frozenTurnThresholdMs,S=!l&&a>0&&a<this.serveStartedAt
|
|
2072
|
+
if(!s&&(!S||this.stallChecked.get(r.id)===a)&&c<k)continue
|
|
2073
|
+
const $=this.settledSkip.get(r.id)
|
|
2074
|
+
!s&&$&&$.updated===a&&p-$.at<18e5||w.push({s:r,tag:g,updated:a,cut:S,running:l,gate:k})}const v=new Set(e.map(e=>e.id))
|
|
2069
2075
|
for(const e of Array.from(this.stallChecked.keys()))v.has(e)||this.stallChecked.delete(e)
|
|
2070
2076
|
for(const e of Array.from(this.settledSkip.keys()))v.has(e)||this.settledSkip.delete(e)
|
|
2071
2077
|
for(const e of Array.from(this.unconfused.keys()))v.has(e)||this.unconfused.delete(e)
|
|
@@ -2094,7 +2100,7 @@ if(!t.ok)continue
|
|
|
2094
2100
|
M=await t.json()}catch(t){this.logger.debug("Stall check failed for session",{sessionId:e.id,error:(0,g.getErrorMessage)(t)})
|
|
2095
2101
|
continue}const R="resume"===_&&!!E
|
|
2096
2102
|
if(m&&!R&&(0,y.awakeSince)(d,p)<j&&!this.capStopped.has(e.id)&&!this.refused.has(e.id)&&!this.judgeNow.has(e.id))continue
|
|
2097
|
-
let P=(0,h.assembleTurnEvidence)(M,{now:p,minInFlightMs:this.stalledWorkThresholdMs,...R?{cutBefore:this.serveStartedAt}:{}})
|
|
2103
|
+
let P=(0,h.assembleTurnEvidence)(M,{now:p,minInFlightMs:this.isAwaitedReply(e.id)?Math.min(this.stalledWorkThresholdMs,this.awaitedReplyThresholdMs):this.stalledWorkThresholdMs,...R?{cutBefore:this.serveStartedAt}:{}})
|
|
2098
2104
|
const O=new Set(["clean-text","empty","no-messages","dead-end","generating"])
|
|
2099
2105
|
if(x&&O.has(P.kind))continue
|
|
2100
2106
|
if("quota"===P.kind){const t=await this.modelForSession(e.id)??this.getDefaultModel?.(),r=(0,h.quotaBlockLifted)(P.quota,t,p)
|
|
@@ -3657,9 +3663,9 @@ e.post("/api/site-shots",async(e,t)=>{const{url:r,fullPage:s,width:n}=e.body??{}
|
|
|
3657
3663
|
if(!r||"string"!=typeof r||!/^https?:\/\//i.test(r))return void t.status(400).json({success:!1,error:"http(s) url required"})
|
|
3658
3664
|
const i=await u(r)
|
|
3659
3665
|
if(i)t.status(403).json({success:!1,error:i})
|
|
3660
|
-
else if(o.getProjectId())try{const{buf:e,mime:i}=await(0,
|
|
3666
|
+
else if(o.getProjectId())try{const{buf:e,mime:i}=await(0,V.shootUrl)(r,{fullPage:!!s,width:"number"==typeof n?n:void 0}),a=8388608
|
|
3661
3667
|
if(e.length>a)return void t.status(413).json({success:!1,error:`screenshot is ${e.length} bytes (cap ${a}); retry without fullPage or with a narrower width`})
|
|
3662
|
-
const c=`data:${i};base64,${e.toString("base64")}`,l=await(0,
|
|
3668
|
+
const c=`data:${i};base64,${e.toString("base64")}`,l=await(0,z.storeLocalAsset)(o.projectPath,c).catch(()=>{})
|
|
3663
3669
|
if(!l)return void t.status(502).json({success:!1,error:"asset upload failed"})
|
|
3664
3670
|
t.json({success:!0,data:{filename:l,dataUrl:c,url:r}})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}else t.status(503).json({success:!1,error:"project not registered with the remote yet"})}),e.post("/api/site-text",async(e,r)=>{const{url:s}=e.body??{}
|
|
3665
3671
|
if(!s||"string"!=typeof s||!/^https?:\/\//i.test(s))return void r.status(400).json({success:!1,error:"http(s) url required"})
|
|
@@ -3694,7 +3700,7 @@ if(!f){const r=Number(e.body?.width),s=Number.isFinite(r)&&r>0?Math.min(1920,Mat
|
|
|
3694
3700
|
u=await a.newPage({viewport:{width:s,height:n},deviceScaleFactor:1,ignoreHTTPSErrors:!0}),u.on("console",e=>{"error"===e.type()&&t.push(String(e.text()).slice(0,300))}),u.on("pageerror",e=>t.push(String(e?.message??e).slice(0,300)))}f&&u.url()===s||(await u.goto(s,{waitUntil:"domcontentloaded",timeout:3e4}),await u.waitForLoadState("load",{timeout:1e4}).catch(()=>{}),await u.waitForTimeout(300))
|
|
3695
3701
|
let i=null
|
|
3696
3702
|
const p=async e=>{try{const t=`data:image/png;base64,${(await u.screenshot({type:"png"})).toString("base64")}`
|
|
3697
|
-
return"final"===e&&(i=t),`asset:${await(0,
|
|
3703
|
+
return"final"===e&&(i=t),`asset:${await(0,z.storeLocalAsset)(o.projectPath,t)}`}catch{return null}}
|
|
3698
3704
|
for(let e=0;e<n.length;e++){if(Date.now()-g>9e4){w.push({step:e,action:"aborted",ok:!1,note:"drive exceeded 90s"})
|
|
3699
3705
|
break}const t=n[e]??{},r=String(t.action||"").toLowerCase(),s="string"==typeof t.selector?t.selector:void 0,o="string"==typeof t.text?t.text:void 0,i={step:e,action:r,...s?{selector:s}:{},...o?{text:o}:{}}
|
|
3700
3706
|
try{if("click"===r){if(!s)throw new Error("click needs a selector")
|
|
@@ -3718,7 +3724,7 @@ try{const e=M.default.normalize(r).split(M.default.sep).join("/").replace(/\/+$/
|
|
|
3718
3724
|
if(e.startsWith("..")||(0,D.refusedPreviewPath)(e))return void t.status(400).json({success:!1,error:`${r} is not a site directory (hidden and AlchemyLab folders are not served)`})
|
|
3719
3725
|
const i=await(0,D.previewRootFor)(o.projectPath,e,()=>(0,C.ensureDefaultBranchSnapshot)(o.projectPath),a),c=M.default.join(i.root,"index.html")
|
|
3720
3726
|
try{await q.promises.access(c)}catch{return void t.status(400).json({success:!1,error:i.main?`no index.html in ${r} on main (a preview of main serves what main has committed, plus build output from commands run for main; files only in the project folder are not served)`:`no index.html in ${r} (build it first, or point at the build output)`})}const{url:l}=await(0,D.serveSite)(i.root,i.main?{refresh:()=>(0,C.ensureDefaultBranchSnapshot)(o.projectPath)}:{}),d=a?`${a}/${"."===e?"":e}`.replace(/\/$/,""):r,u="string"==typeof s&&s.trim()?s.trim():d.replace(/[^a-zA-Z0-9_-]+/g,"-").replace(/^-|-$/g,""),h=(0,O.registerPreviewHandle)(u,l,"string"==typeof n&&n.trim()?n.trim():void 0,{requestId:a,fallbackLabel:d})
|
|
3721
|
-
t.json({success:!0,data:{...h,dir:r,ref:`live:${h.name}`}})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.delete("/api/previews/:name",(e,t)=>{(0,O.unregisterPreviewHandle)(e.params.name),t.json({success:!0,data:{name:e.params.name,deleted:!0}})}),e.get("/api/file",async(e,t)=>{try{const r="string"==typeof e.query.path?e.query.path:"",s="string"==typeof e.query.ref&&/^[\w\/.-]+$/.test(e.query.ref)&&!e.query.ref.startsWith("-")?e.query.ref:void 0,n=(0,X.refusedRepoPath)(r)
|
|
3727
|
+
t.json({success:!0,data:{...h,dir:r,ref:`live:${h.name}`}})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.delete("/api/previews/:name",(e,t)=>{const r=(0,O.getPreviewHandle)(e.params.name),s=!!r&&(0,D.stopSiteAt)(r.url);(0,O.unregisterPreviewHandle)(e.params.name),t.json({success:!0,data:{name:e.params.name,deleted:!0,stopped:s}})}),e.get("/api/file",async(e,t)=>{try{const r="string"==typeof e.query.path?e.query.path:"",s="string"==typeof e.query.ref&&/^[\w\/.-]+$/.test(e.query.ref)&&!e.query.ref.startsWith("-")?e.query.ref:void 0,n=(0,X.refusedRepoPath)(r)
|
|
3722
3728
|
if(n)return void t.status(400).json({success:!1,error:n})
|
|
3723
3729
|
const i=524288,a=async e=>{const t=(0,w.requestWorktreesDir)(o.projectPath)
|
|
3724
3730
|
let r=null
|
|
@@ -3849,7 +3855,7 @@ let s=null
|
|
|
3849
3855
|
try{const e=o.getProjectId(),t=e?await(0,v.readAllPages)(o.apiClient,t=>`/api/projects/${e}/test-runs?limit=100&offset=${t}`,500):void 0
|
|
3850
3856
|
if(t){const e=e=>"string"==typeof e?(0,g.worktreeOnThisComputer)(M.default.join(o.projectPath,y.ALCHEMY_DIR_NAME),e):null
|
|
3851
3857
|
s=t.some(t=>e(t?.worktreePath)===e(r)&&("pending"===t.status||"running"===t.status))}}catch(e){o.logger.debug("release-pool-slot sibling lookup failed",{error:(0,P.getErrorMessage)(e)})}if(!1!==s)return o.logger.info("Pool slot kept for the dispatch idle watch",{worktreePath:r,reason:null===s?"sibling check unavailable":"sibling run still active"}),void t.json({success:!0,released:!1});(0,N.releaseSlot)((0,g.worktreeOnThisComputer)(M.default.join(o.projectPath,y.ALCHEMY_DIR_NAME),r)),o.logger.info("Released pool slot via report-back",{worktreePath:r}),t.json({success:!0,released:!0})}catch(e){o.logger.warn("release-pool-slot failed (non-fatal)",{error:(0,P.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}})}
|
|
3852
|
-
const m=t(76982),g=t(71856),w=t(7202),y=t(14139),v=t(5641),b=t(44600),k=t(96302),S=t(44876),I=t(29418),$=t(96396),j=t(36160),E=t(41666),A=f(t(79896)),C=t(51857),_=t(46898),T=t(6156),x=t(75453),M=f(t(16928)),R=t(27478),P=t(58824),O=t(28295),D=t(3811),q=t(79896),N=t(60489),F=t(92456),L=t(83807),U=t(45738),B=t(65856),H=t(82167),W=t(99970),G=t(24106),K=t(73775),
|
|
3858
|
+
const m=t(76982),g=t(71856),w=t(7202),y=t(14139),v=t(5641),b=t(44600),k=t(96302),S=t(44876),I=t(29418),$=t(96396),j=t(36160),E=t(41666),A=f(t(79896)),C=t(51857),_=t(46898),T=t(6156),x=t(75453),M=f(t(16928)),R=t(27478),P=t(58824),O=t(28295),D=t(3811),q=t(79896),N=t(60489),F=t(92456),L=t(83807),U=t(45738),B=t(65856),H=t(82167),W=t(99970),G=t(24106),K=t(73775),z=t(27584),V=t(3031),Y=t(52232),J=t(62040),X=t(32559),Q=t(96511),Z=new Map,ee=new Map,te=new T.ParallelNotes
|
|
3853
3859
|
let re=null
|
|
3854
3860
|
const se=e=>({id:String(e?.localId??e?.id??""),title:String(e?.title??""),status:String(e?.status??""),description:"string"==typeof e?.description?e.description:null})
|
|
3855
3861
|
exports.COMPLETION_REPLAY_BUDGET_MS=15e4
|
|
@@ -4758,7 +4764,7 @@ let h=null
|
|
|
4758
4764
|
const p=new Map},62366(e,exports,t){"use strict"
|
|
4759
4765
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
4760
4766
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
|
|
4761
|
-
const s=t(84920),n=t(71856),o=t(65856),i=t(42403),a=t(60721),c=t(16130),l=t(5641),d=t(17039),u=t(53526),h=t(53691),p=t(8469),f=t(97387),m=t(52232),g=t(61161),w=r(t(7252)),y=r(t(77174)),v=r(t(98577)),b=t(27584),k=t(79896),S=t(90473),I=t(27207),$=t(85959),j=t(5452),E=t(60885),A=t(21178),C=t(28295),_=t(52640),T=t(65232),x=t(95671),M=t(44192),R=t(14139),P=t(41152),O=r(t(16928)),D=r(t(79896)),q=r(t(70857)),N=t(5607),F=t(76634),L=t(31),U=t(90524),B=t(18984),H=t(58824),W=t(29869),G=t(67078),K=t(60070),
|
|
4767
|
+
const s=t(84920),n=t(71856),o=t(65856),i=t(42403),a=t(60721),c=t(16130),l=t(5641),d=t(17039),u=t(53526),h=t(53691),p=t(8469),f=t(97387),m=t(52232),g=t(61161),w=r(t(7252)),y=r(t(77174)),v=r(t(98577)),b=t(27584),k=t(79896),S=t(90473),I=t(27207),$=t(85959),j=t(5452),E=t(60885),A=t(21178),C=t(28295),_=t(52640),T=t(65232),x=t(95671),M=t(44192),R=t(14139),P=t(41152),O=r(t(16928)),D=r(t(79896)),q=r(t(70857)),N=t(5607),F=t(76634),L=t(31),U=t(90524),B=t(18984),H=t(58824),W=t(29869),G=t(67078),K=t(60070),z=t(37210),V=t(87227),Y=t(82928),J=t(76616),X=t(15047),Q=t(90629),Z=t(42298),ee=t(67563),te=t(41950),re=t(88087),se=t(1285),ne=t(20564),oe=Object.keys(N.actionHandlers).join(","),ie=t(6850),ae=t(58836),ce=t(25146),le=t(23411),de=t(43218),ue=t(81570),he=t(18815),pe=t(73613),fe=t(49779),me=t(76982),ge=t(53340),we=t(89063),ye=t(61292),ve=t(11017),be=t(28148),ke=t(18649),Se=t(64306),Ie=t(96685),$e=t(49816),je=t(35419),Ee=t(33521),Ae=t(56844),Ce=t(84352),_e=t(40713),Te=t(75361),xe=t(75167),Me=t(72188),Re=t(6688),Pe=t(39068),Oe=t(3435),De=t(48463),qe=t(60489),Ne=t(24106),Fe=t(81618),Le=t(80211),Ue=t(2566),Be=t(83807),He=t(92456),We=t(95040),Ge=t(60355),Ke=t(31026),ze=t(73782),Ve=t(53608),Ye=t(63484),Je=t(31851),Xe=t(84102),Qe=t(75979),Ze=t(79329),et=t(60833),tt=t(42593),rt=t(69706),st=t(11773),nt=t(20990),ot=t(45956),it=t(36799),at=t(86116),ct=t(13751),lt=t(13791),dt=new Set(["opencode-proxy","machine-proxy","run-tests","review-fanout","main-checks","check-target","judge-proposal"]),ut=65536
|
|
4762
4768
|
class ht{rememberApprovedCommand(e,t,r){const s=t.rule,o=r?"in this environment":"on your machine",i=(0,Ee.isStandingRulePattern)(s),a=`You chose "Always allow" on the approval card for \`${t.askedCommand.slice(0,300)}\`${t.approvalId?` (card ${t.approvalId})`:""}${t.sessionId?`, asked by agent session ${t.sessionId}`:""}, on ${t.at.replace("T"," ").slice(0,16)} UTC.`;(async()=>{(await(0,$e.getRegisteredAllows)(this.apiClient,e,r??null).catch(()=>[])).some(e=>("string"==typeof e?e:e.exact).trim()===s.trim())||(await this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"ok:card",command:(0,n.commandAsStored)(s),description:`${a} ${i?`It allows exactly this shape, where <file> is any one file inside the project, ${o}`:`This exact command runs ${o}`} without asking. Delete it or switch it off to be asked again.`,timeout:120,isActive:!0,exactMatch:!0,...r?{environmentId:r}:{}}),(0,$e.invalidateRegisteredAllows)(e))})().catch(e=>this.logger.warn("Failed to remember approved command",{error:String(e)}))}rememberJudgeApprovedCommand(e,t,r){const s=new Date(Date.now()+ht.JUDGE_APPROVAL_TTL_MS).toISOString()
|
|
4763
4769
|
this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"agent:judge",command:(0,n.commandAsStored)(t),description:"Approved by the command judge. Expires on its own; delete it to withdraw the approval sooner.",timeout:120,isActive:!0,expiresAt:s,exactMatch:!0,...r?{environmentId:r}:{}}).then(()=>(0,$e.invalidateRegisteredAllows)(e)).catch(e=>this.logger.warn("Failed to remember judge-approved command",{error:String(e)}))}get workCopiesReady(){return this.workCopiesGate.promise}updateAuthToken(e){this.options.authToken=e,this.apiClient.setAuthToken(e),this.configurer&&(this.configurer.authToken=e)}setReconfigureOpenCode(e){this.reconfigureOpenCode=e}setRelaunchOpenCode(e){this.relaunchOpenCode=e}noteServeExit(e){if(e.pid&&this.trackedChildPids.delete(e.pid),e.pid&&this.managedExitPids.delete(e.pid))return void this.logger.info("OpenCode serve exit from a managed restart -- no action",{code:e.code,signal:e.signal,pid:e.pid})
|
|
4764
4770
|
if(!this.parkController?.noteServeDeath(`exit code=${e.code} signal=${e.signal}`))return void this.logger.info("OpenCode serve exit observed during managed lifecycle -- no action",{code:e.code,signal:e.signal,pid:e.pid})
|
|
@@ -4768,7 +4774,7 @@ return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.pendingConfigReload=!1,
|
|
|
4768
4774
|
const r=(0,P.createMetricsCollector)(!1)
|
|
4769
4775
|
if((0,st.setFleetServerKey)(t.projectPath?t.projectPath.split("/").pop():void 0),this.apiClient=new M.BaseApiClient(t.remoteServerUrl,e,r,t.authToken,{timeout:35e3,serverKey:t.projectPath?t.projectPath.split("/").pop():void 0}),t.tokenManager){const e=t.tokenManager
|
|
4770
4776
|
this.apiClient.setTokenRefresher(async()=>{const t=(await e.getToken())?.token
|
|
4771
|
-
return t&&t!==this.options.authToken&&this.updateAuthToken(t),t})}this.configurer=new B.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.hookExecutor=new
|
|
4777
|
+
return t&&t!==this.options.authToken&&this.updateAuthToken(t),t})}this.configurer=new B.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.hookExecutor=new z.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,w.default)(),r=e=>(0,Z.isOriginAllowed)(e,{frontendOrigin:this.frontendOrigin,extra:process.env.ALCHEMY_EXTRA_UI_ORIGINS}),s=new Set
|
|
4772
4778
|
e.use((e,t,r)=>{const n=e.headers.host
|
|
4773
4779
|
if((0,Z.isLoopbackHost)(n))return r()
|
|
4774
4780
|
s.has(String(n))||(s.add(String(n)),this.logger.warn("Refused a request addressed to a name other than localhost (DNS rebinding?)",{host:n,path:e.path})),t.status(403).json({success:!1,error:Z.HOST_REFUSED_MESSAGE})}),e.use((0,Z.inertActiveContent)()),e.use((e,t,s)=>{const n=e.headers.origin
|
|
@@ -4832,7 +4838,7 @@ if("1"===e.query.stats){const e=a&&c?await(0,He.computeDiffStats)(o,a,c):void 0
|
|
|
4832
4838
|
if(!e&&0===l.length)return void t.status(404).json({success:!1,error:`No diff stats for ${r}.`})
|
|
4833
4839
|
const s=(0,We.withDocumentStats)(e,l)
|
|
4834
4840
|
return void t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,stats:s}})}const d=5e5,u=a&&c?await(0,g.documentDiffGit)(this.options.projectPath).raw([...(0,g.documentDiffGitArgs)(),"diff","-M50%",`${a}..${c}`]):"",h=(0,We.withDocumentEdits)(u,l),p=h.length>d
|
|
4835
|
-
t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,diff:p?h.slice(0,d):h,truncated:p}})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),(0,Ke.registerLinearRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient}),(0,
|
|
4841
|
+
t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,diff:p?h.slice(0,d):h,truncated:p}})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),(0,Ke.registerLinearRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient}),(0,ze.registerLocalModelRoutes)(e),(0,Qe.registerExhibitPreviewRoutes)(e),e.get("/api/publish-capabilities",async(e,t)=>{try{const e=await(0,F.getPublishCapabilities)(this.options.projectPath,(0,F.defaultRunner)())
|
|
4836
4842
|
t.json({success:!0,data:e})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/requests/:id/publish-pr",async(e,t)=>{const r=e.params.id
|
|
4837
4843
|
try{const s=this.projectId
|
|
4838
4844
|
if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
@@ -4888,7 +4894,7 @@ r.json({success:!0,data:a})}catch(e){this.logger.error("Hook event processing fa
|
|
|
4888
4894
|
if(!s||"before"!==a&&"after"!==a)return void r.status(400).json({success:!1,error:"toolName and timing ('before'|'after') are required"})
|
|
4889
4895
|
if(n){const{notifyPlannerHandoffToolEvent:e}=await t.e(991).then(()=>t(18991))
|
|
4890
4896
|
e(String(n),String(s),o)}if("after"===a&&e.body?.callId&&this.projectId){const{closeMcpReceipt:r}=await t.e(111).then(()=>t(56111))
|
|
4891
|
-
r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{callId:String(e.body.callId),ok:!0,outputPreview:"string"==typeof i?i:void 0})}const c=(0,
|
|
4897
|
+
r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{callId:String(e.body.callId),ok:!0,outputPreview:"string"==typeof i?i:void 0})}const c=(0,V.parseToolEvents)({toolName:s,sessionId:n||"",input:o,output:i,timing:a})
|
|
4892
4898
|
let l=0
|
|
4893
4899
|
for(const e of c)l+=(await this.hookExecutor.processEvent({eventType:e.eventType,sessionId:n||"",variables:e.variables,timing:e.timing})).triggered
|
|
4894
4900
|
this.logger.info("tool-event received",{toolName:s,timing:a,sessionId:n||"",events:c.length,triggered:l}),r.json({success:!0,data:{events:c.length,triggered:l}})}catch(e){this.logger.error("tool-event processing failed",{error:(0,H.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/web-call",async(e,r)=>{try{const{tool:s,sessionId:n,callId:o,input:i,metadata:a,error:c}=e.body||{}
|
|
@@ -4917,7 +4923,7 @@ try{await(0,De.sweepStaleSetupWorktrees)(a,this.logger)
|
|
|
4917
4923
|
const e=await(0,De.resolveSetupBranch)(this.apiClient,c,n,"string"==typeof o?o:void 0,this.logger,a),s=(0,De.generateSetupId)()
|
|
4918
4924
|
d=O.default.join((0,De.getSetupWorktreesBase)(a),`verify-${s}`),await(0,De.createSetupWorktree)(a,d,e,this.logger)
|
|
4919
4925
|
const u=await(0,Fe.withContainerLabels)({"alchemy.purpose":"verify","alchemy.request":"string"==typeof o?o:void 0},()=>(0,Ne.runConfinedGate)(d,[{name:"verify",command:i,required:!0,timeout:600}],l,{projectName:`verify-${s}`})),h=!String(r.shellInit??"").trim()&&(0,Ue.carriesOwnShellInit)(i),p=h?["The smoke command sources its own setup, and the environment declares no shellInit. This verify proves the smoke command, not the environment: docker exec runs no ENTRYPOINT and reads no login profile, so every LATER command gets a bare PATH. Move the preamble into config.shellInit and verify again with the command alone."]:[]
|
|
4920
|
-
h&&this.logger.warn("environment verify passed on a self-sourcing smoke command",{smoke:i}),t.json({success:!0,data:{ok:!u.error&&"passed"===u.status,status:u.status,error:u.error,commands:u.commands,warnings:p}})}catch(e){this.logger.warn("environment verify failed",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}finally{if(d&&D.default.existsSync(d))try{await(0,Be.simpleGit)(a).raw(["worktree","remove","--force",d])}catch{}}}),(0,
|
|
4926
|
+
h&&this.logger.warn("environment verify passed on a self-sourcing smoke command",{smoke:i}),t.json({success:!0,data:{ok:!u.error&&"passed"===u.status,status:u.status,error:u.error,commands:u.commands,warnings:p}})}catch(e){this.logger.warn("environment verify failed",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}finally{if(d&&D.default.existsSync(d))try{await(0,Be.simpleGit)(a).raw(["worktree","remove","--force",d])}catch{}}}),(0,Ve.registerRunRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath,getDefaultModel:()=>this.cachedDefaultModel||void 0,envSessionStarts:this.envSessionStarts,paintCleaned:this.paintCleaned,rememberApprovedCommand:(e,t,r)=>this.rememberApprovedCommand(e,t,r),rememberJudgeApprovedCommand:(e,t,r)=>this.rememberJudgeApprovedCommand(e,t,r),visualBaselineDir:()=>this.visualBaselineDir()}),(0,Xe.registerRelaySettingsRoutes)(e,{projectPath:this.options.projectPath}),(0,Je.registerProducedFilesRoutes)(e,{projectPath:this.options.projectPath,logger:this.logger,getDocument:async e=>{const t=this.projectId
|
|
4921
4927
|
if(!t||!/^[A-Za-z0-9_-]+$/.test(e))return null
|
|
4922
4928
|
const r=await this.apiClient.get(`/api/projects/${t}/documents/${e}`).catch(()=>null),s=r?.data??r
|
|
4923
4929
|
return s&&"string"==typeof s.content?{filename:String(s.filename||`${e}.md`),content:s.content}:null}}),(0,Ye.registerWorkspaceRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath}),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(35317,23)),s=e("npm root -g",{encoding:"utf8"}).trim()
|
|
@@ -5699,12 +5705,12 @@ const s=e.slice(r+1).find(e=>"assistant"===e.role&&e.created>0)
|
|
|
5699
5705
|
return s?new Date(s.created).toISOString():void 0}function W(e,t){const r=e.findIndex(e=>e.id===t)
|
|
5700
5706
|
return e.slice(r+1).filter(e=>"assistant"===e.role&&e.text.trim()).map(e=>e.id)}var G,K=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r)
|
|
5701
5707
|
var n=Object.getOwnPropertyDescriptor(t,r)
|
|
5702
|
-
n&&!("get"in n?!t.__esModule:n.writable||n.configurable)||(n={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,s,n)}:function(e,t,r,s){void 0===s&&(s=r),e[s]=t[r]}),
|
|
5708
|
+
n&&!("get"in n?!t.__esModule:n.writable||n.configurable)||(n={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,s,n)}:function(e,t,r,s){void 0===s&&(s=r),e[s]=t[r]}),z=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),V=this&&this.__importStar||(G=function(e){return G=Object.getOwnPropertyNames||function(e){var t=[]
|
|
5703
5709
|
for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
|
|
5704
5710
|
return t},G(e)},function(e){if(e&&e.__esModule)return e
|
|
5705
5711
|
var t={}
|
|
5706
5712
|
if(null!=e)for(var r=G(e),s=0;s<r.length;s++)"default"!==r[s]&&K(t,e,r[s])
|
|
5707
|
-
return
|
|
5713
|
+
return z(t,e),t})
|
|
5708
5714
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.extendChatWatches=function(e){if(e>0)for(const t of ce.values())t.hardCapAtMs+=e},exports.persistedExchanges=o,exports.resumeInboxChatWatches=function(e){try{const t=s(e.projectPath)
|
|
5709
5715
|
if(!re.existsSync(t))return
|
|
5710
5716
|
const r=JSON.parse(re.readFileSync(t,"utf-8"))
|
|
@@ -5727,7 +5733,7 @@ s.checking=!0
|
|
|
5727
5733
|
try{await r()
|
|
5728
5734
|
const n=await T(e,s,t)
|
|
5729
5735
|
return n&&s.logger.warn("inbox-chat loop: turn stopped answering; ended it and resumed it once",{sessionId:e,markerMessageId:s.exchanges[s.exchanges.length-1].markerMessageId}),n}finally{s.checking=!1}},exports.extractTurnSilence=x,exports.absorbedByReply=q
|
|
5730
|
-
const Y=t(60833),J=t(58824),X=t(42593),Q=t(24068),Z=t(4234),ee=t(17039),te=t(81602),re=
|
|
5736
|
+
const Y=t(60833),J=t(58824),X=t(42593),Q=t(24068),Z=t(4234),ee=t(17039),te=t(81602),re=V(t(79896)),se=V(t(16928)),ne=t(36799),oe=3e5,ie=9e5,ae=8e3,ce=new Map
|
|
5731
5737
|
let le=null
|
|
5732
5738
|
const de=new Map,ue=6e4,he=/^(let me|i(?:['\u2019]ll| will| am going to|['\u2019]m going to)|one (?:moment|sec)|checking|looking|on it|sure[,.!]?\s|okay[,.!]?\s|ok[,.!]?\s|first[, ]|hang on|give me)/i,pe=/^(ok(?:ay)?|done|sure|noted|got it|all set|that's it|right)[.!]?$/i,fe=/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Thai}]/u,me=/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Thai}]+/gu,ge=/^(?:(?:now|next|then|first|so)[,:]?\s+)?(?:let me|i(?:['\u2019]ll| will| am going to|['\u2019]m going to)|i['\u2019]m (?:now )?(?:checking|looking|nudging|filing|starting|opening|creating|asking|waking|reading|pulling|fetching|comparing|running|updating|writing|going|registering|drafting))\b/i,we=/get-info|:list|:search|list-areas|read_files|read_file|search|grep|query_history|request_diff|environment_list|environment_get|testing_process_list|check-status|list-sessions|read-messages|results:fetch|commands:list|read_site|:brief|:get\b/i,ye=new Set},81602(e,exports){"use strict"
|
|
5733
5739
|
exports.Cr=exports.UT=void 0,exports.wakeThatTellsUser=function(e,t){const r="string"==typeof e?exports.Cr.get(e):void 0
|
|
@@ -5892,8 +5898,10 @@ return l.set(a,{server:h,port:p,root:a}),{port:p,url:`http://127.0.0.1:${p}`}},e
|
|
|
5892
5898
|
return l.get(t)?.server.close(),void l.delete(t)}for(const[,e]of l)e.server.close()
|
|
5893
5899
|
l.clear()},exports.stopSitesUnder=function(e){const t=(0,i.resolve)(e),r=[]
|
|
5894
5900
|
for(const[e,s]of l)(e===t||e.startsWith(t+i.sep))&&(s.server.close(),l.delete(e),r.push(`http://127.0.0.1:${s.port}`))
|
|
5895
|
-
return r},exports.
|
|
5896
|
-
|
|
5901
|
+
return r},exports.stopSiteAt=function(e){let t=0
|
|
5902
|
+
try{t=Number(new URL(e).port)}catch{return!1}for(const[e,r]of l)if(r.port===t)return r.server.close(),l.delete(e),!0
|
|
5903
|
+
return!1},exports.listServedSites=function(){return Array.from(l.values()).map(e=>({root:e.root,port:e.port}))}
|
|
5904
|
+
const s=t(58611),n=t(79896),o=t(91943),i=t(16928),a=t(7202),c={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".wasm":"application/wasm",".webmanifest":"application/manifest+json",".xml":"application/xml; charset=utf-8",".mp3":"audio/mpeg",".wav":"audio/wav",".ogg":"audio/ogg",".mp4":"video/mp4",".webm":"video/webm",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".svg":"image/svg+xml",".ico":"image/x-icon",".avif":"image/avif",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".otf":"font/otf",".txt":"text/plain; charset=utf-8",".pdf":"application/pdf"},l=new Map},3031(e,exports,t){"use strict"
|
|
5897
5905
|
exports.shootUrl=async function(e,r={}){const s=Math.min(1920,Math.max(320,"number"==typeof r.width?Math.round(r.width):1280)),{chromium:n}=t(71257)
|
|
5898
5906
|
let o
|
|
5899
5907
|
try{o=await n.launch({channel:"chrome",args:["--no-sandbox"]})}catch{o=await n.launch({args:["--no-sandbox"]})}try{const t=await o.newPage({viewport:{width:s,height:900},deviceScaleFactor:1,ignoreHTTPSErrors:!0})
|
|
@@ -6812,56 +6820,60 @@ if(!n.ok){try{o.default.rmdirSync(s)}catch{}return{ok:!1,reason:n.reason}}return
|
|
|
6812
6820
|
exports.printBanner=function(){const e=(0,r.ownVersion)()??"unknown"
|
|
6813
6821
|
console.log([""," .oo 8 8 o 8 "," .P 8 8 8 8 8 "," .P 8 8 .oPYo. 8oPYo. .oPYo. ooYoYo. o o 8 .oPYo. 8oPYo. "," oPooo8 8 8 ' 8 8 8oooo8 8' 8 8 8 8 8 .oooo8 8 8 "," .P 8 8 8 . 8 8 8. 8 8 8 8 8 8 8 8 8 8 ",".P 8 8 `YooP' 8 8 `Yooo' 8 8 8 `YooP8 8oooo `YooP8 `YooP' ","..:::::....:.....:..:::..:.....:..:..:..:....8 ......:.....::.....:","::::::::::::::::::::::::::::::::::::::::::ooP'.::::::::::::::::::: v"+e,"::::::::::::::::::::::::::::::::::::::::::...::::::::::::::::::::::",""].join("\n"))}
|
|
6814
6822
|
const r=t(42403)},38778(e,exports,t){"use strict"
|
|
6815
|
-
async function
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
"
|
|
6823
|
+
function r(){return S}function s(e){return e?`\u26a0\ufe0f This project is not registered with AlchemyLab yet: ${e}.`.replace(/\.\.$/,"."):"\u26a0\ufe0f This project is not registered with AlchemyLab yet: the hosted service did not answer. Run `alchemy` again to try once more."}async function n(e,t,r,s,n="cli-init"){try{const o=(0,c.basename)(r)||"Local Project",i=process.env.ALCHEMY_MACHINE_ID,a=(0,l.folderKeyFromEnv)()
|
|
6824
|
+
let d=await u()
|
|
6825
|
+
for(let h=0;h<6&&503===d.status;h++)await new Promise(e=>setTimeout(e,2e3)),d=await u()
|
|
6826
|
+
async function u(){return fetch(`${e}/api/projects`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,"X-Client-Type":"alchemy-local-service",...(0,v.machineCallerHeaders)(e)},body:JSON.stringify({name:o,description:`Project for ${o}`,status:"active",metadata:{basePath:r,createdVia:n},...i?{machineId:i}:{},...a?{folderKey:a}:{}})})}if(S=void 0,d.ok){const p=await d.json()
|
|
6827
|
+
return p.data?.id||p.id}if(s.info(`Project registration failed (HTTP ${d.status})`),401===d.status)S="the sign-in on this computer is no longer accepted. Run `alchemy logout`, then `alchemy`"
|
|
6828
|
+
else if(503===d.status)S="the hosted service has not heard from this computer yet. Run `alchemy` again in a moment"
|
|
6829
|
+
else if(d.status>=400&&d.status<500){const m=await d.json().catch(()=>null),g=m?.error?.message??m?.error??m?.message
|
|
6830
|
+
"string"==typeof g&&g.trim()&&(S=g.trim())}}catch(w){s.info("Project registration failed",{error:(0,f.getErrorMessage)(w)})}}async function o(e){if(process.env.FRONTEND_URL)return process.env.FRONTEND_URL
|
|
6819
6831
|
if(e.includes("api.alchemylabcode.com"))return"https://alchemylabcode.com"
|
|
6820
6832
|
try{const t=await fetch(e+"/health")
|
|
6821
6833
|
if(t.ok){const e=await t.json()
|
|
6822
|
-
if(e?.data?.frontendUrl)return e.data.frontendUrl}}catch{}return e.replace(/:\d+/,":3000")}exports.ProjectRefused=void 0,exports.checkProjectIdentity=function(e){const t=(0,
|
|
6823
|
-
if(!(0,
|
|
6834
|
+
if(e?.data?.frontendUrl)return e.data.frontendUrl}}catch{}return e.replace(/:\d+/,":3000")}exports.ProjectRefused=void 0,exports.checkProjectIdentity=function(e){const t=(0,b.identityPath)(e)
|
|
6835
|
+
if(!(0,i.existsSync)(t))return
|
|
6824
6836
|
let r
|
|
6825
|
-
try{r=JSON.parse((0,
|
|
6826
|
-
const s=e=>{try{return(0,
|
|
6827
|
-
if(s(r)!==s(e))throw new
|
|
6828
|
-
|
|
6829
|
-
const
|
|
6830
|
-
if("created"===
|
|
6831
|
-
else if("failed"===
|
|
6832
|
-
try{(0,
|
|
6833
|
-
const
|
|
6834
|
-
let
|
|
6835
|
-
await(0,
|
|
6836
|
-
const
|
|
6837
|
-
return(0,
|
|
6838
|
-
let
|
|
6839
|
-
try{const e=JSON.parse((0,
|
|
6840
|
-
if(
|
|
6841
|
-
|
|
6842
|
-
if(!
|
|
6843
|
-
return"GET"===e?
|
|
6844
|
-
return(t?.data??t)?.type}:void 0
|
|
6845
|
-
let
|
|
6846
|
-
if(
|
|
6847
|
-
const e=(0,
|
|
6848
|
-
|
|
6849
|
-
y.setReconfigureOpenCode(async()=>{const e=(await
|
|
6850
|
-
return{success:
|
|
6851
|
-
return new
|
|
6852
|
-
try{const e=(0,
|
|
6853
|
-
if((0,
|
|
6854
|
-
t&&null!=t.port&&(0,
|
|
6837
|
+
try{r=JSON.parse((0,i.readFileSync)(t,"utf8"))?.metadata?.basePath}catch{return}if(!r)return
|
|
6838
|
+
const s=e=>{try{return(0,i.realpathSync)(e)}catch{return(0,c.resolve)(e)}}
|
|
6839
|
+
if(s(r)!==s(e))throw new k(["\u274c This folder is a copy that has not been re-identified."," .alchemy_lab/project.json belongs to a project whose directory is:",` ${r}`," but you are starting in:",` ${e}`,""," Starting here would register as the ORIGINAL project and silently do no"," work for this one. If a fork is still being created, wait for it to finish."," If you copied this folder by hand, delete .alchemy_lab/project.json and"," start again to get a project of its own."])},exports.prepareProject=async function(e){const{projectPath:t,remoteServerUrl:o,authToken:d,logger:u}=e,h=e.say??(e=>console.log(e)),p=await async function(e,t,o,l,d){if(function(e){return(0,i.existsSync)((0,c.join)(e,g.ALCHEMY_DIR_NAME,"project.json"))}(e))return!1
|
|
6840
|
+
d("\u{1f4c1} Initializing AlchemyLab project..."),d("")
|
|
6841
|
+
const u=await(0,w.ensureProjectRepository)(e)
|
|
6842
|
+
if("created"===u.status)d(`\u{1f331} ${u.message}`)
|
|
6843
|
+
else if("failed"===u.status||!u.hasHistory)throw function(e){const t=(0,c.join)(e,g.ALCHEMY_DIR_NAME)
|
|
6844
|
+
try{(0,i.readdirSync)(t).every(e=>"server.log"===e)&&(0,i.rmSync)(t,{recursive:!0,force:!0})}catch{}}(e),new k([`\u26a0\ufe0f ${u.message??"This folder cannot be used as a project yet."}`," Nothing was registered and no servers were started."])
|
|
6845
|
+
const h=(0,c.join)(e,g.ALCHEMY_DIR_NAME)
|
|
6846
|
+
let p
|
|
6847
|
+
await(0,a.mkdir)(h,{recursive:!0}),o?(d(`\u{1f4cb} Registering project "${(0,c.basename)(e)||"Local Project"}"...`),p=await n(t,o,e,l),d(p?`\u2705 Project registered: ${p}`:s(r()))):d("\u26a0\ufe0f No auth token, skipping project registration")
|
|
6848
|
+
const f=(0,c.join)(h,"project.json")
|
|
6849
|
+
return(0,i.writeFileSync)(f,JSON.stringify({id:p||null,name:(0,c.basename)(e)||"Local Project",status:"active",registeredRemotely:!!p,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:h}},null,2),"utf-8"),d(""),!0}(t,o,d,u,h),m=(0,b.identityPath)(t)
|
|
6850
|
+
let S
|
|
6851
|
+
try{const e=JSON.parse((0,i.readFileSync)(m,"utf-8"))
|
|
6852
|
+
if(S=e.id||void 0,!1===e.registeredRemotely&&d){const a=await n(o,d,t,u,"cli-init-retry")
|
|
6853
|
+
a?(e.id=a,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,i.writeFileSync)(m,JSON.stringify(e,null,2),"utf-8"),S=a,h(`\u2705 Project registered: ${a}`)):h(s(r()))}}catch{u.warn("Could not read project.json")}const I=async(e,t,r)=>{const s=await fetch(`${o}${t}`,{method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${d}`,"X-Client-Type":"alchemy-local-service",...(0,v.machineCallerHeaders)(o)},...void 0===r?{}:{body:JSON.stringify(r)}})
|
|
6854
|
+
if(!s.ok)throw new Error(`HTTP ${s.status}`)
|
|
6855
|
+
return"GET"===e?s.json():void 0},$=d&&S?async e=>{const t=await I("GET",`/api/projects/${S}/requests/${e}`)
|
|
6856
|
+
return(t?.data??t)?.type}:void 0,j=await(0,y.defaultBranchHasCode)(t,$).catch(()=>!1)
|
|
6857
|
+
let E=Promise.resolve()
|
|
6858
|
+
if(d){E=(0,y.reportProjectHasCode)(S,j,(e,t)=>I("PATCH",e,t))
|
|
6859
|
+
const e=(0,l.folderKeyFromEnv)()
|
|
6860
|
+
S&&e&&I("PATCH",`/api/projects/${S}`,{folderKey:e}).catch(e=>u.info("Folder key not recorded yet",{error:(0,f.getErrorMessage)(e)}))}return{firstRun:p,projectId:S,hasCode:j,codeReported:E}},exports.startProject=async function(e){const{projectPath:t,remoteServerUrl:r,tokenManager:s,authToken:n,projectId:a,logger:l}=e,w=e.port||await(0,m.findAvailablePort)(3456),y=new u.AlchemyActionServer(l,{projectPath:t,remoteServerUrl:r,port:w,authToken:n,tokenManager:s,retireTwins:e.retireTwins,onIdlePark:e.onIdlePark}),v=(0,h.getInstallContext)(),b=new d.MCPClientConfigurer(l,r,n),k=new p.UnifiedLauncher(l,v,b)
|
|
6861
|
+
y.setReconfigureOpenCode(async()=>{const e=(await s.getToken())?.token??n,o=new d.MCPClientConfigurer(l,r,e),{installPath:i,nodePath:c}=v,u=await o.configureOpenCode(i,t,c,a?{projectId:a}:void 0,!0)
|
|
6862
|
+
return{success:u.success,error:u.error}}),y.setRelaunchOpenCode(async()=>{const e=(await s.getToken())?.token??n,o=new d.MCPClientConfigurer(l,r,e)
|
|
6863
|
+
return new p.UnifiedLauncher(l,v,o).launch({projectPath:t,settings:a?{projectId:a}:void 0,onServeExit:e=>y.noteServeExit(e)})})
|
|
6864
|
+
try{const e=(0,c.join)(t,g.ALCHEMY_DIR_NAME,"opencode.json")
|
|
6865
|
+
if((0,i.existsSync)(e)){const t=JSON.parse((0,i.readFileSync)(e,"utf-8"))
|
|
6866
|
+
t&&null!=t.port&&(0,i.writeFileSync)(e,JSON.stringify({...t,port:null},null,2))}}catch{}const S=y.start(),[,I,$]=await Promise.all([S,o(r),Promise.race([y.workCopiesReady,S]).then(()=>k.launch({projectPath:t,settings:a?{projectId:a}:void 0,onServeExit:e=>y.noteServeExit(e)})).catch(e=>(l.warn("Failed to launch OpenCode serve",{error:(0,f.getErrorMessage)(e)}),{success:!1,error:(0,f.getErrorMessage)(e)}))])
|
|
6855
6867
|
let j
|
|
6856
6868
|
if($.success){const e="port"in $?$.port:void 0,r="pid"in $?$.pid:void 0
|
|
6857
6869
|
if(j={success:!0,port:e,pid:r},e){r&&y.trackChildPid(r),y.setOpenCodePort(e)
|
|
6858
|
-
const s=(0,
|
|
6859
|
-
return{actionServer:y,frontendUrl:I,opencode:j}},exports.resolveFrontendUrl=
|
|
6870
|
+
const s=(0,c.join)(t,g.ALCHEMY_DIR_NAME,"opencode.json");(0,i.writeFileSync)(s,JSON.stringify({port:e,pid:r,startedAt:(new Date).toISOString()},null,2)),l.info("Wrote OpenCode config",{path:s,port:e})}}else j={success:!1,error:"error"in $&&$.error?String($.error):"no reason reported"}
|
|
6871
|
+
return{actionServer:y,frontendUrl:I,opencode:j}},exports.resolveFrontendUrl=o,exports.getRemoteServerUrl=async function(e){if(process.env.REMOTE_SERVER_URL)return process.env.REMOTE_SERVER_URL
|
|
6860
6872
|
try{const{createConfigManager:r}=await t.e(60).then(()=>t.t(49060,23)),s=r(e)
|
|
6861
6873
|
return(await s.getInstallationConfig()).remoteServerUrl||"https://api.alchemylabcode.com"}catch{return"https://api.alchemylabcode.com"}}
|
|
6862
|
-
const
|
|
6863
|
-
class
|
|
6864
|
-
exports.ProjectRefused=
|
|
6874
|
+
const i=t(79896),a=t(91943),c=t(16928),l=t(40390),d=t(18984),u=t(62366),h=t(73962),p=t(81187),f=t(58824),m=t(66800),g=t(14139),w=t(37776),y=t(71879),v=t(11773),b=t(20990)
|
|
6875
|
+
class k extends Error{constructor(e){super(e.join("\n")),this.lines=e}}let S
|
|
6876
|
+
exports.ProjectRefused=k},89122(e,exports,t){"use strict"
|
|
6865
6877
|
function r(e,t=process.env){const r=Number(t.ALCHEMY_DEV_WORDS_DELAY_MS??0)
|
|
6866
6878
|
if(!Number.isFinite(r)||r<=0)return 0
|
|
6867
6879
|
let s=""
|
|
@@ -7195,21 +7207,7 @@ const o=Array.isArray(n?.data?.wakes)?n.data.wakes:[]
|
|
|
7195
7207
|
for(const e of o)"string"==typeof e.projectId&&("work"!==e.reason&&"start"!==e.reason&&"stop"!==e.reason||this.opts.wake(e.projectId,e.reason))
|
|
7196
7208
|
const i=Array.isArray(n?.data?.creates)?n.data.creates:[]
|
|
7197
7209
|
for(const e of i)"string"==typeof e.create&&this.opts.create?.(e.create)}catch(t){if(this.stopped)return
|
|
7198
|
-
e=Math.min(Math.max(2*e,5e3),3e4),e>=3e4&&this.opts.logger.warn("The hosted service is not answering the wake poll",{error:t instanceof Error?t.message:String(t)})}}else e=6e4}}}},
|
|
7199
|
-
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
7200
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.migrationCandidates=function(e,t={}){let r
|
|
7201
|
-
try{r=JSON.parse(s.default.readFileSync(e,"utf8"))}catch{return[]}if(!Array.isArray(r))return[]
|
|
7202
|
-
const i=new Set,a=[]
|
|
7203
|
-
for(const e of r){if("string"!=typeof e?.projectId||"string"!=typeof e?.projectPath)continue
|
|
7204
|
-
const r=(0,o.checkApprovable)(e.projectPath,t)
|
|
7205
|
-
if(!r.ok||i.has(r.path))continue
|
|
7206
|
-
let c
|
|
7207
|
-
try{c=JSON.parse(s.default.readFileSync(n.default.join(r.path,".alchemy_lab","project.json"),"utf8"))}catch{continue}if(c?.id!==e.projectId)continue
|
|
7208
|
-
const l=c?.metadata?.basePath
|
|
7209
|
-
if("string"!=typeof l)continue
|
|
7210
|
-
let d
|
|
7211
|
-
try{d=s.default.realpathSync(l)}catch{continue}d===r.path&&(i.add(r.path),a.push({path:r.path,dev:r.dev,ino:r.ino,projectId:e.projectId,name:"string"==typeof c?.name&&c.name?c.name:n.default.basename(r.path)}))}return a.sort((e,t)=>e.path.localeCompare(t.path))}
|
|
7212
|
-
const s=r(t(79896)),n=r(t(16928)),o=t(79616)},67518(e,exports,t){"use strict"
|
|
7210
|
+
e=Math.min(Math.max(2*e,5e3),3e4),e>=3e4&&this.opts.logger.warn("The hosted service is not answering the wake poll",{error:t instanceof Error?t.message:String(t)})}}else e=6e4}}}},67518(e,exports,t){"use strict"
|
|
7213
7211
|
function r(e){return String(e).replace(/[\u0000-\u0009\u000b-\u001f\u007f]/g,"").split("\n").map(e=>`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(" & return & ")}function s(e,t){for(const r of String(t.PATH??"").split(":")){if(!r)continue
|
|
7214
7212
|
const t=a.default.join(r,e)
|
|
7215
7213
|
try{return i.default.accessSync(t,i.default.constants.X_OK),t}catch{}}return null}var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
@@ -7395,16 +7393,16 @@ r.close()
|
|
|
7395
7393
|
const n=s.trim().toLowerCase()
|
|
7396
7394
|
return""===n?t:"y"===n||"yes"===n}async function o(e,t){const s={}
|
|
7397
7395
|
process.env.ALCHEMY_LAB_HOME&&(s.ALCHEMY_LAB_HOME=process.env.ALCHEMY_LAB_HOME)
|
|
7398
|
-
try{const n=await(0,
|
|
7399
|
-
console.log(n.loaded?`AlchemyLab will start
|
|
7400
|
-
try{r=await(0,
|
|
7396
|
+
try{const n=await(0,U.installLoginItem)({nodePath:process.execPath,cliPath:r(),remoteServerUrl:t.remoteServerUrl,envPath:process.env.PATH??"/usr/bin:/bin",env:s,logPath:e.log})
|
|
7397
|
+
console.log(n.loaded?`AlchemyLab will start every time this computer starts (${n.file}).`:`Written to ${n.file}, but it could not be loaded now: ${n.detail??"no reason given"}. It loads the next time this computer starts.`),"darwin"===process.platform&&console.log('Started with the computer, macOS may ask once whether "node" may use a project in Documents, Desktop or Downloads. Answer it at the computer.')}catch(e){console.error(`Could not set AlchemyLab to start at login: ${e instanceof Error?e.message:String(e)}`)}}async function i(e,t){await(0,x.isServiceRunning)(e)||(console.log("Starting the AlchemyLab service..."),await(0,x.startServiceInBackground)(e,t,r())||(console.error(`\u274c The AlchemyLab service did not start. Its log: ${e.log}`),process.exit(1)))}function a(e){return e.state}async function c(e){let t,r
|
|
7398
|
+
try{r=await(0,x.request)(e,"ping"),t=await(0,x.request)(e,"status")}catch{return void console.log("AlchemyLab service: not running. `alchemy service` starts it.")}console.log(`AlchemyLab service: running (pid ${r.pid}) for ${r.remote}`)
|
|
7401
7399
|
const s=t.projects??[]
|
|
7402
7400
|
if(0===s.length)return void console.log(" No projects on this computer yet. Run `alchemy` in a folder to add it.")
|
|
7403
7401
|
const n=Math.max(8,...s.map(e=>e.name.length))+2
|
|
7404
7402
|
console.log(` ${"PROJECT".padEnd(n)}${"STATE".padEnd(10)}${"PORT".padEnd(7)}FOLDER`)
|
|
7405
7403
|
for(const e of s)console.log(` ${e.name.padEnd(n)}${a(e).padEnd(10)}${String(e.port??"").padEnd(7)}${e.path}`),e.reason&&console.log(` ${"".padEnd(n)}${e.reason}`)
|
|
7406
7404
|
s.some(e=>"parked"===e.state||"stopped"===e.state)&&console.log(" A parked or stopped project connects when you open it in the app.")}function l(e){console.error(`\u274c ${e.reason??"It did not start."}`)
|
|
7407
|
-
for(const t of(e.lines??[]).slice(-8))console.error(` ${t}`)}function d(e){const t=async(t,r={},s=12e4)=>{const n=await(0,
|
|
7405
|
+
for(const t of(e.lines??[]).slice(-8))console.error(` ${t}`)}function d(e){const t=async(t,r={},s=12e4)=>{const n=await(0,x.request)(e,"new-computer",{op:t,...r},s)
|
|
7408
7406
|
if(!n.ok)throw new Error(n.reason??"The AlchemyLab service did not do it.")
|
|
7409
7407
|
return n}
|
|
7410
7408
|
return{candidates:async()=>{const e=await t("candidates",{},4e4)
|
|
@@ -7414,7 +7412,8 @@ return{say:(e="")=>console.log(e),ask:t=>new Promise(r=>e.question(`${t} `,e=>r(
|
|
|
7414
7412
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.serviceSetUp=s,exports.runService=async function(e,t){if(t.includes("--foreground"))return async function(e){const t=(0,A.servicePaths)(e);(0,A.ensurePrivateDir)(t.dir)
|
|
7415
7413
|
const s=(0,C.readServiceConfig)(t)
|
|
7416
7414
|
s||(console.error("The AlchemyLab service is not set up for this server yet. Run `alchemy service` in a terminal first."),process.exit(1))
|
|
7417
|
-
const n=(0,S.createLogger)({serviceName:"alchemy-service",console:!1,logToFile:!0,logFilePath:t.log,baseDir:t.dir}),o=new
|
|
7415
|
+
const n=(0,S.createLogger)({serviceName:"alchemy-service",console:!1,logToFile:!0,logFilePath:t.log,baseDir:t.dir}),o=new T.Supervisor({paths:t,config:s,logger:n,childModule:r(),childArgs:e=>["project-run","--project",e.path,"--expect",`${e.dev}:${e.ino}`],childEnv:()=>({...(0,C.childEnv)((0,C.readServiceConfig)(t)??s),ALCHEMY_MACHINE_ID:(0,p.machineIdentity)(t).id}),approvalEnv:{installDir:H},version:(0,j.ownVersion)()??void 0,onChange:()=>u?.noteChanged(),cap:()=>(0,C.readServiceConfig)(t)?.cap??(0,T.defaultCap)(),machineId:(0,p.machineIdentity)(t).id,confirmIdentity:async()=>(await c).register(),signedOut:()=>!0===u?.signedOut,startAsNew:(0,q.startAsNewProject)({remoteServerUrl:s.remoteServerUrl,machineId:(0,p.machineIdentity)(t).id,getToken:async()=>(await k.getToken())?.token}),connected:()=>i()}),i=async()=>{k.forgetCachedSignIn()
|
|
7416
|
+
const e=(e,t)=>Promise.race([e,new Promise(e=>{const r=setTimeout(()=>e(!1),t)
|
|
7418
7417
|
r.unref?.()})]),t=await e(u?u.register(1e4):Promise.resolve(!1),12e3),r=await e(h?h.connectNow():Promise.resolve(!1),12e3)
|
|
7419
7418
|
return!0===t&&!0===r}
|
|
7420
7419
|
let a=()=>{}
|
|
@@ -7422,43 +7421,43 @@ const c=new Promise(e=>{a=e})
|
|
|
7422
7421
|
let l,d,u,h,g,w
|
|
7423
7422
|
const v=async(e=0)=>{l?.(),await(u?.stop().catch(()=>{})),h?.stop(),w?.stop(),g?.stop(),d?.close(),await o.stop(),process.exit(e)}
|
|
7424
7423
|
process.on("SIGTERM",()=>{v(0)}),process.on("SIGINT",()=>{v(0)})
|
|
7425
|
-
try{await o.start(()=>{v(0)})}catch(e){throw e instanceof
|
|
7426
|
-
await k.initialize(),l=(0,
|
|
7424
|
+
try{await o.start(()=>{v(0)})}catch(e){throw e instanceof T.AlreadyRunningError&&(console.error(e.message),process.exit(0)),e}const k=new I.TokenManager(n,t.dir,{remoteServerUrl:s.remoteServerUrl,allowInteractiveAuth:!1})
|
|
7425
|
+
await k.initialize(),l=(0,M.startSignInWatchdog)(k,e=>n.info(e)),u=new D.MachineLink({paths:t,remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await k.getToken())?.token,projects:()=>o.status(),wake:(e,t)=>o.wake(e,t),archived:e=>o.setArchived(e),answered:(0,q.onListAnswered)({supervisor:o,remoteServerUrl:s.remoteServerUrl,machineId:(0,p.machineIdentity)(t).id,getToken:async()=>(await k.getToken())?.token,logger:n}),version:(0,j.ownVersion)()??void 0,create:e=>{R.create(e,!1).then(t=>n.info("A folder asked for from another device",{name:e,ok:t.ok,reason:t.reason}))},logger:n}),a(u),u.start(),h=new f.ContentLink({paths:t,remoteServerUrl:s.remoteServerUrl,privateData:!0===(0,C.readServiceConfig)(t)?.privateData,getToken:async()=>(await k.getToken())?.token,projectIds:()=>o.status().map(e=>e.projectId).filter(e=>"string"==typeof e),logger:n}),h.start(),g=new y.WordsBackup({store:h.store,stateFile:b.default.join(t.dir,"words-backup.json"),settings:()=>(0,y.backupSettingsOf)((0,C.readServiceConfig)(t)?.wordsBackup),privateData:()=>h?.privateData??!0===(0,C.readServiceConfig)(t)?.privateData,logger:n}),g.start()
|
|
7427
7426
|
const $=async e=>{const r=h?await h.setPrivateData(e):{privateData:e,switching:!1},s=(0,C.readServiceConfig)(t)
|
|
7428
7427
|
return s&&s.privateData!==r.privateData&&(0,C.writeServiceConfig)(t,{...s,privateData:r.privateData}),n.info(r.privateData?"This install keeps its content on this computer":"This install keeps its content on the hosted service"),r}
|
|
7429
7428
|
o.handle("connect-now",async()=>({ok:await i()})),o.handle("move-words-here",async()=>{const e=await $(!0)
|
|
7430
7429
|
return{ok:!e.refused,reason:e.refused,privateData:e.privateData,switching:e.switching}})
|
|
7431
7430
|
const E=new m.NewComputer({machineId:(0,p.machineIdentity)(t).id,remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await k.getToken())?.token,channelKey:()=>(0,f.channelKey)(t),store:h.store,backup:g,defaultFolder:()=>(0,y.backupSettingsOf)((0,C.readServiceConfig)(t)?.wordsBackup).folder})
|
|
7432
7431
|
let _
|
|
7433
|
-
o.handle("new-computer",e=>(0,m.newComputerCommand)(E,e)),o.handle("computer-gone",async e=>h?await h.sayGone(String(e.machineId??"")):{ok:!1,reason:"The words channel is not running."}),o.handle("pair-channel",async e=>h?await h.pairAgain(String(e.code??"")):{ok:!1,reason:"The words channel is not running."}),w=new
|
|
7434
|
-
const
|
|
7432
|
+
o.handle("new-computer",e=>(0,m.newComputerCommand)(E,e)),o.handle("computer-gone",async e=>h?await h.sayGone(String(e.machineId??"")):{ok:!1,reason:"The words channel is not running."}),o.handle("pair-channel",async e=>h?await h.pairAgain(String(e.code??"")):{ok:!1,reason:"The words channel is not running."}),w=new N.ScheduleWaker({remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await k.getToken())?.token,parked:()=>o.status().filter(e=>"parked"===e.state&&e.projectId).map(e=>e.projectId),wake:e=>o.wake(e,"work"),logger:n}),w.start(),(0,P.resolveFrontendUrl)(s.remoteServerUrl).then(e=>{try{/^https?:\/\//.test(e)&&(_=new URL(e).origin)}catch{}})
|
|
7433
|
+
const x=(0,L.nativeDialogs)(),R=new F.Adding({supervisor:o,dialogs:x,logger:n,approvalEnv:{installDir:H}}),U=e=>{const r=(0,C.readServiceConfig)(t)
|
|
7435
7434
|
if(!r)throw new Error("the service has no configuration");(0,C.writeServiceConfig)(t,{...r,wordsBackup:{...r.wordsBackup??{},...e}})}
|
|
7436
|
-
d=await(0,
|
|
7437
|
-
return{settings:e,state:g.state(),runs:g.ownRuns(),folderAvailable:g.folderAvailable(e.folder)}},set:e=>
|
|
7438
|
-
return e&&
|
|
7435
|
+
d=await(0,O.startDoor)({supervisor:o,adding:R,wordsBackup:{view:()=>{const e=(0,y.backupSettingsOf)((0,C.readServiceConfig)(t)?.wordsBackup)
|
|
7436
|
+
return{settings:e,state:g.state(),runs:g.ownRuns(),folderAvailable:g.folderAvailable(e.folder)}},set:e=>U(e),pick:async()=>{const e=await x.pickFolder("Choose a folder for backups of your words (one your backup tool or iCloud Drive covers)")
|
|
7437
|
+
return e&&U({folder:e}),e},run:()=>g.run(),restore:e=>g.restore(e)},privateData:{get:()=>h?.privateData??!0===(0,C.readServiceConfig)(t)?.privateData,set:$},newComputer:{api:E,pickBackupFolder:()=>x.pickFolder('Choose the "AlchemyLab Backups" folder of your old computer (restored or copied onto this one)')},verifyMachineTicket:async e=>{const t=(await k.getToken())?.token
|
|
7439
7438
|
if(!t)throw new Error("this computer is not signed in")
|
|
7440
|
-
const r=await fetch(`${s.remoteServerUrl}/api/verify-machine-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,"X-Client-Type":"alchemy-local-service",...(0,
|
|
7439
|
+
const r=await fetch(`${s.remoteServerUrl}/api/verify-machine-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,"X-Client-Type":"alchemy-local-service",...(0,B.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:e}),signal:AbortSignal.timeout(1e4)})
|
|
7441
7440
|
if(r.status>=400&&r.status<500)return{ok:!1}
|
|
7442
7441
|
if(!r.ok)throw new Error(`HTTP ${r.status}`)
|
|
7443
7442
|
const n=await r.json(),o=n?.data??n
|
|
7444
7443
|
return{ok:!0===o?.ok,expiresAt:o?.expiresAt??null}},logger:n,remoteServerUrl:s.remoteServerUrl,version:(0,j.ownVersion)()??void 0,machineId:u.id,frontendOrigin:()=>_,verifyTicket:async(e,t)=>{const r=(await k.getToken())?.token
|
|
7445
7444
|
if(!r)throw new Error("this computer is not signed in")
|
|
7446
|
-
const n=await fetch(`${s.remoteServerUrl}/api/projects/${encodeURIComponent(e)}/verify-local-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`,"X-Client-Type":"alchemy-local-service",...(0,
|
|
7445
|
+
const n=await fetch(`${s.remoteServerUrl}/api/projects/${encodeURIComponent(e)}/verify-local-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`,"X-Client-Type":"alchemy-local-service",...(0,B.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:t}),signal:AbortSignal.timeout(1e4)})
|
|
7447
7446
|
if(n.status>=400&&n.status<500)return{ok:!1}
|
|
7448
7447
|
if(!n.ok)throw new Error(`HTTP ${n.status}`)
|
|
7449
7448
|
const o=await n.json(),i=o?.data??o
|
|
7450
|
-
return{ok:!0===i?.ok,expiresAt:i?.expiresAt??null}}},s.doorPort??(0,
|
|
7449
|
+
return{ok:!0===i?.ok,expiresAt:i?.expiresAt??null}}},s.doorPort??(0,O.defaultDoorPort)(s.remoteServerUrl)),await new Promise(()=>{})}(e)
|
|
7451
7450
|
const s=(0,A.servicePaths)(e)
|
|
7452
|
-
if("install"===t[0]||"uninstall"===t[0]){if("uninstall"===t[0]){const t=await(0,
|
|
7453
|
-
return void console.log(t.removed?`AlchemyLab no longer starts when
|
|
7454
|
-
return r||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),void await o(s,r)}if("stop"===t[0]){if(!await(0,
|
|
7455
|
-
await(0,
|
|
7456
|
-
for(let e=0;e<240&&await(0,
|
|
7451
|
+
if("install"===t[0]||"uninstall"===t[0]){if("uninstall"===t[0]){const t=await(0,U.uninstallLoginItem)(e)
|
|
7452
|
+
return void console.log(t.removed?`AlchemyLab no longer starts when this computer starts (${t.file} removed).`:"AlchemyLab was not set to start at login.")}const r=(0,C.readServiceConfig)(s)
|
|
7453
|
+
return r||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),void await o(s,r)}if("stop"===t[0]){if(!await(0,x.isServiceRunning)(s))return void console.log("The AlchemyLab service is not running.")
|
|
7454
|
+
await(0,x.request)(s,"shutdown")
|
|
7455
|
+
for(let e=0;e<240&&await(0,x.isServiceRunning)(s);e++)await new Promise(e=>setTimeout(e,250))
|
|
7457
7456
|
return void console.log("The AlchemyLab service stopped, and with it every project it ran. They come back when it starts again.")}if("computer-gone"===t[0]){const e=t.slice(1).join(" ").trim()
|
|
7458
|
-
e||(console.error("Usage: alchemy service computer-gone <name or id> (the computer that is gone, by its name in Settings, General, Computers; Copy id there gives its id)"),process.exit(1)),await(0,
|
|
7459
|
-
const r=await(0,
|
|
7460
|
-
return r.ok||(console.error(`Not recorded: ${r.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("Recorded: that computer is gone. Its projects can now be moved to this computer without a backup: `alchemy service new-computer`.")}if("pair"===t[0]){t[1]||(console.error("Usage: alchemy service pair <code> (Settings, Data in the app gives the code)"),process.exit(1)),await(0,
|
|
7461
|
-
const e=await(0,
|
|
7457
|
+
e||(console.error("Usage: alchemy service computer-gone <name or id> (the computer that is gone, by its name in Settings, General, Computers; Copy id there gives its id)"),process.exit(1)),await(0,x.isServiceRunning)(s)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
|
|
7458
|
+
const r=await(0,x.request)(s,"computer-gone",{machineId:e})
|
|
7459
|
+
return r.ok||(console.error(`Not recorded: ${r.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("Recorded: that computer is gone. Its projects can now be moved to this computer without a backup: `alchemy service new-computer`.")}if("pair"===t[0]){t[1]||(console.error("Usage: alchemy service pair <code> (Settings, Data in the app gives the code)"),process.exit(1)),await(0,x.isServiceRunning)(s)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
|
|
7460
|
+
const e=await(0,x.request)(s,"pair-channel",{code:t[1]})
|
|
7462
7461
|
return e.ok||(console.error(`Not paired: ${e.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("This computer is paired again: the hosted service accepts its new channel key.")}if("new-computer"===t[0]){const e=(0,C.readServiceConfig)(s)
|
|
7463
7462
|
return e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(s,e),void await async function(e){process.stdin.isTTY||(console.error("`alchemy service new-computer` asks questions: run it in a terminal."),process.exit(1))
|
|
7464
7463
|
const t=d(e),r=u()
|
|
@@ -7467,54 +7466,51 @@ if("moved"===s||"no-candidates"===s){const s=v.default.realpathSync(process.cwd(
|
|
|
7467
7466
|
if(i){const t=(0,m.attachFolder)(s,i,(0,p.machineIdentity)(e).id)
|
|
7468
7467
|
r.say(t.ok?`This folder is ${i.name||"that project"} now. Run \`alchemy\` here to start it.`:`Not done: ${t.reason}`)}}}catch(e){r.say(`Stopped: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1}finally{r.close()}}(s)}if("move-words-here"===t[0]){const e=(0,C.readServiceConfig)(s)
|
|
7469
7468
|
e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(s,e)
|
|
7470
|
-
const t=await(0,
|
|
7469
|
+
const t=await(0,x.request)(s,"move-words-here",{},9e4)
|
|
7471
7470
|
return t.ok&&!0===t.privateData||(console.error(`Not started: ${t.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("This computer keeps the words of its projects from now on. The words the hosted service holds for them are moving here; Settings, Data shows the count still held, per project.")}(0,A.ensurePrivateDir)(s.dir)
|
|
7472
7471
|
let a=(0,C.readServiceConfig)(s)
|
|
7473
7472
|
const l=!a
|
|
7474
|
-
a?process.env.ALCHEMY_OPENCODE_DIR&&process.env.ALCHEMY_OPENCODE_DIR!==a.opencodeDir&&(a={...a,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR},(0,C.writeServiceConfig)(s,a),console.log(`Projects will run with the OpenCode build in ${a.opencodeDir} from their next start.`)):(a={remoteServerUrl:e,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR||void 0,privateData:!0,createdAt:(new Date).toISOString()},(0,C.writeServiceConfig)(s,a),console.log(`AlchemyLab service set up for ${e}.`),console.log('Your projects\' words (requests, messages, documents, results) are kept on this computer, and backed up daily to the folder "AlchemyLab Backups" in your home folder. Keep that folder in your own backup: a lost disk without it loses the words.')),l&&"ready"!==(await(0,E.runDockerPreflight)({interactive:!0===process.stdin.isTTY&&!0===process.stdout.isTTY})).status&&console.log("Running without Docker: agents fall back to the host sandbox. AlchemyLab picks Docker up when it is there."),!l||!process.stdin.isTTY||"darwin"!==process.platform&&"linux"!==process.platform||(0,
|
|
7475
|
-
const h=await(0,
|
|
7476
|
-
k.exists()||h||
|
|
7477
|
-
if(0!==t.length)if(process.stdin.isTTY){console.log(""),console.log("These projects already have AlchemyLab on this computer:")
|
|
7478
|
-
for(const e of t)console.log(` ${e.name.padEnd(24)} ${e.path}`)
|
|
7479
|
-
if(await n(`Keep these ${t.length} projects in AlchemyLab?`,!0)){for(const r of t)e.add({path:r.path,dev:r.dev,ino:r.ino,projectId:r.projectId,name:r.name,wanted:"stopped",addedAt:(new Date).toISOString(),addedVia:"migration"})
|
|
7480
|
-
console.log("Added. Each starts when you open it in the app, or with `alchemy start <name>`.")}else e.touch(),console.log("None added. Run `alchemy` in a folder to add it.")}else console.log(`${t.length} existing projects can be added; run \`alchemy service\` in a terminal to choose.`)
|
|
7481
|
-
else e.touch()}(k),h||await(0,M.startServiceInBackground)(s,a,r())||(console.error(`\u274c The AlchemyLab service did not start. Its log: ${s.log}`),process.exit(1)),await c(s)},exports.printStatus=c,exports.runControl=async function(e,t,r){const o=function(e){const t=s(e)
|
|
7473
|
+
a?process.env.ALCHEMY_OPENCODE_DIR&&process.env.ALCHEMY_OPENCODE_DIR!==a.opencodeDir&&(a={...a,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR},(0,C.writeServiceConfig)(s,a),console.log(`Projects will run with the OpenCode build in ${a.opencodeDir} from their next start.`)):(a={remoteServerUrl:e,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR||void 0,privateData:!0,createdAt:(new Date).toISOString()},(0,C.writeServiceConfig)(s,a),console.log(`AlchemyLab service set up for ${e}.`),console.log('Your projects\' words (requests, messages, documents, results) are kept on this computer, and backed up daily to the folder "AlchemyLab Backups" in your home folder. Keep that folder in your own backup: a lost disk without it loses the words.')),l&&"ready"!==(await(0,E.runDockerPreflight)({interactive:!0===process.stdin.isTTY&&!0===process.stdout.isTTY})).status&&console.log("Running without Docker: agents fall back to the host sandbox. AlchemyLab picks Docker up when it is there."),!l||!process.stdin.isTTY||"darwin"!==process.platform&&"linux"!==process.platform||(0,U.loginItemInstalled)(e)||(await n("Start AlchemyLab every time this computer starts?",!0)?await o(s,a):console.log("Not set. `alchemy service install` sets it later."))
|
|
7474
|
+
const h=await(0,x.isServiceRunning)(s),k=new _.ProjectsStore(s.projects)
|
|
7475
|
+
k.exists()||h||k.touch(),h||await(0,x.startServiceInBackground)(s,a,r())||(console.error(`\u274c The AlchemyLab service did not start. Its log: ${s.log}`),process.exit(1)),await c(s)},exports.printStatus=c,exports.runControl=async function(e,t,r){const o=function(e){const t=s(e)
|
|
7482
7476
|
return t||(console.error("The AlchemyLab service is not set up yet. Run `alchemy service` once to set it up."),process.exit(1)),t.paths}(e),a=(0,C.readServiceConfig)(o)
|
|
7483
7477
|
if("status"===t)return c(o)
|
|
7484
|
-
if("approve"===t){const e=(await(0,
|
|
7478
|
+
if("approve"===t){const e=(await(0,x.request)(o,"approvals")).approvals??[]
|
|
7485
7479
|
if(0===e.length)return void console.log("Nothing is waiting for your approval.")
|
|
7486
7480
|
for(const t of e){console.log(""),console.log(t.message)
|
|
7487
7481
|
const e=await n("Approve?",!1)
|
|
7488
|
-
await(0,
|
|
7482
|
+
await(0,x.request)(o,"approve",{approvalId:t.id,yes:e}),console.log(e?"Approved.":"Declined.")}return}await i(o,a)
|
|
7489
7483
|
const d=function(e){if(!e)return v.default.realpathSync(process.cwd())
|
|
7490
7484
|
if(e.startsWith("/")||e.startsWith(".")||e.startsWith("~")){const t=e.startsWith("~")?b.default.join(process.env.HOME??"",e.slice(1)):b.default.resolve(e)
|
|
7491
7485
|
try{return v.default.realpathSync(t)}catch{return t}}return e}(r)
|
|
7492
|
-
if("add"===t){const e=await(0,
|
|
7486
|
+
if("add"===t){const e=await(0,x.request)(o,"add",{path:d})
|
|
7493
7487
|
return e.ok||(console.error(`\u274c ${e.reason}`),process.exit(1)),void console.log(e.added?`Added ${e.project.path}.`:`${e.project.path} is already on this computer.`)}if("start"===t||"restart"===t){console.log(`${"start"===t?"Starting":"Restarting"} ${b.default.basename(d)}...`)
|
|
7494
|
-
const e=await(0,
|
|
7488
|
+
const e=await(0,x.request)(o,t,{ref:d},18e4)
|
|
7495
7489
|
for(const t of e.lines??[])e.ok&&console.log(t)
|
|
7496
|
-
return e.ok||(l(e),process.exit(e.pending?0:1)),void console.log(`Running, action server on port ${e.port}.`)}if("stop"===t||"remove"===t){const e=await(0,
|
|
7490
|
+
return e.ok||(l(e),process.exit(e.pending?0:1)),void console.log(`Running, action server on port ${e.port}.`)}if("stop"===t||"remove"===t){const e=await(0,x.request)(o,t,{ref:d},12e4)
|
|
7497
7491
|
if(e.ok||(console.error(`\u274c ${e.reason}`),process.exit(1)),console.log("stop"===t?"Stopped.":"Removed from this computer. The folder and its history are untouched."),"remove"===t&&Array.isArray(e.keptWork)&&e.keptWork.length){console.log(`Kept, because ${1===e.keptWork.length?"it holds":"they hold"} work that was never committed:`)
|
|
7498
7492
|
for(const t of e.keptWork)console.log(` ${t}`)}return}console.error(`Unknown command ${t}`),process.exit(1)},exports.runSupervisedDefault=async function(e,r){const{paths:s,cfg:n}=r,o=v.default.realpathSync(process.cwd()),a=(0,S.createLogger)({serviceName:"alchemy-cli",console:!1,logToFile:!0,logFilePath:s.log,baseDir:s.dir}),c=new I.TokenManager(a,o,{remoteServerUrl:e,allowInteractiveAuth:!1})
|
|
7499
7493
|
await c.initialize()
|
|
7500
|
-
const h=await(0,
|
|
7501
|
-
await i(s,n)
|
|
7494
|
+
const h=await(0,M.ensureSignedIn)(c)
|
|
7495
|
+
await i(s,n)
|
|
7496
|
+
const p=await(0,x.request)(s,"connect-now",{},3e4).catch(()=>{})
|
|
7497
|
+
p?.ok||await(0,x.request)(s,"connect-now",{},3e4).catch(()=>{}),await async function(e){const t=b.default.join(e.dir,"new-computer-asked.json")
|
|
7502
7498
|
if(!process.stdin.isTTY||!process.stdout.isTTY||v.default.existsSync(t))return
|
|
7503
|
-
if(((await(0,
|
|
7499
|
+
if(((await(0,x.request)(e,"status")).projects??[]).length>0)return
|
|
7504
7500
|
const r=d(e),{candidates:s}=await r.candidates()
|
|
7505
7501
|
if(0===s.length)return
|
|
7506
7502
|
const n=u()
|
|
7507
7503
|
try{await(0,g.runNewComputerFlow)(r,n,{explicit:!1}),v.default.writeFileSync(t,JSON.stringify({askedAt:(new Date).toISOString()})+"\n",{mode:384}),n.say("")}finally{n.close()}}(s).catch(()=>{})
|
|
7508
|
-
const
|
|
7509
|
-
|
|
7510
|
-
const
|
|
7511
|
-
for(const e of
|
|
7512
|
-
|
|
7513
|
-
const
|
|
7514
|
-
if(console.log(""),console.log(` Alchemy UI: \x1b]8;;${
|
|
7515
|
-
console.log(" \u{1f310} First run: opening AlchemyLab in your browser (60-second welcome tour)..."),console.log(" No AI provider connected yet? That is the next step there (Settings > Providers)."),console.log(""),(0,$.openBrowser)(e,"Open the Alchemy UI link above in a browser on any device to continue there.")}const{InteractiveShell:
|
|
7516
|
-
await
|
|
7517
|
-
const p=t(60134),f=t(89122),m=t(98074),g=t(62671),w=t(14139),y=t(79564),v=h(t(79896)),b=h(t(16928)),k=h(t(23785)),S=t(12782),I=t(11253),$=t(90629),j=t(42403),E=t(88918),A=t(18944),C=t(30030),_=t(52800),T=t(
|
|
7504
|
+
const f=await(0,x.request)(s,"add",{path:o})
|
|
7505
|
+
f.ok||(console.error(`\u274c ${f.reason}`),process.exit(1))
|
|
7506
|
+
const m=await(0,x.request)(s,"start",{ref:o},18e4)
|
|
7507
|
+
for(const e of m.lines??[])console.log(e)
|
|
7508
|
+
m.ok||(l(m),process.exit(1)),(0,R.printBanner)(),console.log(`\u{1f4c1} Project: ${o}`),console.log(""),console.log(" Action server .... port "+m.port)
|
|
7509
|
+
const w=await(0,P.resolveFrontendUrl)(e)
|
|
7510
|
+
if(console.log(""),console.log(` Alchemy UI: \x1b]8;;${w}\x07${w}\x1b]8;;\x07`),console.log(""),m.firstRun){const e=`${w.replace(/\/$/,"")}${m.projectId?`/projects/${m.projectId}`:""}/timeline?welcome=1`
|
|
7511
|
+
console.log(" \u{1f310} First run: opening AlchemyLab in your browser (60-second welcome tour)..."),console.log(" No AI provider connected yet? That is the next step there (Settings > Providers)."),console.log(""),(0,$.openBrowser)(e,"Open the Alchemy UI link above in a browser on any device to continue there.")}const{InteractiveShell:y}=await t.e(994).then(()=>t.t(75994,23)),k=new y({projectPath:o,logger:a,remoteServerUrl:e,authToken:h,projectId:m.projectId,hasCode:!0===m.hasCode})
|
|
7512
|
+
await k.start(async()=>{console.log(""),console.log(`AlchemyLab keeps running ${b.default.basename(o)}. \`alchemy stop\` in this folder stops it.`),process.exit(0)})}
|
|
7513
|
+
const p=t(60134),f=t(89122),m=t(98074),g=t(62671),w=t(14139),y=t(79564),v=h(t(79896)),b=h(t(16928)),k=h(t(23785)),S=t(12782),I=t(11253),$=t(90629),j=t(42403),E=t(88918),A=t(18944),C=t(30030),_=t(52800),T=t(81480),x=t(56257),M=t(19071),R=t(64504),P=t(38778),O=t(35382),D=t(60134),q=t(71918),N=t(36968),F=t(76231),L=t(67518),U=t(99269),B=t(11773),H=(()=>{let e=__dirname
|
|
7518
7514
|
for(let t=0;t<6;t++){try{if("alchemylab"===JSON.parse(v.default.readFileSync(b.default.join(e,"package.json"),"utf8"))?.name)return e}catch{}const t=b.default.dirname(e)
|
|
7519
7515
|
if(t===e)break
|
|
7520
7516
|
e=t}})()},30030(e,exports,t){"use strict"
|
|
@@ -7523,13 +7519,13 @@ if(t&&"string"==typeof t.remoteServerUrl&&/^https?:\/\//.test(t.remoteServerUrl)
|
|
|
7523
7519
|
return e.opencodeDir?r.ALCHEMY_OPENCODE_DIR=e.opencodeDir:delete r.ALCHEMY_OPENCODE_DIR,r}
|
|
7524
7520
|
const r=t(18944)},19071(e,exports,t){"use strict"
|
|
7525
7521
|
async function r(e,t=3e5){return new Promise(r=>{const s=setInterval(async()=>{await e.hasValidToken()&&(clearInterval(s),r(!0))},2e3)
|
|
7526
|
-
setTimeout(()=>{clearInterval(s),r(!1)},t)})}exports.waitForAuth=r,exports.ensureSignedIn=async function(e){let t=await e.getToken()
|
|
7527
|
-
if(t?.token)return t.token
|
|
7528
|
-
console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
|
|
7529
|
-
const
|
|
7530
|
-
console.log(`\u{1f310} ${
|
|
7531
|
-
const
|
|
7532
|
-
return console.log(""),console.log(
|
|
7522
|
+
setTimeout(()=>{clearInterval(s),r(!1)},t)})}exports.waitForAuth=r,exports.ensureSignedIn=async function(e){let t=await e.getToken(),n=!1
|
|
7523
|
+
if(t?.token&&await e.signInRefused()&&(n=!0,await e.removeToken(),t=null),t?.token)return t.token
|
|
7524
|
+
console.log(""),console.log(n?"\u{1f511} The sign-in on this computer is no longer accepted. Opening browser to sign in again...":"\u{1f511} Not logged in. Opening browser to authenticate...")
|
|
7525
|
+
const o=await e.startAuthenticationFlow()
|
|
7526
|
+
console.log(`\u{1f310} ${o.authUrl}`)
|
|
7527
|
+
const i=(0,s.openBrowser)(o.authUrl)
|
|
7528
|
+
return console.log(""),console.log(i?"\u{1f446} Browser window opened. Complete login there.":"\u{1f446} Complete the login in a browser, then return here."),console.log("\u23f1\ufe0f Waiting for authentication..."),await r(e)||(console.error("\u274c Authentication timed out. Run `alchemy` again to retry."),process.exit(1)),t=await e.getToken(),console.log(""),t?.token},exports.startSignInWatchdog=function(e,t,n=3e5){let o=!1
|
|
7533
7529
|
const i=setInterval(async()=>{if(!o)try{if(await e.hasValidToken())return
|
|
7534
7530
|
o=!0,t("Sign-in expired; opening a browser to sign in again")
|
|
7535
7531
|
const n=await e.startAuthenticationFlow();(0,s.openBrowser)(n.authUrl,`Sign in again: ${n.authUrl}`),t(await r(e)?"Signed in again":"Sign-in not completed; asking again in five minutes")}catch(e){t(`Sign-in check failed: ${e instanceof Error?e.message:String(e)}`)}finally{o=!1}},n)
|
|
@@ -7908,8 +7904,8 @@ for(const[r,s]of Object.entries(e.tools||{})){const e=s,n=r.replace(/-/g,"_")
|
|
|
7908
7904
|
t[v.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${k}_${n}`]=e}if(!K)for(const e of v.WEB_TOOLS)t[v.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${k}_${e}`]=!1
|
|
7909
7905
|
Object.assign(t,(0,c.agentToolGrants)(B,e.name,H)),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name||"reviewer"===e.name||"criteria-reviewer"===e.name)&&void 0===t.task&&(t.task=!1),W.agent[e.name]={tools:t,...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}W.agent.general={...W.agent.general,disable:!0},W.agent.explore={...W.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[`${k}_search`]:!0,[`${k}_read_files`]:!0,[`${k}_view_asset`]:!0,[`${k}_query_history`]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
|
|
7910
7906
|
const e=this.buildExploreMarkdown(F)
|
|
7911
|
-
this.setAgentMarkdown("explore",e),this.logger.info("Explore agent override held in memory"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(W.agent).length})}const
|
|
7912
|
-
|
|
7907
|
+
this.setAgentMarkdown("explore",e),this.logger.info("Explore agent override held in memory"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(W.agent).length})}const z=e=>"string"==typeof e&&e.includes(".opencode/plugin/"),V=(await this.fetchPluginManifest())["opencode-claude-auth"],Y=W.plugin,J=Array.isArray(Y)?Y.filter(e=>!z(e)):[]
|
|
7908
|
+
V&&this.applyAuthPluginPin(J,V),J.length>0?W.plugin=J:delete W.plugin
|
|
7913
7909
|
try{const e=["opencode.jsonc",".opencode/opencode.json",".opencode/opencode.jsonc",".opencode/plugin",".opencode/tool",".opencode/command"].filter(e=>(0,d.existsSync)(y.join(s,e)))
|
|
7914
7910
|
e.length>0&&this.logger.warn("This repository carries OpenCode config the AlchemyLab runtime does not load (only the generated config is read)",{ignored:e})}catch{}W.instructions=function(e,t){const r=["AGENTS.md","CLAUDE.md","CONTEXT.md"].map(t=>(0,u.join)(e,t)).find(e=>(0,d.existsSync)(e))??(0,u.join)(e,"AGENTS.md"),s=(Array.isArray(t)?t:[]).filter(e=>"string"==typeof e&&e.trim().length>0).map(t=>t.startsWith("~/")||(0,u.isAbsolute)(t)?t:(0,u.join)(e,t))
|
|
7915
7911
|
return[...new Set([r,...s])]}(s,C.instructions)
|
|
@@ -7919,8 +7915,8 @@ const Q={...C,...W}
|
|
|
7919
7915
|
await w.mkdir(y.dirname(I),{recursive:!0}),await w.writeFile(I,JSON.stringify(Q,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:I})
|
|
7920
7916
|
try{const e=await Promise.resolve().then(()=>t.t(70857,23)),r=y.join(e.homedir(),".alchemy_lab","opencode","config"),s=y.join(r,"opencode.json")
|
|
7921
7917
|
let n=null
|
|
7922
|
-
try{n=JSON.parse(await w.readFile(s,"utf-8"))}catch{}const o=n??{},i=Array.isArray(o.plugin)?o.plugin.slice():[],a=i.filter(e=>!
|
|
7923
|
-
|
|
7918
|
+
try{n=JSON.parse(await w.readFile(s,"utf-8"))}catch{}const o=n??{},i=Array.isArray(o.plugin)?o.plugin.slice():[],a=i.filter(e=>!z(e))
|
|
7919
|
+
V&&this.applyAuthPluginPin(a,V),i.length===a.length&&!i.some((e,t)=>e!==a[t])&&n||(o.plugin=a,await w.mkdir(r,{recursive:!0}),await w.writeFile(s,JSON.stringify(o,null,2),"utf8"),this.logger.info("Updated global opencode config plugin list",{globalConfigPath:s,plugins:a}))}catch(e){this.logger.warn("Failed to update global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(T&&!l)try{let e={}
|
|
7924
7920
|
try{const t=await w.readFile(x,"utf-8")
|
|
7925
7921
|
e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=M,await w.writeFile(x,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:x}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPluginsGlobal:r,cleanupLegacyProjectPlugins:n}=await t.e(939).then(()=>t.t(49939,23)),o=await r(b??!1,this.logger,e)
|
|
7926
7922
|
o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors}),await n(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:I}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,n.getErrorMessage)(e)}),{success:!1,error:(0,n.getErrorMessage)(e)}}}}exports.MCPClientConfigurer=v,v.WEB_TOOLS=["webfetch","websearch","codesearch","capture_site","read_site","fetch_asset"],v.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","webfetch","websearch","codesearch","agent","task","question","todoread","todowrite"]),v.rerouteLogged=new Set},6688(e,exports,t){"use strict"
|
|
@@ -10008,7 +10004,8 @@ if(t.status===a.PollingStatus.POLLING)return null
|
|
|
10008
10004
|
const r=this.authFlow.getPollingUrl(e),s=await fetch(r)
|
|
10009
10005
|
if(s.ok){const e=await s.json()
|
|
10010
10006
|
if(e.authenticated&&e.token){const t=this.authFlow.parseTokenResponse(e)
|
|
10011
|
-
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
|
|
10007
|
+
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 signInRefused(){try{const e=await this.getToken()
|
|
10008
|
+
return!!e&&401===(await fetch(`${this.remoteServerUrl}/api/auth/validate`,{method:"GET",headers:{Authorization:`Bearer ${e.token}`},signal:AbortSignal.timeout(1e4)})).status}catch{return!1}}forgetCachedSignIn(){this.invalidateCache()}async validateTokenWithRemoteServer(){this.logger.debug("Validating stored token with remote server")
|
|
10012
10009
|
try{const e=await this.getToken()
|
|
10013
10010
|
if(!e)return this.logger.debug("No token available for validation"),!1
|
|
10014
10011
|
const t=`${this.remoteServerUrl}/auth/validate`
|
|
@@ -10252,7 +10249,7 @@ o&&o(e)
|
|
|
10252
10249
|
for(var a=0;a<n.length;a++)t[n[a]]=1})(s)}}})()
|
|
10253
10250
|
let s={};(()=>{"use strict"
|
|
10254
10251
|
function t(){return(0,i.ownVersion)()??"unknown"}function r(){const e=t()
|
|
10255
|
-
console.log(`\nAlchemyLab CLI v${e}\n\nUSAGE:\n alchemy [command] [options]\n\nCOMMANDS:\n (none) Start this folder's project and open its shell. The\n AlchemyLab service runs the project (the first run sets\n the service up), and leaving the shell leaves it running.\n service Set up the AlchemyLab service for this computer and start it\n ("alchemy service stop" stops it and its projects;\n "alchemy service install" starts it
|
|
10252
|
+
console.log(`\nAlchemyLab CLI v${e}\n\nUSAGE:\n alchemy [command] [options]\n\nCOMMANDS:\n (none) Start this folder's project and open its shell. The\n AlchemyLab service runs the project (the first run sets\n the service up), and leaving the shell leaves it running.\n service Set up the AlchemyLab service for this computer and start it\n ("alchemy service stop" stops it and its projects;\n "alchemy service install" starts it every time this computer starts;\n "alchemy service uninstall" stops that;\n "alchemy service pair <code>" pairs this computer again,\n with the code Settings, Data in the app gives;\n "alchemy service computer-gone <name>" says another of your\n computers is gone, by its name in Settings, General, Computers\n (or its id), so its projects can move without a backup;\n "alchemy service new-computer" is for a computer that\n replaces another one: it brings that computer's words back\n from a backup, then moves its projects here)\n status Projects on this computer and their state\n start [folder] Start a project (default: this folder)\n stop [folder] Stop a project\n restart [folder]\n add [folder] Add a folder as a project without starting it\n remove [folder] Remove a project from this computer (files are untouched)\n approve Answer requests the app made that need you at this computer\n logout Log out and remove stored auth token\n help Show this help message\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 logout # Log out\n\nNOTES:\n On startup, alchemy checks for Docker and offers to install or start it.\n Docker powers confined execution (sealed environments, experiments, test\n gates); without it, agents fall back to the host sandbox.\n\nFor more information, visit: https://alchemylabcode.com/\n`)}function n(e){try{(0,I.checkProjectIdentity)(e)}catch(e){if(e instanceof I.ProjectRefused){console.error("")
|
|
10256
10253
|
for(const t of e.lines)console.error(t)
|
|
10257
10254
|
console.error(""),process.exit(1)}throw e}}function o(){return(0,a.createEnvLogger)({serviceName:"alchemy-cli",console:!1,baseDir:process.cwd()})}let exports=s
|
|
10258
10255
|
Object.defineProperty(exports,"__esModule",{value:!0})
|