alchemylab 3.0.4 → 3.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/293.js +41 -40
- package/dist/cli.js +191 -168
- package/dist/http-server.js +98 -70
- package/dist/index.js +98 -70
- package/dist/mcp-direct.js +275 -247
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1910,8 +1910,8 @@ const r=await e.json()
|
|
|
1910
1910
|
if(!Array.isArray(r)||r.some(e=>e?.name===t))return!0}catch{return!0}if(Date.now()>=o)return!1
|
|
1911
1911
|
i||(i=!0,e.logger.info("start-worker: waiting for the agent to load in the runtime",{agent:t})),await new Promise(e=>setTimeout(e,s))}}async function n(e,t,r){let s=""
|
|
1912
1912
|
try{const r=await e.apiClient.get(`/api/projects/${e.projectId}/requests/${t}/record-for-agents`)
|
|
1913
|
-
s=String((r?.data??r)?.text??"")}catch{}try{return function(e,t){const r=Array.isArray(e?.acceptanceCriteria)?e.acceptanceCriteria:[]
|
|
1914
|
-
return[`## Description\n${String(e?.description??"").trim()||"_No description provided._"}
|
|
1913
|
+
s=String((r?.data??r)?.text??"")}catch{}try{return function(e,t){const r=Array.isArray(e?.acceptanceCriteria)?e.acceptanceCriteria:[],s=e?.roundOf?`## ${e.roundOf}'s criteria (met when it completed)\nAn answer needs nothing more. If you change the project, your completion accounts for these again, on your change.\n${(0,j.formatCriteriaWithHashes)(r).replace(/\[UNPROVEN\] /g,"")}`:`## Acceptance Criteria\n${(0,j.formatCriteriaWithHashes)(r)}`
|
|
1914
|
+
return[`## Description\n${String(e?.description??"").trim()||"_No description provided._"}`,s,...t&&t.trim()?[t.trim()]:[]].join("\n\n")}(r,s)}catch{return""}}function o(e,t,r,s){const n=[t?.roundOf?`The user wrote on the page of ${t.roundOf}, which is completed. Their message is ${e.requestId}; its record is at the end of this message. If you only answer it, what you write last in this turn is your reply to them, and nothing else is needed. If you change the project for it, the change is ${e.requestId}'s: make it and complete ${e.requestId} as you complete any request.`:`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).']]
|
|
1915
1915
|
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}`)]
|
|
1916
1916
|
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")}async function i(e,t){if(!t)return null
|
|
1917
1917
|
try{const r=(0,_.simpleGit)(e),s=await r.diffSummary([`${t}..HEAD`])
|
|
@@ -1982,31 +1982,32 @@ const I=(b.get(o)??0)>0,$=c.areaReuse&&v>0,A=I||$?await p(o):{},_=Math.max(w.get
|
|
|
1982
1982
|
C.push({...void 0!==A.contextTokens?{contextTokens:A.contextTokens}:{},...void 0!==M?{changedSinceLines:M}:{},...I||$?{contextCeiling:(0,E.contextCeilingFor)(T)}:{},providerID:A.providerID??null,externalSessionId:o,agentType:n.agentType,threadRequests:b.get(o)??0,sharedAreas:v,modelKey:(0,E.modelKeyOf)(t.kindModels,l),toolsKey:(0,E.toolsKeyOf)(l),lastActiveMs:_,busy:k,busyElsewhere:k&&(b.get(o)??0)>0?!!await u(e,o,t.requestId):void 0,archived:"archived"===n.status})}const _=(0,E.pickWarmCoder)(c,Date.now(),{worker:t.worker,modelKey:(0,E.modelKeyOf)(t.kindModels,t.kind),toolsKey:(0,E.toolsKeyOf)(t.kind)},C,d)
|
|
1983
1983
|
if(!_.reuse){const e=C.filter(e=>(e.threadRequests??0)>0&&(e.archived||!(0,E.isCacheWarm)(Date.now(),e,d))&&(void 0!==e.contextTokens||void 0!==e.changedSinceLines))
|
|
1984
1984
|
return e.length&&o.info("session routing: cold thread worker weighed and not resumed",{requestId:t.requestId,why:_.why,limits:{contextTokens:c.coldMaxContextTokens,changedLines:c.coldMaxChangedLines},weighed:e.map(e=>({sessionId:e.externalSessionId,contextTokens:e.contextTokens,changedSinceLines:e.changedSinceLines}))}),{why:_.why}}const T=C.find(e=>e.externalSessionId===_.sessionId)
|
|
1985
|
-
return o.info(_.cold?"session routing: cold worker chosen (it built this thread and its code is still current)":_.area?"session routing: warm worker of the same area chosen (no thread link)":"session routing: warm worker chosen",{requestId:t.requestId,sessionId:_.sessionId,threadRequestsWorked:b.get(_.sessionId)??0,..._.area?{sharedAreas:T?.sharedAreas}:{},contextTokens:T?.contextTokens,contextCeiling:T?.contextCeiling,provider:T?.providerID??void 0,afterTurn:!!_.afterTurn,candidates:C.length,..._.cold?{changedSinceLines:T?.changedSinceLines,archived:!!T?.archived}:{}}),{sessionId:_.sessionId,previousRequestId:S.get(_.sessionId),..._.afterTurn?{afterTurn:!0}:{},..._.area?{area:!0}:{},..._.cold&&T?.archived?{reopenLocalId:j.get(_.sessionId)}:{}}}catch(e){return o.warn("session routing: warm-worker survey failed; spawning",{error:(0,$.getErrorMessage)(e)}),{why:"no-candidate"}}}exports.handleStartWorkerAction=async function(e,t){const s=e,{apiClient:i,projectId:a,logger:
|
|
1985
|
+
return o.info(_.cold?"session routing: cold worker chosen (it built this thread and its code is still current)":_.area?"session routing: warm worker of the same area chosen (no thread link)":"session routing: warm worker chosen",{requestId:t.requestId,sessionId:_.sessionId,threadRequestsWorked:b.get(_.sessionId)??0,..._.area?{sharedAreas:T?.sharedAreas}:{},contextTokens:T?.contextTokens,contextCeiling:T?.contextCeiling,provider:T?.providerID??void 0,afterTurn:!!_.afterTurn,candidates:C.length,..._.cold?{changedSinceLines:T?.changedSinceLines,archived:!!T?.archived}:{}}),{sessionId:_.sessionId,previousRequestId:S.get(_.sessionId),..._.afterTurn?{afterTurn:!0}:{},..._.area?{area:!0}:{},..._.cold&&T?.archived?{reopenLocalId:j.get(_.sessionId)}:{}}}catch(e){return o.warn("session routing: warm-worker survey failed; spawning",{error:(0,$.getErrorMessage)(e)}),{why:"no-candidate"}}}exports.handleStartWorkerAction=async function(e,t){const s=e,{apiClient:i,projectId:a,logger:h}=t
|
|
1986
1986
|
if(!s.requestId)return{success:!1,message:"requestId is required",error:"missing_params"}
|
|
1987
|
-
const
|
|
1988
|
-
if(!
|
|
1989
|
-
const
|
|
1990
|
-
if(
|
|
1991
|
-
if(
|
|
1992
|
-
if(!e.busy){const e="string"==typeof s.worker&&s.worker.trim()||(0,f.kindProfile)(
|
|
1993
|
-
if(await c(t,i,{sessionId:s.unitWorker,previousRequestId:
|
|
1987
|
+
const w=t.startWorkerDeps??{},y=await i.get(`/api/projects/${a}/requests/${s.requestId}`),v=y?.data??y
|
|
1988
|
+
if(!v||!["open"].includes(v.status))return{success:!0,message:`${s.requestId} is ${v?.status??"unknown"}: nothing to start`}
|
|
1989
|
+
const k=M.get(s.requestId)
|
|
1990
|
+
if(k)return{success:!0,message:`${s.requestId} starts on ${k} when its current turn ends`}
|
|
1991
|
+
if(v.roundOf&&"string"==typeof s.unitWorker&&s.unitWorker&&null!==(0,f.kindProfile)(v.type).worker)try{const[e,r]=await Promise.all([(w.turnState??d)(t,s.unitWorker,s.requestId),(w.requestRecord??n)(t,s.requestId,v)]),i=e.busy?e.liveElsewhere:await u(t,s.unitWorker,s.requestId).then(e=>e&&e!==v.roundOf?e:null)
|
|
1992
|
+
if(!e.busy&&!i){const e="string"==typeof s.worker&&s.worker.trim()||(0,f.kindProfile)(v?.type).worker||"worker",n=(0,g.spawnToolMask)({kind:v?.type,hasTestProcess:"boolean"==typeof s.hasTestProcess?s.hasTestProcess:void 0}),i={p:s,req:v,worker:e,startPrompt:o(s,v,[],r),toolMask:n,deps:w}
|
|
1993
|
+
if(await c(t,i,{sessionId:s.unitWorker,previousRequestId:v.roundOf}))return{success:!0,message:`${s.requestId} handed to ${v.roundOf}'s own ${e} ${s.unitWorker}`}}}catch(e){h.info("session routing: the unit did not go straight to its request's worker; the ordinary start takes it",{requestId:s.requestId,error:(0,$.getErrorMessage)(e)})}try{const e=async e=>{const t=await i.get(`/api/projects/${a}/requests/${e}`)
|
|
1994
1994
|
return(t?.data??t)?.type}
|
|
1995
|
-
t.projectPath&&await(0,x.defaultBranchHasCode)(t.projectPath,e)&&(await(0,x.reportProjectHasCode)(a,!0,(e,t)=>i.patch(e,t)),await i.post(`/api/projects/${a}/requests/${s.requestId}/test-setup-offer`,{...s.creatorSessionId?{creatorSessionId:s.creatorSessionId}:{}}))}catch(e){
|
|
1996
|
-
const
|
|
1997
|
-
let
|
|
1998
|
-
const
|
|
1999
|
-
if("string"==typeof
|
|
2000
|
-
e.liveElsewhere?
|
|
2001
|
-
if(r.liveElsewhere)u
|
|
2002
|
-
|
|
1995
|
+
t.projectPath&&await(0,x.defaultBranchHasCode)(t.projectPath,e)&&(await(0,x.reportProjectHasCode)(a,!0,(e,t)=>i.patch(e,t)),await i.post(`/api/projects/${a}/requests/${s.requestId}/test-setup-offer`,{...s.creatorSessionId?{creatorSessionId:s.creatorSessionId}:{}}))}catch(e){h.warn("test-check card could not be asked for (the start goes on)",{requestId:s.requestId,error:(0,$.getErrorMessage)(e)})}const S=await(w.openDuplicates??r)(t,v,s.requestId),I=await(w.requestRecord??n)(t,s.requestId,v),j=o(s,v,S,I),A="string"==typeof s.worker&&s.worker.trim()||(0,f.kindProfile)(v?.type).worker||"worker",C=(0,g.spawnToolMask)({kind:v?.type,hasTestProcess:"boolean"==typeof s.hasTestProcess?s.hasTestProcess:void 0});(0,g.hasMask)(C)&&h.info("session routing: narrowing the worker tool surface for this request",{requestId:s.requestId,worker:A,off:Object.keys(C)})
|
|
1996
|
+
const _={p:s,req:v,worker:A,startPrompt:j,toolMask:C,deps:w}
|
|
1997
|
+
let T=null
|
|
1998
|
+
const R=v?.filedBySession??v?.filed_by_session,P=v?.filedByAgent??v?.filed_by_agent
|
|
1999
|
+
if("string"==typeof R&&R&&(0,E.sameWorkerRole)(P,A))try{if((await(0,m.readAllPages)(i,e=>`/api/projects/${a}/cli-sessions?status=active&limit=100&offset=${e}`)).some(e=>e?.externalSessionId===R)){const e=await(w.turnState??d)(t,R,s.requestId)
|
|
2000
|
+
e.liveElsewhere?h.info("session routing: the filer is mid-turn on another live request; not handing it this one",{requestId:s.requestId,sessionId:R,on:e.liveElsewhere}):(T={sessionId:R},h.info("session routing: the session that filed this request keeps it",{requestId:s.requestId,sessionId:R}))}}catch{}if(T=T??await(w.pickWarm??p)(t,{worker:A,requestId:s.requestId,kind:v?.type,kindModels:s.kindModels,planDocumentId:v?.planDocumentId??v?.plan_document_id??null,areas:Array.isArray(v?.areas)?v.areas.map(String):[],waitedOn:Array.isArray(v?.waitedOn)?v.waitedOn.map(String):[]}),T?.sessionId){const e=T.sessionId,r=await(w.turnState??d)(t,e,s.requestId)
|
|
2001
|
+
if(!r.busy&&!r.liveElsewhere&&v.roundOf){const n=await(w.liveElsewhere??u)(t,e,s.requestId)
|
|
2002
|
+
n&&n!==v.roundOf&&(r.liveElsewhere=n)}if(r.liveElsewhere)h.info("session routing: the chosen worker is mid-turn on another live request; spawning instead",{requestId:s.requestId,sessionId:e,on:r.liveElsewhere})
|
|
2003
|
+
else{if(r.busy)return M.set(s.requestId,e),(w.whenIdle??((e,r)=>(0,b.whenSessionIdle)(e,async()=>(await d(t,e,s.requestId)).busy,r)))(e,e=>{(async function(e,t,r,s){const{apiClient:n,projectId:o,logger:i}=e,{p:a}=t
|
|
2003
2004
|
try{const u=await n.get(`/api/projects/${o}/requests/${a.requestId}`),h=u?.data??u
|
|
2004
2005
|
if(!h||"open"!==h.status)return void i.info("session routing: the request moved on while its worker finished a turn; nothing to hand over",{requestId:a.requestId,status:h?.status??"unknown"})
|
|
2005
2006
|
const p=await(t.deps.turnState??d)(e,r.sessionId,a.requestId)
|
|
2006
2007
|
if("idle"===s&&!p.busy&&!p.liveElsewhere&&await c(e,{...t,req:h},r))return
|
|
2007
|
-
i.info("session routing: the worker did not come free for this start; spawning",{requestId:a.requestId,sessionId:r.sessionId,why:s,busy:p.busy,on:p.liveElsewhere}),await l(e,{...t,req:h})}catch(e){i.warn("session routing: the start after a turn failed",{requestId:a.requestId,sessionId:r.sessionId,error:(0,$.getErrorMessage)(e)})}})(t,
|
|
2008
|
-
if(await c(t,
|
|
2009
|
-
return l(t,
|
|
2008
|
+
i.info("session routing: the worker did not come free for this start; spawning",{requestId:a.requestId,sessionId:r.sessionId,why:s,busy:p.busy,on:p.liveElsewhere}),await l(e,{...t,req:h})}catch(e){i.warn("session routing: the start after a turn failed",{requestId:a.requestId,sessionId:r.sessionId,error:(0,$.getErrorMessage)(e)})}})(t,_,T,e).finally(()=>M.delete(s.requestId))}),h.info("session routing: the thread's worker is finishing a turn; the start is handed over when it ends",{requestId:s.requestId,sessionId:e,previous:T.previousRequestId}),{success:!0,message:`${s.requestId} starts on warm ${A} ${e} when its current turn ends (the thread shares the session)`}
|
|
2009
|
+
if(await c(t,_,T))return{success:!0,message:`${s.requestId} handed to warm ${A} ${e} (${T.area?"same area, separate work":"the thread shares the session"})`}}}else T?.why&&h.info(`session routing: spawning, because ${(0,E.whyNotReused)(T.why)}`,{requestId:s.requestId,why:T.why})
|
|
2010
|
+
return l(t,_)}
|
|
2010
2011
|
const f=t(87780),m=t(5641),g=t(45687),w=t(28148),y=t(17039),v=t(86116),b=t(67078),k=t(60833),S=t(2424),I=t(23687),$=t(58824),j=t(65454),E=t(64469),A=t(81570),C=t(76379),_=t(83807),T=t(47912),x=t(71879),M=new Map,R=new Set(["completed","canceled","reverted"])},82928(e,exports,t){"use strict"
|
|
2011
2012
|
function r(e){let t=0
|
|
2012
2013
|
for(const r of[...e??[]].reverse())if("user"===r?.info?.role&&!(r.parts??[]).some(e=>!0===e?.metadata?.alchemy?.payload?.nudge&&!0===e?.metadata?.alchemy?.payload?.restart)){if(!(r.parts??[]).some(e=>!0===e?.metadata?.alchemy?.payload?.nudge))break
|
|
@@ -2162,7 +2163,7 @@ const i=e.refusal
|
|
|
2162
2163
|
this.logger.error("REFUSED TURN: session stopped on an error another try on the same model cannot get past",{sessionId:t.id,title:t.title,errorName:e.errorName,model:i?.model,message:i?.message})
|
|
2163
2164
|
const a=i?.model??"its model",l=this.resumedOn.get(t.id)
|
|
2164
2165
|
this.resumedOn.delete(t.id)
|
|
2165
|
-
const d=!!s&&"hub"!==n&&!0===this.isAgentModelPinned?.(s),u=(w={scope:i?.scope,providerID:i?.providerID,pinned:d}).pinned?"Sending it again on the same model fails the same way. This agent is pinned to that model, so a pick at the top of Steer does not move it: open Workflow agents and clear its pinned model, or pin another one, and it resumes on the new model by itself.":"provider"===w.scope?`Reconnect ${w.providerID??"the provider"} in Settings > Providers, or pick a model from another provider by clicking the model name at the top of Steer: it resumes on it by itself.`:"Sending it again on the same model fails the same way. Pick another model by clicking the model name at the top of Steer: it resumes on it by itself.",h
|
|
2166
|
+
const d=!!s&&"hub"!==n&&!0===this.isAgentModelPinned?.(s),u=(w={scope:i?.scope,providerID:i?.providerID,pinned:d}).pinned?"Sending it again on the same model fails the same way. This agent is pinned to that model, so a pick at the top of Steer does not move it: open Workflow agents and clear its pinned model, or pin another one, and it resumes on the new model by itself.":"provider"===w.scope?`Reconnect ${w.providerID??"the provider"} in Settings > Providers, or pick a model from another provider by clicking the model name at the top of Steer: it resumes on it by itself.`:"Sending it again on the same model fails the same way. Pick another model by clicking the model name at the top of Steer: it resumes on it by itself.",h=i?.looped?`${a} could not use AlchemyLab's tools. ${i.message} ${u}`:`${a} refused its last turn: "${i?.message??e.errorName??"unknown error"}". ${u}`,p=c(n,s,r,t.title),f=l?`Resuming it on ${l} did not work. `:"",g=()=>this.reportFleetHealth?.(`${f}${p} stopped: ${h}`,{sessionId:t.id,attention:"act",dedupKey:o,...r?{requestId:r}:{}})
|
|
2166
2167
|
var w
|
|
2167
2168
|
"hub"!==n&&r&&this.raiseStalledAsk?this.raiseStalledAsk({requestId:r,sessionId:t.id,detail:("owned"===n?`${f}It stopped because ${h}`:`${f}${p} stopped: ${h}`).replace(/\.$/,""),refusedModel:i?.model??"its model",...d?{refusedPinned:!0}:{}}).then(e=>{e||g()}).catch(()=>g()):g()}noticeSilence(e,t,r,s,o,a){const c=`silent|${e.id}|${r.messageId??""}`
|
|
2168
2169
|
if(this.reportedKeys.has(c))return!0
|
|
@@ -2210,8 +2211,10 @@ if(s.ok){const e=await s.json(),r=e?.[e.length-1]?.info?.providerID
|
|
|
2210
2211
|
r&&(t=r)}}catch{}this.reportQuotaExhaustion({providerID:t,planType:void 0,message:String(s.message??"the provider is refusing requests")},r,`retry attempt ${s.attempt}`)}}reportQuotaExhaustion(e,t,r){const s=`quota|${e.providerID}|${e.resetsAt??"unknown"}`
|
|
2211
2212
|
if(this.reportedKeys.has(s))return
|
|
2212
2213
|
this.reportedKeys.add(s)
|
|
2213
|
-
const n=e.
|
|
2214
|
-
this.logger.error("PROVIDER QUOTA EXHAUSTED: turns on this provider fail until the limit resets",{providerID:e.providerID,resetsAt:e.resetsAt,planType:e.planType,sessionId:t,title:r}),this.recordProviderBlock?.(e.providerID,e.resetsAt,e.planType,e.message),this.reportFleetHealth?.(
|
|
2214
|
+
const n=e.planType?` (plan: ${e.planType})`:""
|
|
2215
|
+
this.logger.error("PROVIDER QUOTA EXHAUSTED: turns on this provider fail until the limit resets",{providerID:e.providerID,resetsAt:e.resetsAt,planType:e.planType,sessionId:t,title:r}),this.recordProviderBlock?.(e.providerID,e.resetsAt,e.planType,e.message),this.reportFleetHealth?.(function(e,t,r="",s=new Date){let n="until the limit lifts"
|
|
2216
|
+
if(t){const e=new Date(1e3*t),r=e.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit"})
|
|
2217
|
+
n=e.toDateString()===s.toDateString()?`until ${r}`:`until ${e.toLocaleDateString("en-GB",{day:"numeric",month:"short"})} ${r}`}return`${e} usage limit reached${r}. Work on ${e} models waits ${n}, then resumes by itself. To go on now, pick a model from another provider by clicking the model name at the top of Steer: what stopped resumes on it by itself.`}(e.providerID,e.resetsAt,n),{sessionId:t,attention:"act",dedupKey:s,details:`What ${e.providerID} said: ${e.message}`})}async checkModelAttestation(e){const t=this.getOpenCodeUrl()
|
|
2215
2218
|
if(!t)return
|
|
2216
2219
|
const r=new Set(e.map(e=>e.id))
|
|
2217
2220
|
for(const e of Array.from(this.attestedAt.keys()))r.has(e)||this.attestedAt.delete(e)
|
|
@@ -2680,7 +2683,9 @@ const s=t(e),n=t(r)
|
|
|
2680
2683
|
if(!s||!n)return!1
|
|
2681
2684
|
if(s===n)return!1
|
|
2682
2685
|
const o=(e,t)=>t.startsWith(e)&&/^[-_.]\d[\d._-]{5,}$/.test(t.slice(e.length))
|
|
2683
|
-
|
|
2686
|
+
if(o(s,n)||o(n,s))return!1
|
|
2687
|
+
const i=e=>e.replace(/:[a-z0-9._-]+$/,"")
|
|
2688
|
+
return s.includes(":")===n.includes(":")||i(s)!==i(n)},exports.substitutionMessage=function(e,t,r){return`Model substitution detected${r?` (session: ${r})`:""}: AlchemyLab requested \`${e}\` but the provider reported serving \`${t}\`. Work produced in this session did NOT run on the model you selected. Check the provider account's model access and any routing in front of it.`}},25146(e,exports,t){"use strict"
|
|
2684
2689
|
async function r(e,t){const r=e.condition_params||{}
|
|
2685
2690
|
switch(e.condition_type){case"deadline":{const s=Number(r.deadlineSeconds)||0,n=new Date(e.created_at).getTime()+1e3*s
|
|
2686
2691
|
return t.now()>=n?{fire:!0,payload:{deadlineSeconds:s}}:{fire:!1}}case"command-check":{const s=String(r.fireOn||"fail"),n=await t.runCheck(String(r.commandName||""),{monitorId:e.id,minIntervalSeconds:Math.max(30,Number(r.minIntervalSeconds)||300),trackChange:"change"===s,inlineCommand:r.command?String(r.command):void 0,ownerSessionId:e.owner_external_session_id||void 0})
|
|
@@ -2975,35 +2980,37 @@ const d=r.command.replace(/\s+/g," ").trim()
|
|
|
2975
2980
|
if((i[1].endsWith("\u2026")?d.startsWith(a):d===a)&&(r.command===s||t(r.command).includes(c)))return r}}
|
|
2976
2981
|
const r=new Map},43218(e,exports,t){"use strict"
|
|
2977
2982
|
function r(e){if(e&&e.includes("/"))return e.split("/")[0].trim().toLowerCase()||void 0}async function s(e,t=fetch){if(!e)return null
|
|
2978
|
-
const r=
|
|
2983
|
+
const r=c.get(e)
|
|
2979
2984
|
if(r&&Date.now()-r.at<3e4)return r.value
|
|
2980
2985
|
try{const r=await t(`${e.replace(/\/$/,"")}/config/providers`,{signal:AbortSignal.timeout(5e3)})
|
|
2981
2986
|
if(!r.ok)return null
|
|
2982
2987
|
const s=await r.json(),n={connected:(s.providers||[]).map(e=>String(e.id||"")).filter(Boolean),defaults:s.default||{}}
|
|
2983
|
-
return
|
|
2988
|
+
return c.set(e,{at:Date.now(),value:n}),n}catch{return null}}function n(e,t){if(!e||!t||0===t.connected.length)return e
|
|
2989
|
+
const s=r(e)
|
|
2990
|
+
if(!s||t.connected.includes(s))return e
|
|
2991
|
+
const n=(0,i.primaryProviderOf)(t.connected,e)
|
|
2992
|
+
if(n)return(0,i.carryModelToProvider)(e,n)??(0,i.defaultModelFor)(n)
|
|
2993
|
+
for(const e of t.connected)if(t.defaults[e])return`${e}/${t.defaults[e]}`
|
|
2994
|
+
return e}exports.b=void 0,exports.fetchProviderUsage=async function(e,t=fetch){const r=(0,o.getOpenCodeUrlFromConfig)(e)
|
|
2984
2995
|
if(!r)return[]
|
|
2985
|
-
const s=r.replace(/\/$/,""),
|
|
2986
|
-
if(
|
|
2996
|
+
const s=r.replace(/\/$/,""),n=a.get(s)
|
|
2997
|
+
if(n&&Date.now()-n.at<6e4)return n.data
|
|
2987
2998
|
try{const e=await t(`${s}/config/usage`,{signal:AbortSignal.timeout(15e3)})
|
|
2988
|
-
if(!e.ok)return
|
|
2989
|
-
const r=await e.json(),
|
|
2990
|
-
return
|
|
2999
|
+
if(!e.ok)return n?.data??[]
|
|
3000
|
+
const r=await e.json(),o=Array.isArray(r?.providers)?r.providers:[]
|
|
3001
|
+
return a.set(s,{at:Date.now(),data:o}),o}catch{return n?.data??[]}},exports.providerOf=r,exports.spentWindow=function(e,t,r=exports.b){if(!t)return null
|
|
2991
3002
|
const s=e.find(e=>e.providerID===t)
|
|
2992
3003
|
if(!s)return null
|
|
2993
3004
|
let n=null
|
|
2994
3005
|
for(const e of s.windows)e.usedPercent>=r&&(!n||e.usedPercent>n.usedPercent)&&(n=e)
|
|
2995
3006
|
return n},exports.describeWindow=function(e,t){const r=t.resetsAt?new Date(1e3*t.resetsAt).toLocaleString([],{weekday:"short",hour:"2-digit",minute:"2-digit"}):"an unknown time"
|
|
2996
|
-
return`${e} is ${t.usedPercent}% into its ${t.label} (resets ${r})`},exports.fetchConnectedProviders=async function(e,t=fetch){return s((0,
|
|
2997
|
-
const s=
|
|
2998
|
-
|
|
2999
|
-
const
|
|
3000
|
-
if(n)return(0,o.carryModelToProvider)(e,n)??(0,o.defaultModelFor)(n)
|
|
3001
|
-
for(const e of t.connected)if(t.defaults[e])return`${e}/${t.defaults[e]}`
|
|
3002
|
-
return e}
|
|
3003
|
-
const n=t(60833),o=t(95671)
|
|
3007
|
+
return`${e} is ${t.usedPercent}% into its ${t.label} (resets ${r})`},exports.fetchConnectedProviders=async function(e,t=fetch){return s((0,o.getOpenCodeUrlFromConfig)(e),t)},exports.fetchConnectedProvidersAt=s,exports.unchosenDefaultToFollow=function(e,t,r){if(!t||!e||!r||0===r.connected.length)return null
|
|
3008
|
+
const s=n(e,r)
|
|
3009
|
+
return s&&s!==e?s:null},exports.runnableModel=n
|
|
3010
|
+
const o=t(60833),i=t(95671)
|
|
3004
3011
|
exports.b=(()=>{const e=Number(process.env.ALCHEMY_PROVIDER_SPENT_PERCENT)
|
|
3005
3012
|
return Number.isFinite(e)&&e>0&&e<=100?e:90})()
|
|
3006
|
-
const
|
|
3013
|
+
const a=new Map,c=new Map},1285(e,exports,t){"use strict"
|
|
3007
3014
|
function r(e,t){return(0,s.createHmac)("sha256",e).update(`read|${t}`).digest("base64url")}exports.J=void 0,exports.mintReadToken=function(e,t=Date.now()){const s=t+exports.J
|
|
3008
3015
|
return{token:`r1.${s}.${r(e,s)}`,expiresAt:s}},exports.readTokenValid=function(e,t,s=Date.now()){if("string"!=typeof e||!e.startsWith("r1."))return!1
|
|
3009
3016
|
const[,o,i]=e.split("."),a=Number(o)
|
|
@@ -4772,8 +4779,8 @@ class ht{rememberApprovedCommand(e,t,r){const s=t.rule,o=r?"in this environment"
|
|
|
4772
4779
|
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})
|
|
4773
4780
|
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})
|
|
4774
4781
|
const t=Date.now()
|
|
4775
|
-
this.serveDeathTimestamps=this.serveDeathTimestamps.filter(e=>t-e<6e5),this.serveDeathTimestamps.push(t),this.logger.warn("OpenCode serve died unexpectedly -- parked for self-heal; next needing signal revives it",{code:e.code,signal:e.signal,pid:e.pid,unexpectedDeathsLast10m:this.serveDeathTimestamps.length}),this.serveDeathTimestamps.length>=3&&this.logger.error("OpenCode serve is FLAPPING (3+ unexpected deaths in 10 minutes). Revival keeps retrying, but check the opencode-serve log for the underlying crash.",{})}constructor(e,t){this.envSessionStarts=new Map,this.leakedCallNudges=new Map,this.paintCleaned=new Set,this.runningSessions=new Set,this.lastRunningReport="",this.directReportDedup=new Map,this.cachedReceipts=null,this.cachedIntegrations=null,this.lastSettingsSyncAt=0,this.activeMcpCalls=new Map,this.ownerGuidanceStale=!1,this.workCopiesGate=(()=>{let e
|
|
4776
|
-
return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.pendingConfigReload=!1,this.pendingConfigReloadSince=0,this.lastConfigReloadAt=0,this.deferredReloadCheckInFlight=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.clientVersion=(0,i.ownVersion)(),this.remoteLastOkAt=null,this.remoteFailures=0,this.remoteLastError=null,this.isPolling=!1,this.isProcessingAction=!1,this.localSecret=(0,te.newLocalSecret)(),this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.previewTunnel=null,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.droppedEventCount=0,this.firstDropAt=0,this.sseReconnectTimers=new Set,this.sseConnectGeneration=0,this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.serveStartedAt=0,this.registeredOnce=!1,this.recordedUsageMsgs=new Map,this.usageStepTimers=new Map,this.inFlightProxyActions=0,this.pendingRegularActions=[],this.queuedRegularActionIds=new Set,this.inFlightServeProxyRequests=0,this.serveDeathTimestamps=[],this.managedExitPids=new Set,this.pauseApplied=null,this.childDeathTold=new Set,this.mainBranchName=null,this.projectSubsystemsStarted=!1,this.lastParkVetoes=[],this.logger=e,this.options=t,(0,rt.installServeAuthFetch)(),(0,te.installLocalSecretFetch)(()=>this.options.projectPath),t.projectPath&&(process.env[n.PROJECT_ROOT_VAR]=t.projectPath),this.port=t.port||3456,this.startTime=new Date
|
|
4782
|
+
this.serveDeathTimestamps=this.serveDeathTimestamps.filter(e=>t-e<6e5),this.serveDeathTimestamps.push(t),this.logger.warn("OpenCode serve died unexpectedly -- parked for self-heal; next needing signal revives it",{code:e.code,signal:e.signal,pid:e.pid,unexpectedDeathsLast10m:this.serveDeathTimestamps.length}),this.serveDeathTimestamps.length>=3&&this.logger.error("OpenCode serve is FLAPPING (3+ unexpected deaths in 10 minutes). Revival keeps retrying, but check the opencode-serve log for the underlying crash.",{})}constructor(e,t){this.envSessionStarts=new Map,this.leakedCallNudges=new Map,this.paintCleaned=new Set,this.runningSessions=new Set,this.lastRunningReport="",this.directReportDedup=new Map,this.cachedReceipts=null,this.cachedIntegrations=null,this.defaultModelUnchosen=!1,this.lastSettingsSyncAt=0,this.activeMcpCalls=new Map,this.ownerGuidanceStale=!1,this.workCopiesGate=(()=>{let e
|
|
4783
|
+
return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.pendingConfigReload=!1,this.pendingConfigReloadSince=0,this.lastConfigReloadAt=0,this.deferredReloadCheckInFlight=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.clientVersion=(0,i.ownVersion)(),this.remoteLastOkAt=null,this.remoteFailures=0,this.remoteLastError=null,this.isPolling=!1,this.isProcessingAction=!1,this.localSecret=(0,te.newLocalSecret)(),this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.previewTunnel=null,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.droppedEventCount=0,this.firstDropAt=0,this.sseReconnectTimers=new Set,this.sseConnectGeneration=0,this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.serveStartedAt=0,this.registeredOnce=!1,this.recordedUsageMsgs=new Map,this.usageStepTimers=new Map,this.inFlightProxyActions=0,this.pendingRegularActions=[],this.queuedRegularActionIds=new Set,this.inFlightServeProxyRequests=0,this.serveDeathTimestamps=[],this.managedExitPids=new Set,this.followingDefaultTo=null,this.pauseApplied=null,this.childDeathTold=new Set,this.mainBranchName=null,this.projectSubsystemsStarted=!1,this.lastParkVetoes=[],this.logger=e,this.options=t,(0,rt.installServeAuthFetch)(),(0,te.installLocalSecretFetch)(()=>this.options.projectPath),t.projectPath&&(process.env[n.PROJECT_ROOT_VAR]=t.projectPath),this.port=t.port||3456,this.startTime=new Date
|
|
4777
4784
|
const r=(0,P.createMetricsCollector)(!1)
|
|
4778
4785
|
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
|
|
4779
4786
|
this.apiClient.setTokenRefresher(async()=>{const t=(await e.getToken())?.token
|
|
@@ -4938,7 +4945,11 @@ if(r)try{const e=await r.hasValidToken(),s=r.getAuthStatus(),n=e?await r.getToke
|
|
|
4938
4945
|
t.json({authenticated:e,isPendingAuth:s.isPendingAuth,expiresAt:n?.expiresAt??null})}catch(e){this.logger.warn("auth-status check failed",{error:(0,H.getErrorMessage)(e)}),t.json({authenticated:!1,isPendingAuth:!1})}else t.json({authenticated:!0,isPendingAuth:!1})}handleStatus(e,t){t.json({status:"running",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),startTime:this.startTime.toISOString(),projectId:this.projectId,serverId:this.serverId,isPolling:this.isPolling,parkState:this.parkController?.getState(),parkVetoes:this.parkController?this.lastParkVetoes:void 0,parkIdleForMs:this.parkController?.idleForMs(),port:this.port,timestamp:(new Date).toISOString()})}handleOpenCodeConfig(e,t){const r=O.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
4939
4946
|
try{if(!D.default.existsSync(r))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
|
|
4940
4947
|
const e=D.default.readFileSync(r,"utf-8"),s=JSON.parse(e)
|
|
4941
|
-
t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:`http://localhost:${s.port}`,directory:this.options.projectPath,proxyEnabled:!0,parkState:this.parkController?.getState()??"active"})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,H.getErrorMessage)(e)})}}
|
|
4948
|
+
t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:`http://localhost:${s.port}`,directory:this.options.projectPath,proxyEnabled:!0,parkState:this.parkController?.getState()??"active"})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,H.getErrorMessage)(e)})}}async followUnchosenDefault(){const e=(0,de.unchosenDefaultToFollow)(this.cachedDefaultModel,this.defaultModelUnchosen,this.configurer.getConnectedProviders())
|
|
4949
|
+
if(!e||!this.projectId||this.followingDefaultTo===e)return
|
|
4950
|
+
this.followingDefaultTo=e
|
|
4951
|
+
const t=this.cachedDefaultModel
|
|
4952
|
+
try{await this.apiClient.patch(`/api/projects/${this.projectId}`,{metadata:{defaultModel:e,defaultModelUnchosen:!0}}),this.logger.info("unchosen default model moved to a connected provider",{from:t,to:e}),await this.updateDefaultModel(e)}catch(e){this.followingDefaultTo=null,this.logger.debug("could not move the unchosen default model",{error:(0,H.getErrorMessage)(e)})}}updateDefaultModel(e){return void 0===e?Promise.resolve():(e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),this.stuckSessionWatchdog?.capacityChanged("model-changed")),e&&this.projectId&&this.lastAgentResyncModel!==e?this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,H.getErrorMessage)(e)})):Promise.resolve())}handleMcpToolCallEvent(e,t){this.parkController?.noteActivity(),"start"!==e?(this.activeMcpCalls.delete(t),0===this.activeMcpCalls.size&&this.pendingConfigReload&&this.fireDeferredReloadIfIdle("mcp-idle")):this.activeMcpCalls.set(t,Date.now())}startMcpStaleCleanup(){this.mcpStaleCleanupInterval||(this.mcpStaleCleanupInterval=setInterval(()=>{const e=Date.now()-12e4
|
|
4942
4953
|
let t=0
|
|
4943
4954
|
for(const[r,s]of this.activeMcpCalls)s<e&&(this.activeMcpCalls.delete(r),t++)
|
|
4944
4955
|
t>0&&this.logger.warn("Cleaned up stale MCP tool-call tracking entries",{removed:t}),this.pendingConfigReload&&this.fireDeferredReloadIfIdle("stale-cleanup")},6e4))}async anySessionBusy(){if(Date.now()-(0,d.lastPromptDeliveredAt)()<ht.PROMPT_DELIVERY_BUSY_MS)return!0
|
|
@@ -5073,12 +5084,12 @@ const{agent:r,planId:s,prompt:n,title:o,parentSessionId:i,flavor:a}=e.body||{},c
|
|
|
5073
5084
|
if(c&&!d.has(c))return void t.status(400).json({success:!1,error:`agent must be one of: ${[...d].join(", ")}`})
|
|
5074
5085
|
const u=await(0,ke.spawnPlannerWithDoc)({agent:c||"planner",flavor:l,planId:s,prompt:n,title:o||"Planner",parentSessionId:i,apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e)})
|
|
5075
5086
|
if(!u.success||!u.sessionId)return void t.status(500).json({success:!1,error:u.error||"spawn failed"})
|
|
5076
|
-
t.json({success:!0,data:{sessionId:u.sessionId,planId:u.planId,pid:u.pid}})}catch(e){(0,Oe.respondWithError)(t,this.logger,"Spawn planner",e)}}async ensureHubAlchemist(){try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/persistent/alchemist`),t=e?.data??e
|
|
5087
|
+
t.json({success:!0,data:{sessionId:u.sessionId,planId:u.planId,pid:u.pid}})}catch(e){(0,Oe.respondWithError)(t,this.logger,"Spawn planner",e)}}scheduleHubAlchemist(e){this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.hubRetryTimer=setTimeout(async()=>{this.hubRetryTimer=void 0,await this.ensureHubAlchemist()||this.scheduleHubAlchemist(Math.min(2*e,6e4))},e),this.hubRetryTimer.unref?.()}async ensureHubAlchemist(){try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/persistent/alchemist`),t=e?.data??e
|
|
5077
5088
|
if(t&&t.externalSessionId){const e=(0,et.getOpenCodeUrlFromConfig)(this.options.projectPath),r=e?await this.serveHasSession(e,String(t.externalSessionId)):null
|
|
5078
|
-
if(!1!==r)return
|
|
5079
|
-
this.logger.warn("Hub alchemist row points at a session this serve does not have; ending it and creating a new hub",{sessionId:t.id,externalSessionId:t.externalSessionId}),await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${t.id}`,{status:"completed"}).catch(e=>this.logger.warn("could not end the orphan hub row",{sessionId:t.id,error:(0,H.getErrorMessage)(e)}))}if(!this.projectId)return
|
|
5089
|
+
if(!1!==r)return this.logger.debug("Hub alchemist already present",{sessionId:t.id,verified:!0===r}),!0
|
|
5090
|
+
this.logger.warn("Hub alchemist row points at a session this serve does not have; ending it and creating a new hub",{sessionId:t.id,externalSessionId:t.externalSessionId}),await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${t.id}`,{status:"completed"}).catch(e=>this.logger.warn("could not end the orphan hub row",{sessionId:t.id,error:(0,H.getErrorMessage)(e)}))}if(!this.projectId)return!0
|
|
5080
5091
|
const r=new ye.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,getDefaultModel:()=>this.cachedDefaultModel||void 0}),s="Hub",n=await r.prepareSession({agent:"alchemist",prompt:s,title:s,projectPath:this.options.projectPath})
|
|
5081
|
-
this.logger.info("Hub alchemist created at boot (no browser required)",{sessionId:n?.sessionId??null})}catch(e){this.logger.warn("Hub alchemist bootstrap failed;
|
|
5092
|
+
return this.logger.info("Hub alchemist created at boot (no browser required)",{sessionId:n?.sessionId??null}),!0}catch(e){return this.logger.warn("Hub alchemist bootstrap failed; trying again shortly",{error:(0,H.getErrorMessage)(e)}),!1}}async reapVanishedSessions(){if(!this.projectId)return
|
|
5082
5093
|
const e=(0,et.getOpenCodeUrlFromConfig)(this.options.projectPath)
|
|
5083
5094
|
if(!e)return
|
|
5084
5095
|
let t
|
|
@@ -5166,7 +5177,7 @@ const t=D.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
|
5166
5177
|
return this.cachedProjectConfig={config:r,timestamp:Date.now()},r}catch(e){return this.logger.error("Failed to read project config",{error:(0,H.getErrorMessage)(e)}),this.cachedProjectConfig={config:null,timestamp:Date.now()},null}}setOpenCodePort(e){this.openCodePort=e,(0,rt.noteServe)(e)
|
|
5167
5178
|
const t=`http://127.0.0.1:${e}`
|
|
5168
5179
|
this.openCodeUrlCache={url:t,timestamp:Date.now()},this.logger.info("OpenCode port set directly",{port:e}),this.serveStartedAt=Date.now(),this.stuckSessionWatchdog?.serveStarted(this.serveStartedAt),(async()=>{for(let e=0;e<10;e++){try{const t=await this.configurer.refreshConnectedProviders(this.options.projectPath)
|
|
5169
|
-
if(this.configurer.hasConnectedProviders())return this.logger.info("connected providers known",{changed:t,attempt:e}),
|
|
5180
|
+
if(this.configurer.hasConnectedProviders())return this.logger.info("connected providers known",{changed:t,attempt:e}),t&&await this.resyncAgentMarkdowns(),void await this.followUnchosenDefault()}catch(t){this.logger.debug("connected-provider refresh failed",{attempt:e,error:(0,H.getErrorMessage)(t)})}await new Promise(e=>setTimeout(e,3e3))}this.logger.warn("the serve never reported its connected providers; agent models were not checked against them")})(),this.cachedDefaultModel||this.seedDefaultModel(e).catch(e=>this.logger.debug("Failed to seed default model",{error:(0,H.getErrorMessage)(e)}))}async seedDefaultModel(e){const t=await fetch(`http://localhost:${e}/config/providers`,{signal:AbortSignal.timeout(5e3)})
|
|
5170
5181
|
if(!t.ok)return
|
|
5171
5182
|
const r=await t.json(),s=(r?.providers||[]).map(e=>String(e.id||"")).filter(Boolean),n=(0,x.primaryProviderOf)(s),o=r?.default||{},[i,a]=Object.entries(o)[0]||[],c=n?(0,x.defaultModelFor)(n):i&&a?`${i}/${a}`:null
|
|
5172
5183
|
c&&(this.updateDefaultModel(c),this.logger.info("Seeded defaultModel",{defaultModel:c,primaryProvider:n}),this.apiClient.post("/api/user/preferences",{defaultModel:c}).catch(e=>this.logger.debug("Failed to seed user preference",{error:(0,H.getErrorMessage)(e)})))}getOpenCodeUrl(){if(this.parkController?.isServeDown())return null
|
|
@@ -5292,8 +5303,8 @@ r&&(t["X-Main-Sha"]=r)
|
|
|
5292
5303
|
const s=await fetch(e,{headers:t,signal:AbortSignal.timeout(35e3)})
|
|
5293
5304
|
if(!s.ok)throw new Error(`Poll failed with status ${s.status}`)
|
|
5294
5305
|
const n=await s.json(),o={success:!0,data:n.data||n}
|
|
5295
|
-
if(o.success&&o.data&&void 0!==o.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=o.data.hasRemoteEventConsumers,this.hasRemoteEventConsumers&&this.parkController?.noteActivity()),o.success&&o.data?.settings){const{defaultModel:e,paused:t,claudeThinking:r}=o.data.settings
|
|
5296
|
-
void 0!==e&&await this.updateDefaultModel(e),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r)}if(o.success&&!0===o.data?.previewTunnel&&this.projectId&&(this.previewTunnel||(this.previewTunnel=new T.PreviewTunnelClient({remoteServerUrl:this.options.remoteServerUrl,projectId:this.projectId,projectPath:this.options.projectPath,headers:()=>({...this.apiClient.authToken?{Authorization:`Bearer ${this.apiClient.authToken}`}:{},...(0,st.machineCallerHeaders)(this.options.remoteServerUrl)}),ports:()=>({ownPort:this.port,blocked:[...this.openCodePort?[this.openCodePort]:[],...(0,_.listLive)().map(e=>e.port).filter(e=>e!==this.port)]}),logger:this.logger})),this.previewTunnel.want()),o.success&&Array.isArray(o.data?.invalidations)&&o.data.invalidations.length>0){const e=o.data.invalidations
|
|
5306
|
+
if(o.success&&o.data&&void 0!==o.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=o.data.hasRemoteEventConsumers,this.hasRemoteEventConsumers&&this.parkController?.noteActivity()),o.success&&o.data?.settings){const{defaultModel:e,paused:t,claudeThinking:r,defaultModelUnchosen:s}=o.data.settings
|
|
5307
|
+
"boolean"==typeof s&&(this.defaultModelUnchosen=s),void 0!==e&&await this.updateDefaultModel(e),await this.followUnchosenDefault(),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r)}if(o.success&&!0===o.data?.previewTunnel&&this.projectId&&(this.previewTunnel||(this.previewTunnel=new T.PreviewTunnelClient({remoteServerUrl:this.options.remoteServerUrl,projectId:this.projectId,projectPath:this.options.projectPath,headers:()=>({...this.apiClient.authToken?{Authorization:`Bearer ${this.apiClient.authToken}`}:{},...(0,st.machineCallerHeaders)(this.options.remoteServerUrl)}),ports:()=>({ownPort:this.port,blocked:[...this.openCodePort?[this.openCodePort]:[],...(0,_.listLive)().map(e=>e.port).filter(e=>e!==this.port)]}),logger:this.logger})),this.previewTunnel.want()),o.success&&Array.isArray(o.data?.invalidations)&&o.data.invalidations.length>0){const e=o.data.invalidations
|
|
5297
5308
|
this.logger.info("Received cache invalidations from remote",{invalidations:e}),(e.includes("quests")||e.includes("documents")||e.includes("plans"))&&this.scheduleRunner?.invalidateCache(),e.includes("alchemist")&&this.scheduleRunner?.invalidateAlchemistCache(),e.includes("owner-guidance")&&(this.ownerGuidanceStale||this.logger.info("Your guidance changed; it reaches this project's agents at the next moment that costs nothing"),this.ownerGuidanceStale=!0),e.includes("agents")&&(this.logger.info("Agent definitions changed on the remote; re-syncing markdowns"),this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Agent markdown re-sync after invalidation failed",{error:(0,H.getErrorMessage)(e)})))}const i=o.success?o.data?.scheduleNudges??o.data?.questNudges:void 0,a=Array.isArray(i)?i:[],c=o.success&&Array.isArray(o.data?.actions)?o.data.actions.length:0,l=a.length
|
|
5298
5309
|
if((c>0||l>0)&&await this.ensureServeAlive("poll-work"),a.length>0){const e=a
|
|
5299
5310
|
this.logger.info("Received goal nudges from remote",{scheduleNudges:e})
|
|
@@ -5358,13 +5369,13 @@ continue}throw new Error(`Port ${this.port} is already in use (tried 50 ports)`)
|
|
|
5358
5369
|
try{const e=this.readProjectConfig()
|
|
5359
5370
|
if(e?.id){const{upsertEntry:r}=await Promise.resolve().then(()=>t(52640))
|
|
5360
5371
|
r({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,H.getErrorMessage)(e)})}try{await this.register(),this.startHeartbeat()}catch(e){const t=(0,H.getErrorMessage)(e)
|
|
5361
|
-
t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}if(this.projectId&&(
|
|
5372
|
+
t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}if(this.projectId&&(this.scheduleHubAlchemist(1e4),setTimeout(()=>{this.reconcileSetupRunPass()},12e3),this.setupRunReconcileTimer=setInterval(()=>{this.reconcileSetupRunPass()},3e5),this.setupRunReconcileTimer.unref?.()),this.projectId&&(0,Ie.sweepOrphanExperiments)(this.projectId,this.logger,this.options.projectPath),this.projectId){const e=Date.now()
|
|
5362
5373
|
setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}this.startExperimentReaper(),this.startLinearAutoImportPoller()
|
|
5363
5374
|
try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=0&settingsOnly=true`,t={"Content-Type":"application/json",Accept:"application/json"}
|
|
5364
5375
|
this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,st.machineCallerHeaders)(this.options.remoteServerUrl))
|
|
5365
5376
|
const r=await fetch(e,{headers:t,signal:AbortSignal.timeout(8e3)})
|
|
5366
5377
|
if(r.ok){const e=await r.json(),t=e?.data?.settings||e?.settings
|
|
5367
|
-
void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel})),"boolean"==typeof t?.paused&&await this.applyPause(t.paused),"string"==typeof t?.claudeThinking&&(this.claudeThinkingSeen=t.claudeThinking)}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,H.getErrorMessage)(e)})}this.startPolling(),await this.startProjectSubsystems("start"),this.startMcpStaleCleanup(),this.projectId&&this.startParkController()}async startProjectSubsystems(e){if(this.projectId&&!this.projectSubsystemsStarted){this.projectSubsystemsStarted=!0,this.logger.info("Starting project subsystems",{source:e,projectId:this.projectId}),(0,Fe.setContainerProject)(this.projectId),this.startSSEEventForwarder()
|
|
5378
|
+
"boolean"==typeof t?.defaultModelUnchosen&&(this.defaultModelUnchosen=t.defaultModelUnchosen),void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel})),"boolean"==typeof t?.paused&&await this.applyPause(t.paused),"string"==typeof t?.claudeThinking&&(this.claudeThinkingSeen=t.claudeThinking)}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,H.getErrorMessage)(e)})}this.startPolling(),await this.startProjectSubsystems("start"),this.startMcpStaleCleanup(),this.projectId&&this.startParkController()}async startProjectSubsystems(e){if(this.projectId&&!this.projectSubsystemsStarted){this.projectSubsystemsStarted=!0,this.logger.info("Starting project subsystems",{source:e,projectId:this.projectId}),(0,Fe.setContainerProject)(this.projectId),this.startSSEEventForwarder()
|
|
5368
5379
|
try{const{resumeInboxChatWatches:e}=await Promise.resolve().then(()=>t.t(86116,23))
|
|
5369
5380
|
e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})}catch(e){this.logger.warn("inbox watch resume failed at start",{error:(0,H.getErrorMessage)(e)})}this.apiClient.post(`/api/projects/${this.projectId}/session-messages/expire-orphaned-approvals`,{}).then(e=>{const t=e?.data?.expired??0
|
|
5370
5381
|
t>0&&this.logger.info("Ended approval cards left behind by the previous engine",{expired:t})}).catch(e=>{this.logger.warn("Could not end orphaned approval cards",{error:(0,H.getErrorMessage)(e)})}),this.startStuckSessionWatchdog(),this.startScheduleRunner(),(0,ge.ensureOpsDiagnostics)(this.apiClient,this.projectId,this.options.projectPath,this.logger),(0,ae.startSessionCompactor)({getOpenCodeUrl:()=>this.getOpenCodeUrl()??void 0,apiClient:this.apiClient,projectId:this.projectId||void 0,projectName:this.options.projectPath?O.default.basename(this.options.projectPath):void 0,logger:this.logger}),this.keepWarmRunner=(0,le.startKeepWarmRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,getDefaultModel:()=>this.cachedDefaultModel}),this.ownerGuidanceTimer=setInterval(()=>{this.checkOwnerGuidanceApply()},6e4),this.ownerGuidanceTimer.unref?.(),this.workMetricsRunner||(this.workMetricsRunner=(0,he.startWorkMetricsRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})),this.mcpConnectionRunner||(this.mcpConnectionRunner=(0,pe.startMcpConnectionRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")},isBusy:async()=>this.activeMcpCalls.size>0||await this.anySessionBusy()})),(0,ce.startMonitorRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,serveReady:async()=>(await this.ensureServeAlive("monitor-wake"),null!=this.openCodePort&&!this.parkController?.isServeDown()),reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},runOwn:e=>this.runOwn(e),verifyDeliveryEnv:async e=>{let r
|
|
@@ -5428,7 +5439,7 @@ for(const r of t?.data||t||[]){if(!(0,Y.isWaitDeclaringQuestion)(r))continue
|
|
|
5428
5439
|
const t=r?.fromSessionId||r?.from_external_session_id
|
|
5429
5440
|
"string"==typeof t&&t&&e.add(t)}const r=await this.apiClient.get(`/api/projects/${this.projectId}/monitors?status=armed`)
|
|
5430
5441
|
for(const t of r?.data||[]){const r=t?.owner_external_session_id
|
|
5431
|
-
"string"==typeof r&&r&&e.add(r)}}catch{}return e},reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}},...t.requestId?{requestId:t.requestId}:{},attention:t.attention}).catch(t=>{this.logger.warn("Fleet-health report did not land on the steer stream",{error:(0,H.getErrorMessage)(t),summary:e.slice(0,120)})})},closeStalledAsk:async({requestId:e,sessionId:t,why:r})=>{if(this.projectId)try{await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask/resumed`,{sessionId:t,why:r})}catch(r){this.logger.warn("stalled-ask close after resume failed",{requestId:e,sessionId:t,error:(0,H.getErrorMessage)(r)})}},raiseStalledAsk:async({requestId:e,sessionId:t,detail:r,refusedModel:s,refusedPinned:n})=>{if(!this.projectId)return!1
|
|
5442
|
+
"string"==typeof r&&r&&e.add(r)}}catch{}return e},reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.details?{details:t.details}:t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}},...t.requestId?{requestId:t.requestId}:{},attention:t.attention}).catch(t=>{this.logger.warn("Fleet-health report did not land on the steer stream",{error:(0,H.getErrorMessage)(t),summary:e.slice(0,120)})})},closeStalledAsk:async({requestId:e,sessionId:t,why:r})=>{if(this.projectId)try{await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask/resumed`,{sessionId:t,why:r})}catch(r){this.logger.warn("stalled-ask close after resume failed",{requestId:e,sessionId:t,error:(0,H.getErrorMessage)(r)})}},raiseStalledAsk:async({requestId:e,sessionId:t,detail:r,refusedModel:s,refusedPinned:n})=>{if(!this.projectId)return!1
|
|
5432
5443
|
try{const o=await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask`,{sessionId:t,detail:r,...s?{refusedModel:s}:{},...n?{refusedPinned:!0}:{}}),i=o?.data??o
|
|
5433
5444
|
return!0===i?.created||"string"==typeof i?.reason||null===i?.messageCode}catch(t){return this.logger.warn("stalled-ask mint failed; falling back to the plain report",{requestId:e,error:(0,H.getErrorMessage)(t)}),!1}},getStrandedCandidates:async()=>{if(!this.projectId)return[]
|
|
5434
5445
|
try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/requests/stranded-candidates`),t=e?.data||e
|
|
@@ -5448,9 +5459,9 @@ if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"
|
|
|
5448
5459
|
const s=await this.stuckSessionWatchdog.retrySession(r)
|
|
5449
5460
|
s.success?t.json({success:!0}):t.status(400).json({success:!1,error:s.error})}async enterUpdateMode(){if(!this.updateBlocked){this.updateBlocked=!0,this.logger.error("Entering update-required mode \u2014 shutting down all services"),console.error("\n\x1b[31m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 UPDATE REQUIRED \u2551"),console.error("\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\x1b[0m")
|
|
5450
5461
|
for(const e of this.versionWarnings)console.error(`\x1b[31m ${e.message}\x1b[0m`)
|
|
5451
|
-
console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
|
|
5462
|
+
console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
|
|
5452
5463
|
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.clear(),this.logger.info("Update-required mode active. HTTP server still running on port "+this.port)}}async stop(){this.logger.info("Stopping AlchemyActionServer..."),(0,o.markServerStopping)(),(0,S.clearInstanceLock)(this.options.projectPath),(0,Ze.closeExhibitPreviewBrowser)()
|
|
5453
|
-
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.previewTunnel?.close("shutting down"),await this.sweepUsage(this.startTime.getTime(),8e3),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival()
|
|
5464
|
+
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.previewTunnel?.close("shutting down"),await this.sweepUsage(this.startTime.getTime(),8e3),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival()
|
|
5454
5465
|
try{await Promise.race([(0,j.experimentRegistry)().stopAll(),new Promise(e=>setTimeout(e,15e3))])}catch(e){this.logger.warn("shutdown: boxes not all stopped",{error:(0,H.getErrorMessage)(e)})}this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.mcpStaleCleanupInterval&&(clearInterval(this.mcpStaleCleanupInterval),this.mcpStaleCleanupInterval=void 0),this.stopPendingReloadRecheck()
|
|
5455
5466
|
try{const e=O.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
|
|
5456
5467
|
D.default.existsSync(e)&&D.default.unlinkSync(e)}catch{}if(this.projectId)try{const{removeEntry:e}=await Promise.resolve().then(()=>t(52640))
|
|
@@ -5968,14 +5979,15 @@ function t(e,t){if((t.parts??[]).some(e=>"tool"===e.type&&"error"===e.state?.sta
|
|
|
5968
5979
|
if("MessageAbortedError"===e?.name)return{by:"stopped"}
|
|
5969
5980
|
if("APIError"===e?.name){const t=e.data?.statusCode
|
|
5970
5981
|
return{by:"provider",..."number"==typeof t?{status:t}:{}}}return{by:"unknown"}}function r(e,t){const r=e?.data??{},s="number"==typeof r.statusCode?r.statusCode:void 0,n=("string"==typeof r.message&&r.message.trim()?r.message.trim():String(e?.name??"an unknown error")).replace(/\s+/g," ").slice(0,300),o=t?.providerID||("string"==typeof r.providerID?r.providerID:void 0),i=t?.modelID||("string"==typeof r.modelID?r.modelID:void 0)
|
|
5971
|
-
return{message:n,...o&&i?{model:`${o}/${i}`}:{},...o?{providerID:o}:{},scope:"ProviderAuthError"===e?.name||401===s||403===s?"provider":"model"}}function s(e){return(e.parts??[]).filter(e=>"text"===e.type&&"string"==typeof e.text).map(e=>String(e.text)).join(" ").trim()}function n(e){return(e.parts??[]).some(e=>"tool"===e.type||"text"===e.type&&String(e.text??"").trim().length>0)}function o(e){if(!e||!e.name)return!1
|
|
5982
|
+
return{message:n,...o&&i?{model:`${o}/${i}`}:{},...o?{providerID:o}:{},scope:"ProviderAuthError"===e?.name||401===s||403===s?"provider":"model",..."repeated-tool-failure"===r.metadata?.alchemyStop?{looped:!0}:{}}}function s(e){return(e.parts??[]).filter(e=>"text"===e.type&&"string"==typeof e.text).map(e=>String(e.text)).join(" ").trim()}function n(e){return(e.parts??[]).some(e=>"tool"===e.type||"text"===e.type&&String(e.text??"").trim().length>0)}function o(e){if(!e||!e.name)return!1
|
|
5972
5983
|
if("ProviderQuotaError"===e.name)return!0
|
|
5973
5984
|
const t=e.data??{},r="number"==typeof t.statusCode?t.statusCode:void 0,s=`${"string"==typeof t.message?t.message:""} ${"string"==typeof t.responseBody?t.responseBody:""}`
|
|
5974
|
-
return"ProviderAuthError"===e.name||"ProviderModelNotFoundError"===e.name||401===r||403===r||!1===t.isRetryable||/revoked|authentication_error|invalid[_ ]api[_ ]key|unauthorized|model ?not ?found/i.test(s)}function i(e){return"user"===e.info.role&&!!e.parts?.length&&e.parts.every(e=>!0===e.metadata?.alchemy?.noReply)}exports.yX=void 0,exports.turnRefusal=r,exports.refusalLifted=function(e,t){if(!e||!t||!t.includes("/"))return null
|
|
5985
|
+
return"ProviderAuthError"===e.name||"ProviderModelNotFoundError"===e.name||401===r||403===r||!1===t.isRetryable||/revoked|authentication_error|invalid[_ ]api[_ ]key|unauthorized|model ?not ?found/i.test(s)}function i(e){return"user"===e.info.role&&!!e.parts?.length&&e.parts.every(e=>!0===e.metadata?.alchemy?.noReply)}exports.yX=exports.i0=void 0,exports.turnRefusal=r,exports.refusalLifted=function(e,t){if(!e||!t||!t.includes("/"))return null
|
|
5975
5986
|
const r=t.trim().toLowerCase()
|
|
5976
5987
|
if("provider"===e.scope){const t=(e.providerID??e.model?.split("/")[0]??"").trim().toLowerCase()
|
|
5977
5988
|
return t&&r.split("/")[0]!==t?"model-switched":null}return e.model&&r!==e.model.trim().toLowerCase()?"model-switched":null},exports.quotaBlockLifted=function(e,t,r){if(!e)return null
|
|
5978
5989
|
if(e.resetsAt&&1e3*e.resetsAt<r)return"window-passed"
|
|
5990
|
+
if(!e.resetsAt&&e.observedAt&&r-e.observedAt>exports.i0)return"window-passed"
|
|
5979
5991
|
const s=(t??"").includes("/")?t.split("/")[0].trim().toLowerCase():"",n=(e.providerID??"").trim().toLowerCase()
|
|
5980
5992
|
return s&&n&&s!==n?"provider-switched":null},exports.isFatalTurnError=o,exports.isQuietDelivery=i,exports.assembleTurnEvidence=function(e,a){let d=e.length
|
|
5981
5993
|
for(;d>0&&i(e[d-1]);)d--
|
|
@@ -5987,7 +5999,7 @@ if("assistant"!==h.info.role)return{kind:"no-messages"}
|
|
|
5987
5999
|
const f=h.info,m=f.error
|
|
5988
6000
|
if(!f.time.completed)return p(h)?{kind:"generating",messageId:f.id}:{kind:"unfinished",messageId:f.id,finish:f.finish}
|
|
5989
6001
|
if(m&&"object"==typeof m&&m.name){if("ProviderQuotaError"===m.name){const e=m.data??{}
|
|
5990
|
-
return{kind:"quota",messageId:f.id,errorName:m.name,quota:{providerID:"string"==typeof e.providerID&&e.providerID?e.providerID:"the provider",resetsAt:"number"==typeof e.resetsAt?e.resetsAt:void 0,planType:"string"==typeof e.planType?e.planType:void 0,message:"string"==typeof e.message&&e.message?e.message:"usage limit reached"}}}return o(m)?{kind:"fatal-error",messageId:f.id,errorName:m.name,finish:f.finish,refusal:r(m,f)}:{kind:"retryable-error",messageId:f.id,errorName:m.name,finish:f.finish,cause:t(m,h)}}const g=f.finish,w=n(h)
|
|
6002
|
+
return{kind:"quota",messageId:f.id,errorName:m.name,quota:{providerID:"string"==typeof e.providerID&&e.providerID?e.providerID:"the provider",resetsAt:"number"==typeof e.resetsAt?e.resetsAt:void 0,observedAt:f.time.completed,planType:"string"==typeof e.planType?e.planType:void 0,message:"string"==typeof e.message&&e.message?e.message:"usage limit reached"}}}return o(m)?{kind:"fatal-error",messageId:f.id,errorName:m.name,finish:f.finish,refusal:r(m,f)}:{kind:"retryable-error",messageId:f.id,errorName:m.name,finish:f.finish,cause:t(m,h)}}const g=f.finish,w=n(h)
|
|
5991
6003
|
if(g&&l.has(g)&&!w)return{kind:"dead-end",messageId:f.id,finish:g,providerID:f.providerID,modelID:f.modelID}
|
|
5992
6004
|
if("tool-calls"===g)return{kind:"interrupted-toolchain",messageId:f.id,finish:g}
|
|
5993
6005
|
const y=s(h)
|
|
@@ -6007,7 +6019,7 @@ case"unfinished":case"interrupted-toolchain":case"lost-prompt":return"hub"===r?{
|
|
|
6007
6019
|
case"clean-text":return"owned"===r?{act:"unconfuse-then-report"}:{act:"none"}
|
|
6008
6020
|
default:return{act:"none"}}},exports.retryLooksLikeRefusal=function(e){return!(!e||"retry"!==e.type)&&!((e.attempt??0)<exports.yX)&&d.test(String(e.message??""))}
|
|
6009
6021
|
const a=/MCP error -32000|\bConnection closed\b|\bNot connected\b/i,c=300,l=new Set(["content-filter","length","error","other","unknown"])
|
|
6010
|
-
exports.yX=3
|
|
6022
|
+
exports.i0=6e5,exports.yX=3
|
|
6011
6023
|
const d=/\b(exceeded|exhausted|quota|out of (credit|credits)|insufficient|subscribe|upgrade your plan|billing)\b/i},90473(e,exports,t){"use strict"
|
|
6012
6024
|
function r(e,t){const r=(0,a.readActionServerFile)(e)
|
|
6013
6025
|
return r.port===t&&r.secret?{[a.LOCAL_SECRET_HEADER]:r.secret}:{}}async function s(e){const{port:t,projectPath:s,logger:n,pollMs:o=500,pollAttempts:i=16}=e,a=`http://127.0.0.1:${t}`
|
|
@@ -6733,7 +6745,7 @@ else{const r=await(0,i.resolveDefaultBranch)(e)
|
|
|
6733
6745
|
await u.raw(["worktree","add","-b",c,d,r]),a.info("Pre-spawn: created worktree with new branch from latest main",{requestId:t,branchName:c,mainBranch:r,worktreePath:d})}}catch(e){const r=e instanceof Error?e.message:String(e)
|
|
6734
6746
|
a.warn("Pre-spawn: failed to create worktree (MCP process will retry)",{requestId:t,error:r})}}
|
|
6735
6747
|
const r=t(79896),s=t(16928),n=t(83807),o=t(7202),i=t(52232)},35419(e,exports,t){"use strict"
|
|
6736
|
-
function r(e){return(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode-config.json")}function s(e){return(0,o.join)((0,n.homedir)(),i.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}exports.t=void 0,exports.generatedOpencodeConfigPath=r,exports.getGlobalConfigDir=s,exports.getOpencodeSpawnEnv=function(e,t){return{ANTHROPIC_CLI_VERSION:process.env.ANTHROPIC_CLI_VERSION??exports.t,OPENCODE_CLIENT:"
|
|
6748
|
+
function r(e){return(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode-config.json")}function s(e){return(0,o.join)((0,n.homedir)(),i.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}exports.t=void 0,exports.generatedOpencodeConfigPath=r,exports.getGlobalConfigDir=s,exports.getOpencodeSpawnEnv=function(e,t){return{ANTHROPIC_CLI_VERSION:process.env.ANTHROPIC_CLI_VERSION??exports.t,OPENCODE_CLIENT:"cli",ALCHEMYLAB:"1",OPENCODE_ENABLE_EXA:"1",OPENCODE_DATA_DIR:(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode"),OPENCODE_CONFIG_DIR:s(t),ALCHEMY_PROTECTED_ROOT:e,OPENCODE_CONFIG:r(e),OPENCODE_DISABLE_PROJECT_CONFIG:"1",OPENCODE_DISABLE_CLAUDE_CODE:"1",OPENCODE_DISABLE_EXTERNAL_SKILLS:"1"}}
|
|
6737
6749
|
const n=t(70857),o=t(16928),i=t(14139)
|
|
6738
6750
|
exports.t="2.1.280"},39068(e,exports,t){"use strict"
|
|
6739
6751
|
function r(){const e=process.env.ALCHEMY_OPENCODE_DIR
|
|
@@ -6850,13 +6862,13 @@ else if("failed"===u.status||!u.hasHistory)throw function(e){const t=(0,c.join)(
|
|
|
6850
6862
|
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."])
|
|
6851
6863
|
const h=(0,c.join)(e,g.ALCHEMY_DIR_NAME)
|
|
6852
6864
|
let p
|
|
6853
|
-
await(0,a.mkdir)(h,{recursive:!0}),o?(d(`\u{1f4cb}
|
|
6865
|
+
await(0,a.mkdir)(h,{recursive:!0}),o?(d(`\u{1f4cb} Adding "${(0,c.basename)(e)||"this folder"}" to your AlchemyLab account...`),p=await n(t,o,e,l),d(p?"\u2705 Project added.":s(r()))):d("\u26a0\ufe0f No auth token, skipping project registration")
|
|
6854
6866
|
const f=(0,c.join)(h,"project.json")
|
|
6855
6867
|
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)
|
|
6856
6868
|
let S
|
|
6857
6869
|
try{const e=JSON.parse((0,i.readFileSync)(m,"utf-8"))
|
|
6858
6870
|
if(S=e.id||void 0,!1===e.registeredRemotely&&d){const a=await n(o,d,t,u,"cli-init-retry")
|
|
6859
|
-
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(
|
|
6871
|
+
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 added.")):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)}})
|
|
6860
6872
|
if(!s.ok)throw new Error(`HTTP ${s.status}`)
|
|
6861
6873
|
return"GET"===e?s.json():void 0},$=d&&S?async e=>{const t=await I("GET",`/api/projects/${S}/requests/${e}`)
|
|
6862
6874
|
return(t?.data??t)?.type}:void 0,j=await(0,y.defaultBranchHasCode)(t,$).catch(()=>!1)
|
|
@@ -6885,7 +6897,8 @@ if(!Number.isFinite(r)||r<=0)return 0
|
|
|
6885
6897
|
let s=""
|
|
6886
6898
|
try{s=new URL(e).hostname}catch{return 0}return"localhost"===s||"127.0.0.1"===s||"::1"===s?Math.min(r,2e3):0}function s(e,t,r){if(r&&"stores"===r.op)try{return{ok:!0,result:{projects:e.projects()}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(r&&"drop"===r.op&&"string"==typeof r.projectId)try{return{ok:!0,result:{removed:e.drop(r.projectId)}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(r&&"put"===r.op&&"string"==typeof r.projectId)try{const t=r.args??{}
|
|
6887
6899
|
return{ok:!0,result:{ids:e.put(r.projectId,Array.isArray(t.items)?t.items:[])}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(r&&"has"===r.op&&"string"==typeof r.projectId)try{const t=r.args??{}
|
|
6888
|
-
return{ok:!0,result:{ids:e.hasMany(r.projectId,Array.isArray(t.ids)?t.ids.map(String):[])}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(
|
|
6900
|
+
return{ok:!0,result:{ids:e.hasMany(r.projectId,Array.isArray(t.ids)?t.ids.map(String):[])}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(r&&"getAcross"===r.op)try{const s=r.args?.projects&&"object"==typeof r.args.projects?r.args.projects:{},n={}
|
|
6901
|
+
for(const[r,o]of Object.entries(s))if(t.has(r)||e.has(r))try{n[r]=e.getMany(r,Array.isArray(o)?o.map(String):[])}catch{}return{ok:!0,result:{items:n}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(!r||"string"!=typeof r.projectId||!t.has(r.projectId)&&!e.has(r.projectId))return{ok:!1,error:"this computer keeps no words for that project"}
|
|
6889
6902
|
try{const t=r.args??{}
|
|
6890
6903
|
switch(r.op){case"put":return{ok:!0,result:{ids:e.put(r.projectId,Array.isArray(t.items)?t.items:[])}}
|
|
6891
6904
|
case"getMany":return{ok:!0,result:{items:e.getMany(r.projectId,Array.isArray(t.ids)?t.ids.map(String):[])}}
|
|
@@ -7392,132 +7405,138 @@ if(!o.ok)throw new Error(`HTTP ${o.status}`)
|
|
|
7392
7405
|
const i=await o.json(),a=Array.isArray(i?.data)?i.data:Array.isArray(i)?i:[]
|
|
7393
7406
|
for(const e of a){const t=e?.metadata?.schedule
|
|
7394
7407
|
t&&"object"==typeof t&&!0===t.enabled&&r.push(t)}if(a.length<100)break}return r}}},86090(e,exports,t){"use strict"
|
|
7395
|
-
function r(){return
|
|
7408
|
+
function r(){return S.default.resolve(process.argv[1])}function s(e){const t=(0,_.servicePaths)(e),r=(0,T.readServiceConfig)(t)
|
|
7396
7409
|
return r?{paths:t,cfg:r}:void 0}async function n(e,t){if(!process.stdin.isTTY)return t
|
|
7397
|
-
const r=
|
|
7410
|
+
const r=I.default.createInterface({input:process.stdin,output:process.stdout}),s=await new Promise(s=>r.question(`${e} ${t?"[Y/n]":"[y/N]"} `,s))
|
|
7398
7411
|
r.close()
|
|
7399
7412
|
const n=s.trim().toLowerCase()
|
|
7400
7413
|
return""===n?t:"y"===n||"yes"===n}async function o(e,t){const s={}
|
|
7401
7414
|
process.env.ALCHEMY_LAB_HOME&&(s.ALCHEMY_LAB_HOME=process.env.ALCHEMY_LAB_HOME)
|
|
7402
|
-
try{const n=await(0,
|
|
7403
|
-
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,
|
|
7404
|
-
|
|
7415
|
+
try{const n=await(0,H.installLoginItem)({nodePath:process.execPath,cliPath:r(),remoteServerUrl:t.remoteServerUrl,envPath:process.env.PATH??"/usr/bin:/bin",env:s,logPath:e.log})
|
|
7416
|
+
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,R.isServiceRunning)(e)||(console.log("Starting the AlchemyLab service..."),await(0,R.startServiceInBackground)(e,t,r())||(console.error(a(e.log)),process.exit(1)))}function a(e){const t=c(e)
|
|
7417
|
+
return`\u274c The AlchemyLab service did not start${t?`: ${t}`:"."}\n Its log: ${e}`}function c(e){let t=""
|
|
7418
|
+
try{const r=k.default.openSync(e,"r")
|
|
7419
|
+
try{const e=k.default.fstatSync(r).size,s=Math.min(e,16384),n=Buffer.alloc(s)
|
|
7420
|
+
k.default.readSync(r,n,0,s,e-s),t=n.toString("utf8")}finally{k.default.closeSync(r)}}catch{return""}const r=t.split("\n").map(e=>e.trim()).filter(e=>e&&!e.startsWith("at "))
|
|
7421
|
+
for(let e=r.length-1;e>=0;e--){const t=/Error:\s*(.+)$/.exec(r[e])
|
|
7422
|
+
if(t)return t[1].replace(/^Error:\s*/,"").slice(0,300)}return""}function l(e){return e.state}async function d(e){let t,r
|
|
7423
|
+
try{r=await(0,R.request)(e,"ping"),t=await(0,R.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}`)
|
|
7405
7424
|
const s=t.projects??[]
|
|
7406
7425
|
if(0===s.length)return void console.log(" No projects on this computer yet. Run `alchemy` in a folder to add it.")
|
|
7407
7426
|
const n=Math.max(8,...s.map(e=>e.name.length))+2
|
|
7408
7427
|
console.log(` ${"PROJECT".padEnd(n)}${"STATE".padEnd(10)}${"PORT".padEnd(7)}FOLDER`)
|
|
7409
|
-
for(const e of s)console.log(` ${e.name.padEnd(n)}${
|
|
7410
|
-
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
|
|
7411
|
-
for(const t of(e.lines??[]).slice(-8))console.error(` ${t}`)}function
|
|
7428
|
+
for(const e of s)console.log(` ${e.name.padEnd(n)}${l(e).padEnd(10)}${String(e.port??"").padEnd(7)}${e.path}`),e.reason&&console.log(` ${"".padEnd(n)}${e.reason}`)
|
|
7429
|
+
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 u(e){console.error(`\u274c ${e.reason??"It did not start."}`)
|
|
7430
|
+
for(const t of(e.lines??[]).slice(-8))console.error(` ${t}`)}function h(e){const t=async(t,r={},s=12e4)=>{const n=await(0,R.request)(e,"new-computer",{op:t,...r},s)
|
|
7412
7431
|
if(!n.ok)throw new Error(n.reason??"The AlchemyLab service did not do it.")
|
|
7413
7432
|
return n}
|
|
7414
7433
|
return{candidates:async()=>{const e=await t("candidates",{},4e4)
|
|
7415
7434
|
return{candidates:e.candidates??[],here:e.here??[]}},backups:async(e,r)=>{const s=await t("backups",{folder:e,from:r})
|
|
7416
|
-
return{folder:s.folder,runs:s.runs??[],others:Number(s.others)||0}},restore:async(e,r)=>(await t("restore",{folder:e,run:r},18e5)).results??[],preview:async e=>(await t("preview",{from:e},3e5)).preview,move:async(e,r)=>(await t("move",{from:e,withoutWords:r},9e5)).result,forget:async e=>{await t("forget",{from:e},4e4)}}}function
|
|
7417
|
-
return{say:(e="")=>console.log(e),ask:t=>new Promise(r=>e.question(`${t} `,e=>r(e.trim()))),close:()=>e.close()}}var
|
|
7418
|
-
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,
|
|
7419
|
-
const s=(0,
|
|
7435
|
+
return{folder:s.folder,runs:s.runs??[],others:Number(s.others)||0}},restore:async(e,r)=>(await t("restore",{folder:e,run:r},18e5)).results??[],preview:async e=>(await t("preview",{from:e},3e5)).preview,move:async(e,r)=>(await t("move",{from:e,withoutWords:r},9e5)).result,forget:async e=>{await t("forget",{from:e},4e4)}}}function p(){const e=I.default.createInterface({input:process.stdin,output:process.stdout})
|
|
7436
|
+
return{say:(e="")=>console.log(e),ask:t=>new Promise(r=>e.question(`${t} `,e=>r(e.trim()))),close:()=>e.close()}}var f=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
7437
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.serviceSetUp=s,exports.runService=async function(e,t,s={}){if(t.includes("--foreground"))return async function(e){const t=(0,_.servicePaths)(e);(0,_.ensurePrivateDir)(t.dir)
|
|
7438
|
+
const s=(0,T.readServiceConfig)(t)
|
|
7420
7439
|
s||(console.error("The AlchemyLab service is not set up for this server yet. Run `alchemy service` in a terminal first."),process.exit(1))
|
|
7421
|
-
const n=(0
|
|
7440
|
+
const n=(0,$.createLogger)({serviceName:"alchemy-service",console:!1,logToFile:!0,logFilePath:t.log,baseDir:t.dir}),o=new M.Supervisor({paths:t,config:s,logger:n,childModule:r(),childArgs:e=>["project-run","--project",e.path,"--expect",`${e.dev}:${e.ino}`],childEnv:()=>({...(0,T.childEnv)((0,T.readServiceConfig)(t)??s),ALCHEMY_MACHINE_ID:(0,m.machineIdentity)(t).id}),approvalEnv:{installDir:G},version:(0,A.ownVersion)()??void 0,onChange:()=>u?.noteChanged(),cap:()=>(0,T.readServiceConfig)(t)?.cap??(0,M.defaultCap)(),machineId:(0,m.machineIdentity)(t).id,confirmIdentity:async()=>(await c).register(),signedOut:()=>!0===u?.signedOut,startAsNew:(0,F.startAsNewProject)({remoteServerUrl:s.remoteServerUrl,machineId:(0,m.machineIdentity)(t).id,getToken:async()=>(await v.getToken())?.token}),connected:()=>i()}),i=async()=>{v.forgetCachedSignIn()
|
|
7422
7441
|
const e=(e,t)=>Promise.race([e,new Promise(e=>{const r=setTimeout(()=>e(!1),t)
|
|
7423
7442
|
r.unref?.()})]),t=await e(u?u.register(1e4):Promise.resolve(!1),12e3),r=await e(h?h.connectNow():Promise.resolve(!1),12e3)
|
|
7424
7443
|
return!0===t&&!0===r}
|
|
7425
7444
|
let a=()=>{}
|
|
7426
7445
|
const c=new Promise(e=>{a=e})
|
|
7427
|
-
let l,d,u,h,
|
|
7428
|
-
const
|
|
7429
|
-
process.on("SIGTERM",()=>{
|
|
7430
|
-
try{await o.start(()=>{
|
|
7431
|
-
await
|
|
7432
|
-
const
|
|
7433
|
-
return s&&s.privateData!==r.privateData&&(0,
|
|
7434
|
-
o.handle("connect-now",async()=>({ok:await i()})),o.handle("move-words-here",async()=>{const e=await
|
|
7446
|
+
let l,d,u,h,p,f
|
|
7447
|
+
const y=async(e=0)=>{l?.(),await(u?.stop().catch(()=>{})),h?.stop(),f?.stop(),p?.stop(),d?.close(),await o.stop(),process.exit(e)}
|
|
7448
|
+
process.on("SIGTERM",()=>{y(0)}),process.on("SIGINT",()=>{y(0)})
|
|
7449
|
+
try{await o.start(()=>{y(0)})}catch(e){throw e instanceof M.AlreadyRunningError&&(console.error(e.message),process.exit(0)),e}const v=new j.TokenManager(n,t.dir,{remoteServerUrl:s.remoteServerUrl,allowInteractiveAuth:!1})
|
|
7450
|
+
await v.initialize(),l=(0,P.startSignInWatchdog)(v,e=>n.info(e)),u=new N.MachineLink({paths:t,remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await v.getToken())?.token,projects:()=>o.status(),wake:(e,t)=>o.wake(e,t),archived:e=>o.setArchived(e),answered:(0,F.onListAnswered)({supervisor:o,remoteServerUrl:s.remoteServerUrl,machineId:(0,m.machineIdentity)(t).id,getToken:async()=>(await v.getToken())?.token,logger:n}),version:(0,A.ownVersion)()??void 0,create:e=>{x.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 g.ContentLink({paths:t,remoteServerUrl:s.remoteServerUrl,privateData:!0===(0,T.readServiceConfig)(t)?.privateData,getToken:async()=>(await v.getToken())?.token,projectIds:()=>o.status().map(e=>e.projectId).filter(e=>"string"==typeof e),logger:n}),h.start(),p=new b.WordsBackup({store:h.store,stateFile:S.default.join(t.dir,"words-backup.json"),settings:()=>(0,b.backupSettingsOf)((0,T.readServiceConfig)(t)?.wordsBackup),privateData:()=>h?.privateData??!0===(0,T.readServiceConfig)(t)?.privateData,logger:n}),p.start()
|
|
7451
|
+
const k=async e=>{const r=h?await h.setPrivateData(e):{privateData:e,switching:!1},s=(0,T.readServiceConfig)(t)
|
|
7452
|
+
return s&&s.privateData!==r.privateData&&(0,T.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}
|
|
7453
|
+
o.handle("connect-now",async()=>({ok:await i()})),o.handle("move-words-here",async()=>{const e=await k(!0)
|
|
7435
7454
|
return{ok:!e.refused,reason:e.refused,privateData:e.privateData,switching:e.switching}})
|
|
7436
|
-
const
|
|
7437
|
-
let
|
|
7438
|
-
o.handle("new-computer",e=>(0,
|
|
7439
|
-
const
|
|
7440
|
-
if(!r)throw new Error("the service has no configuration");(0,
|
|
7441
|
-
d=await(0,
|
|
7442
|
-
return{settings:e,state:
|
|
7443
|
-
return e&&
|
|
7455
|
+
const I=new w.NewComputer({machineId:(0,m.machineIdentity)(t).id,remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await v.getToken())?.token,channelKey:()=>(0,g.channelKey)(t),store:h.store,backup:p,defaultFolder:()=>(0,b.backupSettingsOf)((0,T.readServiceConfig)(t)?.wordsBackup).folder})
|
|
7456
|
+
let E
|
|
7457
|
+
o.handle("new-computer",e=>(0,w.newComputerCommand)(I,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."}),f=new L.ScheduleWaker({remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await v.getToken())?.token,parked:()=>o.status().filter(e=>"parked"===e.state&&e.projectId).map(e=>e.projectId),wake:e=>o.wake(e,"work"),logger:n}),f.start(),(0,D.resolveFrontendUrl)(s.remoteServerUrl).then(e=>{try{/^https?:\/\//.test(e)&&(E=new URL(e).origin)}catch{}})
|
|
7458
|
+
const C=(0,B.nativeDialogs)(),x=new U.Adding({supervisor:o,dialogs:C,logger:n,approvalEnv:{installDir:G}}),R=e=>{const r=(0,T.readServiceConfig)(t)
|
|
7459
|
+
if(!r)throw new Error("the service has no configuration");(0,T.writeServiceConfig)(t,{...r,wordsBackup:{...r.wordsBackup??{},...e}})}
|
|
7460
|
+
d=await(0,q.startDoor)({supervisor:o,adding:x,wordsBackup:{view:()=>{const e=(0,b.backupSettingsOf)((0,T.readServiceConfig)(t)?.wordsBackup)
|
|
7461
|
+
return{settings:e,state:p.state(),runs:p.ownRuns(),folderAvailable:p.folderAvailable(e.folder)}},set:e=>R(e),pick:async()=>{const e=await C.pickFolder("Choose a folder for backups of your words (one your backup tool or iCloud Drive covers)")
|
|
7462
|
+
return e&&R({folder:e}),e},run:()=>p.run(),restore:e=>p.restore(e)},privateData:{get:()=>h?.privateData??!0===(0,T.readServiceConfig)(t)?.privateData,set:k},newComputer:{api:I,pickBackupFolder:()=>C.pickFolder('Choose the "AlchemyLab Backups" folder of your old computer (restored or copied onto this one)')},verifyMachineTicket:async e=>{const t=(await v.getToken())?.token
|
|
7444
7463
|
if(!t)throw new Error("this computer is not signed in")
|
|
7445
|
-
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,
|
|
7464
|
+
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,W.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:e}),signal:AbortSignal.timeout(1e4)})
|
|
7446
7465
|
if(r.status>=400&&r.status<500)return{ok:!1}
|
|
7447
7466
|
if(!r.ok)throw new Error(`HTTP ${r.status}`)
|
|
7448
7467
|
const n=await r.json(),o=n?.data??n
|
|
7449
|
-
return{ok:!0===o?.ok,expiresAt:o?.expiresAt??null}},logger:n,remoteServerUrl:s.remoteServerUrl,version:(0,
|
|
7468
|
+
return{ok:!0===o?.ok,expiresAt:o?.expiresAt??null}},logger:n,remoteServerUrl:s.remoteServerUrl,version:(0,A.ownVersion)()??void 0,machineId:u.id,frontendOrigin:()=>E,verifyTicket:async(e,t)=>{const r=(await v.getToken())?.token
|
|
7450
7469
|
if(!r)throw new Error("this computer is not signed in")
|
|
7451
|
-
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,
|
|
7470
|
+
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,W.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:t}),signal:AbortSignal.timeout(1e4)})
|
|
7452
7471
|
if(n.status>=400&&n.status<500)return{ok:!1}
|
|
7453
7472
|
if(!n.ok)throw new Error(`HTTP ${n.status}`)
|
|
7454
7473
|
const o=await n.json(),i=o?.data??o
|
|
7455
|
-
return{ok:!0===i?.ok,expiresAt:i?.expiresAt??null}}},s.doorPort??(0,
|
|
7456
|
-
const
|
|
7457
|
-
if("install"===t[0]||"uninstall"===t[0]){if("uninstall"===t[0]){const t=await(0,
|
|
7458
|
-
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,
|
|
7459
|
-
return r||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),void await o(
|
|
7460
|
-
await(0,
|
|
7461
|
-
for(let e=0;e<240&&await(0,
|
|
7474
|
+
return{ok:!0===i?.ok,expiresAt:i?.expiresAt??null}}},s.doorPort??(0,q.defaultDoorPort)(s.remoteServerUrl)),await new Promise(()=>{})}(e)
|
|
7475
|
+
const c=(0,_.servicePaths)(e)
|
|
7476
|
+
if("install"===t[0]||"uninstall"===t[0]){if("uninstall"===t[0]){const t=await(0,H.uninstallLoginItem)(e)
|
|
7477
|
+
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,T.readServiceConfig)(c)
|
|
7478
|
+
return r||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),void await o(c,r)}if("stop"===t[0]){if(!await(0,R.isServiceRunning)(c))return void console.log("The AlchemyLab service is not running.")
|
|
7479
|
+
await(0,R.request)(c,"shutdown")
|
|
7480
|
+
for(let e=0;e<240&&await(0,R.isServiceRunning)(c);e++)await new Promise(e=>setTimeout(e,250))
|
|
7462
7481
|
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()
|
|
7463
|
-
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,
|
|
7464
|
-
const r=await(0,
|
|
7465
|
-
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,
|
|
7466
|
-
const e=await(0,
|
|
7467
|
-
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,
|
|
7468
|
-
return e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(
|
|
7469
|
-
const t=
|
|
7470
|
-
try{const s=await(0,
|
|
7471
|
-
if("moved"===s||"no-candidates"===s){const s=
|
|
7472
|
-
if(i){const t=(0,
|
|
7473
|
-
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()}}(
|
|
7474
|
-
e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(
|
|
7475
|
-
const t=await(0,
|
|
7476
|
-
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,
|
|
7477
|
-
let
|
|
7478
|
-
const l
|
|
7479
|
-
|
|
7480
|
-
const
|
|
7481
|
-
|
|
7482
|
-
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,
|
|
7483
|
-
if("status"===t)return
|
|
7484
|
-
if("approve"===t){const e=(await(0,
|
|
7482
|
+
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,R.isServiceRunning)(c)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
|
|
7483
|
+
const r=await(0,R.request)(c,"computer-gone",{machineId:e})
|
|
7484
|
+
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,R.isServiceRunning)(c)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
|
|
7485
|
+
const e=await(0,R.request)(c,"pair-channel",{code:t[1]})
|
|
7486
|
+
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,T.readServiceConfig)(c)
|
|
7487
|
+
return e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(c,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))
|
|
7488
|
+
const t=h(e),r=p()
|
|
7489
|
+
try{const s=await(0,y.runNewComputerFlow)(t,r,{explicit:!0})
|
|
7490
|
+
if("moved"===s||"no-candidates"===s){const s=k.default.realpathSync(process.cwd()),n=k.default.existsSync(S.default.join(s,v.ALCHEMY_DIR_NAME,"project.json")),{here:o}=await t.candidates(),i=n?null:await(0,y.pickMovedProject)(o,s,r)
|
|
7491
|
+
if(i){const t=(0,w.attachFolder)(s,i,(0,m.machineIdentity)(e).id)
|
|
7492
|
+
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()}}(c)}if("move-words-here"===t[0]){const e=(0,T.readServiceConfig)(c)
|
|
7493
|
+
e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(c,e)
|
|
7494
|
+
const t=await(0,R.request)(c,"move-words-here",{},9e4)
|
|
7495
|
+
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,_.ensurePrivateDir)(c.dir)
|
|
7496
|
+
let l=(0,T.readServiceConfig)(c)
|
|
7497
|
+
const u=!l
|
|
7498
|
+
l?process.env.ALCHEMY_OPENCODE_DIR&&process.env.ALCHEMY_OPENCODE_DIR!==l.opencodeDir&&(l={...l,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR},(0,T.writeServiceConfig)(c,l),console.log(`Projects will run with the OpenCode build in ${l.opencodeDir} from their next start.`)):(l={remoteServerUrl:e,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR||void 0,privateData:!0,createdAt:(new Date).toISOString()},(0,T.writeServiceConfig)(c,l),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.')),u&&"ready"!==(await(0,C.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."),!u||!process.stdin.isTTY||"darwin"!==process.platform&&"linux"!==process.platform||(0,H.loginItemInstalled)(e)||(await n("Start AlchemyLab every time this computer starts?",!0)?await o(c,l):console.log("Not set. `alchemy service install` sets it later."))
|
|
7499
|
+
const f=await(0,R.isServiceRunning)(c),I=new x.ProjectsStore(c.projects)
|
|
7500
|
+
I.exists()||f||I.touch(),f||await(0,R.startServiceInBackground)(c,l,r())||(console.error(a(c.log)),process.exit(1)),s.forFolder?console.log("AlchemyLab service: running."):await d(c)},exports.serviceDidNotStart=a,exports.lastLogReason=c,exports.printStatus=d,exports.runControl=async function(e,t,r){const o=function(e){const t=s(e)
|
|
7501
|
+
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,T.readServiceConfig)(o)
|
|
7502
|
+
if("status"===t)return d(o)
|
|
7503
|
+
if("approve"===t){const e=(await(0,R.request)(o,"approvals")).approvals??[]
|
|
7485
7504
|
if(0===e.length)return void console.log("Nothing is waiting for your approval.")
|
|
7486
7505
|
for(const t of e){console.log(""),console.log(t.message)
|
|
7487
7506
|
const e=await n("Approve?",!1)
|
|
7488
|
-
await(0,
|
|
7489
|
-
const
|
|
7490
|
-
if(e.startsWith("/")||e.startsWith(".")||e.startsWith("~")){const t=e.startsWith("~")?
|
|
7491
|
-
try{return
|
|
7492
|
-
if("add"===t){const e=await(0,
|
|
7493
|
-
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"} ${
|
|
7494
|
-
const e=await(0,
|
|
7507
|
+
await(0,R.request)(o,"approve",{approvalId:t.id,yes:e}),console.log(e?"Approved.":"Declined.")}return}await i(o,a)
|
|
7508
|
+
const c=function(e){if(!e)return k.default.realpathSync(process.cwd())
|
|
7509
|
+
if(e.startsWith("/")||e.startsWith(".")||e.startsWith("~")){const t=e.startsWith("~")?S.default.join(process.env.HOME??"",e.slice(1)):S.default.resolve(e)
|
|
7510
|
+
try{return k.default.realpathSync(t)}catch{return t}}return e}(r)
|
|
7511
|
+
if("add"===t){const e=await(0,R.request)(o,"add",{path:c})
|
|
7512
|
+
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"} ${S.default.basename(c)}...`)
|
|
7513
|
+
const e=await(0,R.request)(o,t,{ref:c},18e4)
|
|
7495
7514
|
for(const t of e.lines??[])e.ok&&console.log(t)
|
|
7496
|
-
return e.ok||(
|
|
7515
|
+
return e.ok||(u(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,R.request)(o,t,{ref:c},12e4)
|
|
7497
7516
|
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
|
-
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=
|
|
7517
|
+
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=k.default.realpathSync(process.cwd()),a=(0,$.createLogger)({serviceName:"alchemy-cli",console:!1,logToFile:!0,logFilePath:s.log,baseDir:s.dir}),c=new j.TokenManager(a,o,{remoteServerUrl:e,allowInteractiveAuth:!1})
|
|
7499
7518
|
await c.initialize()
|
|
7500
|
-
const
|
|
7519
|
+
const l=await(0,P.ensureSignedIn)(c)
|
|
7501
7520
|
await i(s,n)
|
|
7502
|
-
const
|
|
7503
|
-
|
|
7504
|
-
if(!process.stdin.isTTY||!process.stdout.isTTY||
|
|
7505
|
-
if(((await(0,
|
|
7506
|
-
const r=
|
|
7521
|
+
const d=await(0,R.request)(s,"connect-now",{},3e4).catch(()=>{})
|
|
7522
|
+
d?.ok||await(0,R.request)(s,"connect-now",{},3e4).catch(()=>{}),await async function(e){const t=S.default.join(e.dir,"new-computer-asked.json")
|
|
7523
|
+
if(!process.stdin.isTTY||!process.stdout.isTTY||k.default.existsSync(t))return
|
|
7524
|
+
if(((await(0,R.request)(e,"status")).projects??[]).length>0)return
|
|
7525
|
+
const r=h(e),{candidates:s}=await r.candidates()
|
|
7507
7526
|
if(0===s.length)return
|
|
7508
|
-
const n=
|
|
7509
|
-
try{await(0,
|
|
7510
|
-
const f=await(0,
|
|
7527
|
+
const n=p()
|
|
7528
|
+
try{await(0,y.runNewComputerFlow)(r,n,{explicit:!1}),k.default.writeFileSync(t,JSON.stringify({askedAt:(new Date).toISOString()})+"\n",{mode:384}),n.say("")}finally{n.close()}}(s).catch(()=>{})
|
|
7529
|
+
const f=await(0,R.request)(s,"add",{path:o})
|
|
7511
7530
|
f.ok||(console.error(`\u274c ${f.reason}`),process.exit(1))
|
|
7512
|
-
const m=await(0,
|
|
7531
|
+
const m=await(0,R.request)(s,"start",{ref:o},18e4)
|
|
7513
7532
|
for(const e of m.lines??[])console.log(e)
|
|
7514
|
-
m.ok||(
|
|
7515
|
-
const
|
|
7516
|
-
if(console.log(""),console.log(` Alchemy UI: \x1b]8;;${
|
|
7517
|
-
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
|
|
7518
|
-
await
|
|
7519
|
-
const
|
|
7520
|
-
for(let t=0;t<6;t++){try{if("alchemylab"===JSON.parse(
|
|
7533
|
+
m.ok||(u(m),process.exit(1)),(0,O.printBanner)(),console.log(`\u{1f4c1} Project: ${o}`),console.log(""),console.log(" Action server .... port "+m.port)
|
|
7534
|
+
const g=await(0,D.resolveFrontendUrl)(e)
|
|
7535
|
+
if(console.log(""),console.log(` Alchemy UI: \x1b]8;;${g}\x07${g}\x1b]8;;\x07`),console.log(""),m.firstRun){const e=`${g.replace(/\/$/,"")}${m.projectId?`/projects/${m.projectId}`:""}/timeline?welcome=1`
|
|
7536
|
+
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,E.openBrowser)(e,`Open this link in a browser on any device to continue there, with the tour: ${e}`)}const{InteractiveShell:w}=await t.e(994).then(()=>t.t(75994,23)),v=new w({projectPath:o,logger:a,remoteServerUrl:e,authToken:l,projectId:m.projectId,hasCode:!0===m.hasCode})
|
|
7537
|
+
await v.start(async()=>{console.log(""),console.log(`AlchemyLab keeps running ${S.default.basename(o)}. \`alchemy stop\` in this folder stops it.`),process.exit(0)})}
|
|
7538
|
+
const m=t(60134),g=t(89122),w=t(98074),y=t(62671),v=t(14139),b=t(79564),k=f(t(79896)),S=f(t(16928)),I=f(t(23785)),$=t(12782),j=t(11253),E=t(90629),A=t(42403),C=t(88918),_=t(18944),T=t(30030),x=t(52800),M=t(81480),R=t(56257),P=t(19071),O=t(64504),D=t(38778),q=t(35382),N=t(60134),F=t(71918),L=t(36968),U=t(76231),B=t(67518),H=t(99269),W=t(11773),G=(()=>{let e=__dirname
|
|
7539
|
+
for(let t=0;t<6;t++){try{if("alchemylab"===JSON.parse(k.default.readFileSync(S.default.join(e,"package.json"),"utf8"))?.name)return e}catch{}const t=S.default.dirname(e)
|
|
7521
7540
|
if(t===e)break
|
|
7522
7541
|
e=t}})()},30030(e,exports,t){"use strict"
|
|
7523
7542
|
exports.readServiceConfig=function(e){const t=(0,r.readJson)(e.config)
|
|
@@ -7843,7 +7862,7 @@ let a=""
|
|
|
7843
7862
|
try{a=(await s.readFile(r,"utf8")).slice(0,400)}catch{}if(o.has(t)&&/^---[\s\S]*\bmode:\s*(primary|subagent|all)\b/m.test(a)){await s.unlink(r)
|
|
7844
7863
|
continue}const c=`${r}.removed-by-alchemy-${i}`
|
|
7845
7864
|
try{await s.rename(r,c),this.logger.warn("Moved an agent file AlchemyLab did not write out of the way (agent prompts are served from memory now)",{from:r,movedTo:c})}catch(e){this.logger.warn("Could not move an unrecognised agent file aside; leaving it",{path:r,error:e instanceof Error?e.message:String(e)})}}}catch{}}buildExploreMarkdown(e){return`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${e?e.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`}async resolveWorkflowEffort(e){try{const t=await this.fetchDefaultWorkflowFromRemote(e),r="string"==typeof t?.effort&&g.has(t.effort)?t.effort:exports.NK
|
|
7846
|
-
r!==w&&this.logger.info("Effort dial read from the workflow",{effort:r,was:w}),w=r}catch{}return w}hasConnectedProviders(){return null!==this.connectedProviders}async refreshConnectedProviders(e){const t=await(0,a.fetchConnectedProviders)(e)
|
|
7865
|
+
r!==w&&this.logger.info("Effort dial read from the workflow",{effort:r,was:w}),w=r}catch{}return w}hasConnectedProviders(){return null!==this.connectedProviders}getConnectedProviders(){return this.connectedProviders}async refreshConnectedProviders(e){const t=await(0,a.fetchConnectedProviders)(e)
|
|
7847
7866
|
if(!t)return!1
|
|
7848
7867
|
const r=JSON.stringify(t)!==JSON.stringify(this.connectedProviders)
|
|
7849
7868
|
return this.connectedProviders=t,r}generateAgentMarkdown(e,t,r,n,o={}){let c=r
|
|
@@ -8701,14 +8720,16 @@ if(!t)return`NOT-MEASURED-YET:it is measured after the work is completed, on the
|
|
|
8701
8720
|
const s=String(t.note??"").replace(/\s+/g," ").trim().slice(0,300)
|
|
8702
8721
|
return`${"met"===t.verdict?"MEASURED-MET":"unmet"===t.verdict?"MEASURED-WORSE":"MEASURED"}:${s||"measured"}; ${r}`}const s=n(e)
|
|
8703
8722
|
return s?`CHECK-AT-COMPLETION:${s}`:!0===e?.proven?"reviewer"===t.by?"PROVEN:review":"you"===t.by?"PROVEN:user-hand-check":"driver"===t.by?"PROVEN:drive":"PROVEN:test":"not-checked"===r.driver?.verdict?"NOT-CHECKED:drive":"answer"===t.reword?.from?`REWORD:${String(t.reword.note??"the answer it was written for changed").slice(0,300)}; reword it with request_operations update, its key and userSaid "${t.reword.ref}"`:t.notARealTest?"NOT-A-REAL-TEST":t.checkFails?"met"===r.reviewer?.verdict?"FAILED:test (review met, check fails)":"FAILED:test":t.disputed?`DISPUTED:its check passes, but the reviewer says it does not hold${t.disputed.note?` ("${String(t.disputed.note).slice(0,200)}")`:""}; strengthen the check so it tests what the criterion says, or fix the code`:"could-not-run"===r.instrument?.verdict?"COULD-NOT-RUN:test (its check could not run here; it says nothing either way)":"unmet"===r.reviewer?.verdict?"UNMET:review":!0===r.reviewer?.reword?"REWORD:a reviewer found its words no longer fit what was asked; reword it with the user's words, or ask them":"withdrawn"===r.reviewer?.verdict?"RE-REVIEW:its review was withdrawn (the work changed after it, or a new pass is due)":e?.coder?"ATTESTED":"UNPROVEN"}function n(e){const t=e?.proof?.checkSetup
|
|
8704
|
-
return"string"==typeof t&&t?`its check starts when the request is completed: set up at completion (setup run ${t}). The check runs what this request adds, so it is registered once the request is completed, and its first run on main is the proof, written on the criterion then. Decided by that run only, never by a review: registering the check is not a part of this request left undone. What the check will run is in the diff and is yours to read`:null}function o(e){const r
|
|
8723
|
+
return"string"==typeof t&&t?`its check starts when the request is completed: set up at completion (setup run ${t}). The check runs what this request adds, so it is registered once the request is completed, and its first run on main is the proof, written on the criterion then. Decided by that run only, never by a review: registering the check is not a part of this request left undone. What the check will run is in the diff and is yours to read`:null}function o(e){const t=String(e??"").trim(),r=/^\[?(\d+[a-z])\](?:\s|$)/i.exec(t)??/^(\d+[a-z])$/i.exec(t)
|
|
8724
|
+
return r?r[1].toLowerCase():t}function i(e){const r=new c
|
|
8705
8725
|
for(let s=0;s<e.length;s++){const n=t(e[s].id,s)
|
|
8706
|
-
r.set(n,e[s])}return r}function
|
|
8707
|
-
const t=
|
|
8726
|
+
r.set(n,e[s])}return r}function a(e){if(0===e.length)return"No acceptance criteria defined."
|
|
8727
|
+
const t=i(e),r=[]
|
|
8708
8728
|
for(const[e,n]of t){const t=s(n),o="objective"===n.kind||"subjective"===n.kind||"gate"===n.kind?` [${n.kind.toUpperCase()}]`:"",i=n.standing?n.waived?" [WAIVED for this request by the user; it no longer binds, do not work on or judge it]":` [FROM THE ${n.standing.processName?`PROCESS "${String(n.standing.processName).slice(0,60)}"`:"WORKFLOW"}: every request that follows it carries it; it cannot be dropped or reworded; if it does not apply, the guidance tool with guidanceId "[guidance:${n.standing.ruleKey?String(n.standing.ruleKey):String(n.standing.id??"")}]": exception when the user said so, else conflict]`:"",a="string"==typeof n.environment&&n.environment.trim()?` [its checks run in the "${n.environment.trim().slice(0,80)}" environment]`:""
|
|
8709
8729
|
r.push(`[${e}] [${t}]${o}${i}${a} ${n.description}`)}return r.join("\n")}exports.formatCriteriaWithHashes=void 0,exports.criterionShortKey=t,exports.latestRow=r,exports.reviewedSha=function(e){const t=r(e,["reviewer"])
|
|
8710
8730
|
return t&&("met"===t.verdict||"unmet"===t.verdict)&&"string"==typeof t.commit&&t.commit?t.commit:null},exports.criterionPlainLine=function(e){const t=String(e?.description??"").trim(),r=n(e)
|
|
8711
|
-
return t&&r?`${t} (${r})`:t},exports.buildCriteriaKeyMap=
|
|
8731
|
+
return t&&r?`${t} (${r})`:t},exports.buildCriteriaKeyMap=i,exports.formatCriteriaWithKeys=a
|
|
8732
|
+
class c extends Map{get(e){return super.get(e)??super.get(o(e))}has(e){return super.has(e)||super.has(o(e))}}exports.formatCriteriaWithHashes=a},75453(e,exports,t){"use strict"
|
|
8712
8733
|
function r(e,t,r={}){return new Promise(s=>{const n=(0,y.execFile)(r.gitBinary||"git",t,{cwd:e,env:{...process.env,...r.env||{}},maxBuffer:33554432},(e,t,r)=>{s({code:e?"number"==typeof e.code?e.code:1:0,stdout:String(t??""),stderr:String(r??"")})})
|
|
8713
8734
|
void 0!==r.stdin&&n.stdin?.end(r.stdin)})}async function s(e,t,s){const n=await r(e,["rev-parse","--verify","--quiet",`${t}^{commit}`],{gitBinary:s})
|
|
8714
8735
|
return 0===n.code?n.stdout.trim():null}function n(e){try{return v.realpathSync(e)}catch{return b.resolve(e)}}async function o(e,t){const s=await r(e,["worktree","list","--porcelain"],{gitBinary:t})
|
|
@@ -9285,19 +9306,21 @@ s&&(t={...t,toSha:s})
|
|
|
9285
9306
|
try{const r=await e.post(`/api/projects/${e.projectId}/requests/${e.requestId}/main-change-review`,{touched:t,landing:!0}),s=r?.data??r
|
|
9286
9307
|
return{reviewing:!0===s?.reviewing}}catch(t){return e.logger.warn("held for a review of main's change: the review could not be asked for",{requestId:e.requestId,error:t instanceof Error?t.message:String(t)}),{reviewing:!1}}}
|
|
9287
9308
|
const s=t(35317),n=t(35510)},49779(e,exports){"use strict"
|
|
9288
|
-
function t(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_")}function
|
|
9289
|
-
const
|
|
9290
|
-
return!t.startsWith("/")&&!/^[a-zA-Z]:/.test(t)&&!t.split("/").includes("..")},
|
|
9291
|
-
if("string"!=typeof
|
|
9292
|
-
if(
|
|
9309
|
+
function t(e){return/[\r\n]/.test(e)||e.trim().length>240}function r(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_")}function s(e,t){return`${r(e)}_${r(t)}`}function n(e){return`${r(e)}_*`}exports.x4=exports.Tt=void 0,exports.validateRepoConfig=function(e){if(void 0!==e.command)return"A repository connection derives its own command from the image; give it a dockerfile instead"
|
|
9310
|
+
const r=e=>{const t=e.replace(/\\/g,"/")
|
|
9311
|
+
return!t.startsWith("/")&&!/^[a-zA-Z]:/.test(t)&&!t.split("/").includes("..")},s=e.dockerfile
|
|
9312
|
+
if("string"!=typeof s||!s.trim())return"A repository connection needs the dockerfile to build, as a path inside the repository, for example mcp/server.Dockerfile"
|
|
9313
|
+
if(t(s))return"dockerfile is the path of a Dockerfile in this repository, for example mcp/server.Dockerfile, not its contents. To run a published image, the repository needs a Dockerfile that says FROM <the image>: have it written in the repository first, then give its path here."
|
|
9314
|
+
if(!r(s.trim()))return`dockerfile must be a path inside the repository, not "${s}"`
|
|
9293
9315
|
if(void 0!==e.context){if("string"!=typeof e.context||!e.context.trim())return"context, when given, is a directory inside the repository"
|
|
9294
|
-
if(
|
|
9295
|
-
|
|
9296
|
-
|
|
9316
|
+
if(t(e.context))return"context is the path of a directory in this repository (the docker build context), for example mcp/, not a description. Leave it out to build from the repository root."
|
|
9317
|
+
if(!r(e.context.trim()))return`context must be a directory inside the repository, not "${e.context}"`}return null},exports.namespacePattern=n,exports.globalDenyRules=function(e){const t={}
|
|
9318
|
+
for(const r of e)t[n(r.name)]="deny"
|
|
9319
|
+
return t},exports.agentToolGrants=function(e,t,r){const o=new Set(r?.[t]??[])
|
|
9297
9320
|
if(0===o.size)return{}
|
|
9298
9321
|
const i={}
|
|
9299
|
-
for(const t of e)if(t.enabled&&o.has(t.name))if(null!==t.allowedTools)for(const e of t.allowedTools)i[
|
|
9300
|
-
else i[
|
|
9322
|
+
for(const t of e)if(t.enabled&&o.has(t.name))if(null!==t.allowedTools)for(const e of t.allowedTools)i[s(t.name,e)]=!0
|
|
9323
|
+
else i[n(t.name)]=!0
|
|
9301
9324
|
return i},exports.diffManifests=function(e,t){if(!e||!t)return null
|
|
9302
9325
|
const r=new Map(e.map(e=>[e.name,e])),s=new Map(t.map(e=>[e.name,e])),n=[...s.keys()].filter(e=>!r.has(e)).sort(),o=[...r.keys()].filter(e=>!s.has(e)).sort(),i=[...s.entries()].filter(([e,t])=>!0===r.get(e)?.readOnly&&!0!==t.readOnly).map(([e])=>e).sort()
|
|
9303
9326
|
return 0===n.length&&0===o.length&&0===i.length?null:{added:n,removed:o,nowWrites:i}},exports.describeDrift=function(e,t){const r=[]
|
|
@@ -10267,7 +10290,7 @@ const j=s.filter((e,t)=>"--port"!==e&&(-1===$||t!==$+1))
|
|
|
10267
10290
|
if(0===j.length){try{for(const e of(0,m.otherInstallsNotice)(process.argv[1],(0,m.otherInstalls)(process.argv[1])))console.log(e)}catch{}if(void 0===i){const e=await(0,I.getRemoteServerUrl)(o())
|
|
10268
10291
|
let t=(0,k.serviceSetUp)(e)
|
|
10269
10292
|
if(!t){const r=o(),s=new d.TokenManager(r,process.cwd(),{remoteServerUrl:e,allowInteractiveAuth:!1})
|
|
10270
|
-
await s.initialize(),await(0,v.ensureSignedIn)(s),await(0,k.runService)(e,[]),t=(0,k.serviceSetUp)(e)}if(t)return void await(0,k.runSupervisedDefault)(e,t)}return void await async function(t={}){const r=process.cwd()
|
|
10293
|
+
await s.initialize(),await(0,v.ensureSignedIn)(s),await(0,k.runService)(e,[],{forFolder:!0}),t=(0,k.serviceSetUp)(e)}if(t)return void await(0,k.runSupervisedDefault)(e,t)}return void await async function(t={}){const r=process.cwd()
|
|
10271
10294
|
n(r)
|
|
10272
10295
|
const s=(0,l.join)(r,p.ALCHEMY_DIR_NAME)
|
|
10273
10296
|
await(0,c.mkdir)(s,{recursive:!0})
|
|
@@ -10287,7 +10310,7 @@ for(const t of e.error.split("\n").slice(0,4))console.log(` ${t}`)
|
|
|
10287
10310
|
console.log(` Full log: ${(0,l.join)(t,p.ALCHEMY_DIR_NAME,"server.log")}`)}}(o,r),console.log("")
|
|
10288
10311
|
const i=`\x1b]8;;${n}\x07${n}\x1b]8;;\x07`
|
|
10289
10312
|
if(console.log(" Alchemy UI: "+i),console.log(""),k){const e=`${n.replace(/\/$/,"")}${S?`/projects/${S}`:""}/timeline?welcome=1`
|
|
10290
|
-
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(""),await j,(0,f.openBrowser)(e
|
|
10313
|
+
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(""),await j,(0,f.openBrowser)(e,`Open this link in a browser on any device to continue there, with the tour: ${e}`)}let c=!1,d=null
|
|
10291
10314
|
const h=async()=>{c||(c=!0,d&&clearInterval(d),console.log(""),console.log("Shutting down..."),await s.stop(),process.exit(0))}
|
|
10292
10315
|
process.on("SIGTERM",h),process.on("SIGINT",h)
|
|
10293
10316
|
const{InteractiveShell:w}=await e.e(994).then(()=>e.t(75994,23)),b=new w({projectPath:r,logger:a,remoteServerUrl:m,authToken:y,projectId:S,hasCode:$})
|