alchemylab 1.4.13 → 2.0.1
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/README.md +39 -0
- package/dist/129.js +25 -0
- package/dist/19.js +260 -0
- package/dist/{800.js → 237.js} +5 -5
- package/dist/301.js +138 -0
- package/dist/380.js +17 -0
- package/dist/403.js +4 -0
- package/dist/491.js +31 -0
- package/dist/540.js +56 -0
- package/dist/554.js +26 -0
- package/dist/{60.js → 565.js} +8 -8
- package/dist/636.js +11 -0
- package/dist/674.js +15 -0
- package/dist/729.js +89 -0
- package/dist/796.js +47 -0
- package/dist/cli.js +6571 -1611
- package/dist/document-textconv.js +484 -0
- package/dist/http-server.js +7244 -5396
- package/dist/index.js +7257 -5419
- package/dist/mcp-direct.js +7137 -5250
- package/dist/plugins/hooks/plugin.js +1 -157
- package/dist/plugins/inject-session.js +1 -66
- package/dist/plugins/system-logger.js +1 -93
- package/package.json +26 -15
- package/dist/414.js +0 -9
- package/dist/478.js +0 -109
- package/dist/939.js +0 -17
- package/dist/994.js +0 -126
- package/dist/mcp-cli-bridge.js +0 -96
- package/dist/plugins/hooks/lib/condition-evaluator.js +0 -114
- package/dist/plugins/hooks/lib/dedup-utils.js +0 -94
- package/dist/plugins/hooks/lib/hook-utils.js +0 -90
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# AlchemyLab
|
|
2
|
+
|
|
3
|
+
AlchemyLab runs a fleet of coding agents against your project and keeps a record of what they did: every change is a request with a branch, tests, review and a merge you can read back.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Requires Node 24 or newer. Docker is optional; with it, tests and experiments run in sealed containers.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm install -g alchemylab
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Start
|
|
14
|
+
|
|
15
|
+
Run it in the directory of the project you want to work on:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
cd my-project
|
|
19
|
+
alchemy
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The first run asks you to log in with your AlchemyLab account and opens the agent console. Keep it running; the web app talks to it.
|
|
23
|
+
|
|
24
|
+
## Work
|
|
25
|
+
|
|
26
|
+
Open the project at https://alchemylabcode.com and say what you want in plain words. The lead agent registers each ask as a request, hands it to a coder, and reports back in the same stream. Questions that need you show up there too.
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
alchemy start the local server and the agent console (default)
|
|
32
|
+
alchemy serve start the local server only
|
|
33
|
+
alchemy logout forget the stored login
|
|
34
|
+
alchemy --help
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## More
|
|
38
|
+
|
|
39
|
+
Documentation and help live inside the app under Help.
|
package/dist/129.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
exports.id=129,exports.ids=[129],exports.modules={27129:function(t,exports,e){function r(t,e){return new Promise((r=>{(0,l.execFile)("git",e,{cwd:t,maxBuffer:16777216},((t,e,i)=>{r({code:t?"number"==typeof t.code?t.code:1:0,stdout:String(e??""),stderr:String(i??"")})}))}))}async function i(t){return 0===(await r(t,["rev-parse","--verify","--quiet","HEAD"])).code}function s(t){try{return u.realpathSync(t)}catch{return d.resolve(t)}}var o,a=this&&this.__createBinding||(Object.create?function(t,e,r,i){void 0===i&&(i=r)
|
|
3
|
+
var s=Object.getOwnPropertyDescriptor(e,r)
|
|
4
|
+
s&&!("get"in s?!e.__esModule:s.writable||s.configurable)||(s={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,i,s)}:function(t,e,r,i){void 0===i&&(i=r),t[i]=e[r]}),n=this&&this.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),c=this&&this.__importStar||(o=function(t){return o=Object.getOwnPropertyNames||function(t){var e=[]
|
|
5
|
+
for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[e.length]=r)
|
|
6
|
+
return e},o(t)},function(t){if(t&&t.__esModule)return t
|
|
7
|
+
var e={}
|
|
8
|
+
if(null!=t)for(var r=o(t),i=0;i<r.length;i++)"default"!==r[i]&&a(e,t,r[i])
|
|
9
|
+
return n(e,t),e})
|
|
10
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.hasCommits=i,exports.ensureProjectRepository=async function(t){let e=null
|
|
11
|
+
if(!u.existsSync(d.join(t,".git"))){const i=await r(t,["rev-parse","--show-toplevel"])
|
|
12
|
+
if(0===i.code&&i.stdout.trim()){const o=s(i.stdout.trim())
|
|
13
|
+
if(o!==s(t)){const i=await r(t,["ls-files","--","."])
|
|
14
|
+
if(0!==i.code||i.stdout.trim())return{status:"failed",message:`This folder's files belong to the git repository at ${o}, so AlchemyLab will not create a second repository here. Start AlchemyLab in ${o} (requests branch from that repository's default branch), or run \`git init\` here yourself if a nested repository is really what you want.`}
|
|
15
|
+
e=o}}}if(u.existsSync(d.join(t,".git")))return await(0,m.ensureAlchemyIgnoreEntries)(t),await i(t)?{status:"existing",hasHistory:!0}:{status:"existing",hasHistory:!1,message:'This repository has no commits yet. Requests branch from your default branch, so make a first commit (for example `git add -A && git commit -m "Initial commit"`), then start AlchemyLab again.'}
|
|
16
|
+
const o=await r(t,["init","-q","-b","main"])
|
|
17
|
+
if(0!==o.code)return{status:"failed",message:`Could not create a git repository here: ${o.stderr.trim()||"git is not available"}. Install git and run \`git init\` in this folder.`}
|
|
18
|
+
await(0,m.ensureAlchemyIgnoreEntries)(t)
|
|
19
|
+
const a=[];(await r(t,["config","user.email"])).stdout.trim()||a.push("-c","user.name=AlchemyLab","-c","user.email=alchemylab@localhost")
|
|
20
|
+
const n=await r(t,["add","-A"])
|
|
21
|
+
if(0!==n.code)return{status:"failed",message:`Created the repository but could not record the folder: ${n.stderr.trim()}`}
|
|
22
|
+
let c=""
|
|
23
|
+
try{c=(await(0,f.holdBackStagedCredentials)(t)).note}catch(t){return{status:"failed",message:`Created the repository but could not check the folder for key material before its first commit: ${t instanceof Error?t.message:String(t)}`}}const l=(await r(t,["diff","--cached","--name-only"])).stdout.split("\n").filter(Boolean).length,h=await r(t,[...a,"commit","-q","--allow-empty","-m","Initial commit (AlchemyLab created this repository)"+(c?`\n\n${c}`:"")])
|
|
24
|
+
return 0!==h.code?{status:"failed",message:`Created the repository but could not make its first commit: ${h.stderr.trim()}`}:{status:"created",filesCommitted:l,message:(l>0?`Created a git repository and recorded the ${l} file${1===l?"":"s"} already here as its first commit.`:"Created a git repository with an empty first commit.")+(e?` This folder is inside the repository at ${e}, which tracks nothing here, so it has its own.`:"")+(c?` ${c}`:"")}}
|
|
25
|
+
const l=e(35317),u=c(e(79896)),d=c(e(16928)),m=e(84935),f=e(82182)}}
|
package/dist/19.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
exports.id=19,exports.ids=[19],exports.modules={16554:(e,exports,t)=>{function n(e,t,n){if(j)try{const s=`[${(new Date).toISOString()}] ${t}${n?" "+JSON.stringify(n):""}\n`,o=(0,x.join)(e.basePath||process.cwd(),".alchemy_lab","spawn-debug.log");(0,v.appendFileSync)(o,s)}catch{}}async function s(e,n,s,o,i,r){if(!(e&&n&&s&&o))return null
|
|
3
|
+
try{const i=await e.proxyPost(`/api/projects/${n}/requests/${s}/pickup-compose`,{sessionId:o}),a=i?.data?.text??i?.text,l="string"==typeof a&&a.trim()?a.trim():null
|
|
4
|
+
if(l&&/Project map/.test(l))return l
|
|
5
|
+
let c=null
|
|
6
|
+
try{const{generateProjectMap:e}=await t.e(491).then(t.t.bind(t,23491,23))
|
|
7
|
+
c=e(r||process.cwd())}catch{c=null}return l||c?[l,c].filter(Boolean).join("\n\n"):null}catch(e){return i.warn("pickup-compose failed; the association hook will deliver guidance as an inject",{requestId:s,error:(0,k.getErrorMessage)(e)}),null}}async function o(e){const{apiClient:t,fromSessionId:n,toSessionId:s,body:o,requestId:i,logger:r}=e
|
|
8
|
+
if(!t||!n)return
|
|
9
|
+
const a=t.getSessionMessageClient()
|
|
10
|
+
if(a)try{const e=await a.createInstruction({fromSessionId:n,toSessionId:s,body:o,...i?{requestId:i}:{}})
|
|
11
|
+
return e?.messageCode}catch(e){return void r.warn("Failed to mint instruction code",{error:(0,k.getErrorMessage)(e),fromSessionId:n,toSessionId:s})}}async function i(e,t,n,s,o){if(e&&t&&n)try{await e.proxyPost(`/api/projects/${t}/session-messages/${encodeURIComponent(n)}/delivered`,{ok:s.ok,error:s.detail||s.error,messageId:s.messageId})}catch(e){o.warn("instruction delivery receipt not written",{instructionCode:n,error:(0,k.getErrorMessage)(e)})}}async function r(e,t,r,a,l){const{agent:c="work",prompt:d,requestId:u,allowParallel:p=!1,useExistingSession:g}=e
|
|
12
|
+
if(n(t,"handleSpawn called",{agent:c,promptLength:d.length,requestId:u,useExistingSession:g,cliOriginSessionId:l?.sessionId||null}),!(0,A.isValidAgent)(c))return{content:[{type:"text",text:`\u274c Unknown agent '${c}'.\n\n**Available agents:** ${(0,A.getAgentNames)().join(", ")}\n\nChoose one of the available agents listed above to see detailed descriptions of each agent.`}],isError:!0}
|
|
13
|
+
const f=l?.sessionId,h=f?I.sessionIdentity.getAgentTypeFor(f).then((e=>e||void 0)):Promise.resolve(void 0),m=async function(e){try{const t=`http://localhost:${E}/api/agents/by-name/${encodeURIComponent(e)}`,n=await fetch(t,{signal:AbortSignal.timeout(8e3)})
|
|
14
|
+
if(!n.ok)return null
|
|
15
|
+
const s=await n.json()
|
|
16
|
+
return s.success?s.data??null:null}catch{return null}}(c).catch((()=>null)),v=(0,$.getServerUrl)(t,r),x=(async()=>{if(!a||!u)return null
|
|
17
|
+
const e=a.getSessionRequestAssociationClient()
|
|
18
|
+
return e?await e.getSessionsForRequest(u,!0):null})().catch((e=>(r.debug("Failed to check for existing workers",{error:(0,k.getErrorMessage)(e)}),null))),[T,q,P,R]=await Promise.all([h,m,v,x])
|
|
19
|
+
try{if(n(t,"Spawn agent type check",{callerSessionId:f||null,callerAgentType:T||null,pid:process.pid}),T){const e=(0,A.getAllowedSubagents)(T)
|
|
20
|
+
if(e&&!e.includes(c))return{content:[{type:"text",text:`\u274c Agent type '${T}' cannot spawn '${c}'.\n\n**Allowed subagents for ${T}:** ${e.join(", ")}\n\nUse one of the allowed agent types instead.`}],isError:!0}}}catch(e){n(t,"allowedSubagents check failed",{error:(0,k.getErrorMessage)(e)})}let U=q?.requestContextMode
|
|
21
|
+
if(!U){const e=(0,A.getBuiltInAgentConfig)(c)
|
|
22
|
+
U=e?.requestContextMode}if(U&&"none"!==U&&!u)return{content:[{type:"text",text:`\u274c Agent '${c}' requires a requestId to be provided.\n\nThis agent has requestContextMode: '${U}' which means it needs to work in the context of a specific request.\n\nPlease provide a requestId when spawning this agent.`}],isError:!0}
|
|
23
|
+
if(u&&"required"===U&&a)try{const e=await a.getRequestClient().getRequest(u)
|
|
24
|
+
if("draft"===e?.status){const t=e?.intake,n="rejected"===t?.status?`the intake reviewer rejected it: ${String(t?.objection||"").slice(0,240)||"(no objection text)"}`:"pending"===t?.status?"it is held at intake awaiting a verdict":"it has not been registered"
|
|
25
|
+
return{content:[{type:"text",text:`\u274c ${u} is a DRAFT: ${n}.\n\nWork cannot start on drafts. Wait for the request to be registered (the user resolves the intake question from the timeline), or address the objection with a revised proposal. Do NOT spawn workers for draft requests.`}],isError:!0}}}catch(e){n(t,"draft-spawn guard read failed (continuing)",{error:(0,k.getErrorMessage)(e)})}if(n(t,"serverUrl resolved",{serverUrl:P}),!t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath&&!t.basePath)throw new w.ValidationError("Working directory not available in context")
|
|
26
|
+
const M=l?.sessionId
|
|
27
|
+
let O=""
|
|
28
|
+
if(R){const e=(M?R.filter((e=>e.opencodeSessionId!==M)):R).filter((e=>!1!==e.working))
|
|
29
|
+
if(e.length>0){const t=e.map((e=>e.opencodeSessionId)).join(", ")
|
|
30
|
+
if(!p)return r.warn("Blocked spawn: request already has active workers",{requestId:u,existingWorkerCount:e.length,existingSessionIds:e.map((e=>e.opencodeSessionId))}),{content:[{type:"text",text:`\u274c Cannot spawn: Request ${u} already has ${e.length} active worker(s): ${t}\n\nOptions:\n\u2022 Use \`useExistingSession: "${e[0].opencodeSessionId}"\` to send work to the existing session\n\u2022 Use \`allowParallel: true\` if you really need multiple workers on the same request`}],isError:!0}
|
|
31
|
+
O=`\u26a0\ufe0f Warning: Request ${u} already has ${e.length} active worker(s): ${t}. Proceeding anyway (allowParallel=true).\n\n`,r.warn("Spawning parallel subagent for request with existing active workers",{requestId:u,existingWorkerCount:e.length,existingSessionIds:e.map((e=>e.opencodeSessionId)),allowParallel:!0})}}let D,L,_=d
|
|
32
|
+
u&&(D=`requests/${u}`,n(t,"Setting initialBranch for agent",{agent:c,requestId:u,initialBranch:D,agentRequestContextMode:U||"undefined"}),_="readonly"===U?`Working on request ${u} (READONLY mode - you cannot modify files, only explore and report).\n\n${d}`:`Working on request ${u}.\n\n${d}`)
|
|
33
|
+
let F,N=!1
|
|
34
|
+
if(g){if(L=g,r.info("Using existing session for spawn",{sessionId:L,requestId:u}),!P)throw new w.ValidationError("Could not determine OpenCode server URL")
|
|
35
|
+
try{let e
|
|
36
|
+
try{const t=await fetch(`http://localhost:${E}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
|
|
37
|
+
if(t.ok){const n=await t.json()
|
|
38
|
+
if(n.data?.defaultModel){const[t,...s]=n.data.defaultModel.split("/")
|
|
39
|
+
e={providerID:t,modelID:s.join("/")}}}}catch{}F=await o({apiClient:a,fromSessionId:l?.sessionId,toSessionId:L,body:_,requestId:u,logger:r})
|
|
40
|
+
let n=_
|
|
41
|
+
const d=await s(a,t.projectId,u,L,r,t.mainBasePath||t.basePath)
|
|
42
|
+
d&&(n=`${d}\n\n---\n\n${n}`)
|
|
43
|
+
const p={body:_,fromSessionId:l?.sessionId,fromAgent:T,...u?{requestId:u}:{},...F?{instructionCode:F}:{}},g=await(0,S.deliverToSession)(L,n,{kind:"agent-delegation",partPayload:p})
|
|
44
|
+
if(await i(a,t.projectId,F,g,r),!g.ok)throw new Error((0,S.deliveryFailureText)(L,g))
|
|
45
|
+
r.info("Prompt delivered to existing session",{sessionId:L,agent:c,messageId:g.messageId})}catch(e){throw new Error(`Failed to use existing session: ${(0,k.getErrorMessage)(e)}`)}}else{N=!0,n(t,"Creating session via spawn-session endpoint")
|
|
46
|
+
const i=M
|
|
47
|
+
j&&(0,b.logSessionIdentity)("subagent_operations.spawn","_meta",{parentSessionId:i})
|
|
48
|
+
try{const g=`http://localhost:${E}/api/spawn-session`,f=await fetch(g,{method:"POST",signal:AbortSignal.timeout(15e3),headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:c,prompt:d,requestId:u,parentSessionId:i,title:e.title,specialization:e.specialization,specializationHeadline:e.specializationHeadline??(e.specialization?e.specialization.split(/[.\n]/)[0].trim().slice(0,80):void 0),allowParallel:p,callerAgentType:T})})
|
|
49
|
+
if(!f.ok){const e=await f.text().catch((()=>"Unknown error"))
|
|
50
|
+
throw new Error(`Spawn session endpoint failed: ${f.status} ${e}`)}const h=await f.json()
|
|
51
|
+
if(!h.success||!h.data)throw new Error(h.error||"Spawn session returned unsuccessful")
|
|
52
|
+
const{sessionId:m,model:w,serverUrl:$,initialBranch:S}=h.data
|
|
53
|
+
D=S,F=await o({apiClient:a,fromSessionId:l?.sessionId,toSessionId:m,body:_,requestId:u,logger:r})
|
|
54
|
+
let I=_
|
|
55
|
+
const b="pickupGuidance"in(h?.data??{})?h.data.pickupGuidance??null:await s(a,t.projectId,u,m,r,t.mainBasePath||t.basePath)
|
|
56
|
+
b&&(I=`${b}\n\n---\n\n${I}`)
|
|
57
|
+
const k=await(0,y.deliverInitialPrompt)({serverUrl:$,sessionId:m,agent:c,prompt:I,model:w,directory:t.mainBasePath,metadata:{alchemy:{kind:"agent-delegation",payload:{body:_,fromSessionId:l?.sessionId,fromAgent:T,...u?{requestId:u}:{},...F?{instructionCode:F}:{}}}}})
|
|
58
|
+
if(!k.success)throw new Error(`Failed to spawn subagent: ${k.error}`)
|
|
59
|
+
L=m,n(t,"Spawn successful via unified endpoint",{sessionId:m,parentSessionId:i,model:w,instructionCode:F}),r.info("Background subagent spawned via unified endpoint",{agent:c,requestId:u,sessionId:m,parentSessionId:i,instructionCode:F})}catch(e){throw new Error(`Failed to spawn subagent: ${(0,k.getErrorMessage)(e)}`)}}let B=O
|
|
60
|
+
B+=g?`Prompt sent to existing session ${L}.`:`Background ${c} subagent spawned successfully.`,u&&(B+=`\nWorking on: ${u}`),F&&(B+=`\nInstruction code: ${F} (the specialist will cite this on their reports/questions via relatesTo)`),B+="\n\nThe subagent is now working independently. You can continue with other tasks."
|
|
61
|
+
const V=!!L&&"unknown"!==L&&"spawning"!==L
|
|
62
|
+
return{content:[{type:"text",text:B}],...(0,C.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:V?L:void 0,agentType:c,requestId:u||void 0,description:e.title||d.slice(0,50)||c,...F?{instructionCode:F}:{}}),...F?{instructionCode:F}:{}}}async function a(e,t,n,s,r,a,l){if(!await(0,$.getServerUrl)(n,s))throw new w.ValidationError("Could not determine OpenCode server URL")
|
|
63
|
+
if(s.info("Sending message to subagent session",{sessionId:e,promptLength:t.length}),r)try{const t=l?.sessionId
|
|
64
|
+
if(t){const n=r.getCLISessionClient()
|
|
65
|
+
if(n){let o=await n.getByExternalId(t)
|
|
66
|
+
if(o||(o=await n.createOrUpdateSession({externalSessionId:t,providerId:"opencode",status:"active"}),s.info("Created caller session in DB for re-parenting",{callerSessionId:t,dbId:o?.id})),o){const i=await n.getByExternalId(e).catch((()=>null)),r=i?.parentSessionId??null
|
|
67
|
+
r===o.id?s.debug("Parent unchanged for continued agent; no re-parent write",{sessionId:e}):(await n.createOrUpdateSession({externalSessionId:e,providerId:"opencode",parentSessionId:o.id,status:"active"}),s.info("RE-PARENTED continued agent: reports now flow to the current caller",{sessionId:e,fromParentDbId:r,toParentDbId:o.id,toCallerSessionId:t}))}}}}catch(e){s.warn("Failed to update parent session - reports may go to wrong parent",{error:(0,k.getErrorMessage)(e)})}let c=a
|
|
68
|
+
if(!c)try{const t=`http://localhost:${E}/api/sessions/${encodeURIComponent(e)}/agent-type`,n=await fetch(t,{signal:AbortSignal.timeout(8e3)})
|
|
69
|
+
if(n.ok){const e=await n.json()
|
|
70
|
+
e.success&&e.agentType&&(c=e.agentType)}}catch{s.warn("Agent type lookup failed for session",{sessionId:e})}if(!c)throw new w.ValidationError(`Cannot continue session ${e}: unable to determine agent type. The session's agent type could not be looked up from the action server.`)
|
|
71
|
+
try{let a
|
|
72
|
+
try{const e=await fetch(`http://localhost:${E}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
|
|
73
|
+
if(e.ok){const t=await e.json()
|
|
74
|
+
if(t.data?.defaultModel){const[e,...n]=t.data.defaultModel.split("/")
|
|
75
|
+
a={providerID:e,modelID:n.join("/")}}}}catch{}const d=await o({apiClient:r,fromSessionId:l?.sessionId,toSessionId:e,body:t,logger:s}),u=t,p={body:t,fromSessionId:l?.sessionId,fromAgent:await I.sessionIdentity.getAgentTypeFor(l?.sessionId)||void 0,...d?{instructionCode:d}:{}},g=await(0,S.deliverToSession)(e,u,{kind:"agent-followup",partPayload:p})
|
|
76
|
+
return await i(r,n.projectId,d,g,s),g.ok?(s.info("Message delivered to subagent session",{sessionId:e,agent:c,instructionCode:d,messageId:g.messageId}),{content:[{type:"text",text:[d?`Message delivered to session ${e} (${d}); it is in that session's transcript.`:`Message delivered to session ${e}; it is in that session's transcript.`,c?`That session is a ${c}. It will process this in the background -- it is NOT proof that work started; check for tasks before reporting progress.`:"Role unknown for that session. It will process this in the background -- that is not proof work started; check for tasks before reporting progress."].join("\n")}],...d?{_meta:{alchemy:{kind:"continue-subagent",instructionCode:d}},instructionCode:d}:{}}):{content:[{type:"text",text:(0,S.deliveryFailureText)(e,g)+(c?` That session is a ${c}.`:"")}],isError:!0,...d?{_meta:{alchemy:{kind:"continue-subagent",instructionCode:d,failed:!0}},instructionCode:d}:{}}}catch(e){throw new Error(`Failed to continue session: ${(0,k.getErrorMessage)(e)}`)}}function l(e){const t=Math.floor(e/1e3)
|
|
77
|
+
if(t<60)return`${t}s`
|
|
78
|
+
const n=Math.floor(t/60)
|
|
79
|
+
return n<60?`${n}m ${t%60}s`:`${Math.floor(n/60)}h ${n%60}m`}function c(e){const t=[]
|
|
80
|
+
for(const n of e)if("assistant"===n.info.role&&n.parts)for(const e of n.parts)if("tool"===e.type&&e.tool&&e.state){const s=e.state.input||{}
|
|
81
|
+
let o=e.tool.replace(/^alchemy_/,"")
|
|
82
|
+
const i=s.path||s.file||s.directory,r=s.needle||s.query||s.pattern
|
|
83
|
+
"string"==typeof i?o+=` ${i.split("/").pop()||i}`:"string"==typeof r&&(o+=` "${r.length>30?r.slice(0,27)+"...":r}"`),t.push({name:o,state:e.state.status,time:n.info.time.created})}t.sort(((e,t)=>t.time-e.time))
|
|
84
|
+
const n=t.find((e=>"pending"===e.state||"running"===e.state)),s=t.filter((e=>"completed"===e.state||"error"===e.state)).slice(0,3),o=t.length>0?t[0].time:null
|
|
85
|
+
return{current:n?.name??null,recent:s.map((e=>e.name)),lastActivityTime:o}}function d(e){return e<1e3?"<1s ago":e<6e4?`${Math.floor(e/1e3)}s ago`:e<36e5?`${Math.floor(e/6e4)}m ago`:`${Math.floor(e/36e5)}h ago`}function u(e,t){const n=e||t,s=Date.now()-n
|
|
86
|
+
if(e&&s<q)return{status:"working",detail:`last action ${d(s)}`,isReady:!1}
|
|
87
|
+
const o=d(s).replace(/ ago$/,"")
|
|
88
|
+
return s>P&&e?{status:"stalled",detail:`no activity for ${o}`,isReady:!0}:e?{status:"idle",detail:`no activity for ${o}`,isReady:!0}:{status:"idle",detail:"no tool activity",isReady:!0}}function p(e){let t=0,n=0,s=0
|
|
89
|
+
for(const o of e.trim().split("\n")){if(!o)continue
|
|
90
|
+
const[e,i]=o.split("\t")
|
|
91
|
+
"-"!==e&&(t+=parseInt(e,10)||0),"-"!==i&&(n+=parseInt(i,10)||0),s++}return{additions:t,deletions:n,files:s}}function g(e,t,n,s){const o=e.get(t)
|
|
92
|
+
return e.set(t,{at:s,fingerprint:n}),o&&o.fingerprint===n?`Nothing changed since your last check at ${new Date(o.at).toISOString().slice(11,19)} UTC (${d(s-o.at)}): same activity, same messages, same tokens.`:null}async function f(e,n,s,o){const i=await(0,$.getServerUrl)(n,s)
|
|
93
|
+
if(!i)throw new w.ValidationError("Could not determine OpenCode server URL")
|
|
94
|
+
s.info("Checking status of subagent session",{sessionId:e})
|
|
95
|
+
try{const r=`${i.replace(/\/$/,"")}/session/${e}`,a=await fetch(r,{signal:AbortSignal.timeout(8e3)})
|
|
96
|
+
if(!a.ok)return{content:[{type:"text",text:`Session ${e} not found or has exited.`}],isError:!0}
|
|
97
|
+
const d=await a.json(),f=`${i.replace(/\/$/,"")}/session/${e}/message`,h=await fetch(f,{signal:AbortSignal.timeout(8e3)})
|
|
98
|
+
let m={input:0,output:0,reasoning:0},w="",y=u(null,d.time.updated),$=-1
|
|
99
|
+
if(h.ok){const e=await h.json()
|
|
100
|
+
$=e.length
|
|
101
|
+
for(const t of e)"assistant"===t.info.role&&t.info.tokens&&(m.input+=t.info.tokens.input||0,m.output+=t.info.tokens.output||0,m.reasoning+=t.info.tokens.reasoning||0)
|
|
102
|
+
const t=c(e)
|
|
103
|
+
y=u(t.lastActivityTime,d.time.updated),t.current&&(w+=`\nCurrent: ${t.current}`),t.recent.length>0&&(w+=`\nRecent: ${t.recent.join(", ")}`)}const S=Date.now()-d.time.created,I=y.status,b=y.isReady,k=m.input+m.output+m.reasoning
|
|
104
|
+
let v="",x="",A=null
|
|
105
|
+
if(o)try{const t=o.getSessionRequestAssociationClient()
|
|
106
|
+
if(t){const n=await t.getActiveRequestsForSession(e,!1)
|
|
107
|
+
if(n.length>0){const t=n.filter((e=>!e.endedAt)).sort(((e,t)=>new Date(t.startedAt).getTime()-new Date(e.startedAt).getTime())),o=n.filter((e=>e.endedAt)).sort(((e,t)=>new Date(t.endedAt).getTime()-new Date(e.endedAt).getTime())),i=o[0],r=t[0]
|
|
108
|
+
i&&r?A=new Date(i.endedAt).getTime()>new Date(r.startedAt).getTime()?i.requestLocalId:r.requestLocalId:i?A=i.requestLocalId:r&&(A=r.requestLocalId)
|
|
109
|
+
const a=[]
|
|
110
|
+
t.length>0&&a.push(`Active: ${t.map((e=>e.requestLocalId)).join(", ")}`),o.length>0&&a.push(`Previous: ${o.map((e=>e.requestLocalId)).join(", ")}`),a.length>0&&(x=`\nRequests: ${a.join(" | ")}`),s.debug("Found request associations",{sessionId:e,activeCount:t.length,historicalCount:o.length})}}}catch(e){s.debug("Failed to get request associations, falling back to title extraction",{error:e instanceof Error?e.message:String(e)})}if(A||(A=function(e){const t=e.match(/\[(req\d{6})\]/)
|
|
111
|
+
return t?t[1]:null}(d.title||"")),A&&n.basePath){const e=await async function(e,n){try{const{execFileSync:s}=await Promise.resolve().then(t.t.bind(t,35317,23)),o=`requests/${e}`
|
|
112
|
+
let i=!1
|
|
113
|
+
try{s("git",["rev-parse","--verify",o],{cwd:n,stdio:"pipe"}),i=!0}catch{i=!1}if(i){const e=s("git",["diff",`main...${o}`,"--numstat"],{cwd:n,encoding:"utf8"})
|
|
114
|
+
if(e.trim())return p(e)}try{const t=s("git",["log","main","--merges","--oneline","-n","50",`--grep=${e}`],{cwd:n,encoding:"utf8"}).trim()
|
|
115
|
+
if(t){const e=t.split("\n")[0].split(" ")[0],o=s("git",["diff",`${e}^1...${e}`,"--numstat"],{cwd:n,encoding:"utf8"})
|
|
116
|
+
if(o.trim())return{...p(o),merged:!0}}}catch{}return null}catch{return null}}(A,n.basePath)
|
|
117
|
+
if(e){const t=e.additions+e.deletions,n=e.merged?" (merged)":""
|
|
118
|
+
v=`\nDiff vs main (${A}): +${e.additions}/-${e.deletions} lines (${t} total) in ${e.files} file${1!==e.files?"s":""}${n}`}}const C=g(R,`${n.currentSessionId??""}>${e}`,`${d.time.updated}:${$}:${k}`,Date.now())
|
|
119
|
+
return{content:[{type:"text",text:`${C?`${C}\n\n`:""}Session ${e}\nTitle: ${d.title||"Untitled"}\nStatus: ${I} (${y.detail})\nReady for input: ${b?"Yes":"No"}\nAge: ${l(S)}\nTokens: ${k.toLocaleString()} (in: ${m.input.toLocaleString()}, out: ${m.output.toLocaleString()}${m.reasoning>0?`, reasoning: ${m.reasoning.toLocaleString()}`:""})${w}${x}${v}`}]}}catch(e){throw new Error(`Failed to check session status: ${(0,k.getErrorMessage)(e)}`)}}async function h(e,t,n){const s=await(0,$.getServerUrl)(t,n)
|
|
120
|
+
if(!s)throw new w.ValidationError("Could not determine OpenCode server URL")
|
|
121
|
+
n.info("Aborting subagent session",{sessionId:e})
|
|
122
|
+
try{const t=`${s.replace(/\/$/,"")}/session/${e}/abort`,o=await fetch(t,{signal:AbortSignal.timeout(8e3),method:"POST"})
|
|
123
|
+
if(!o.ok){const e=await o.text().catch((()=>"Unknown error"))
|
|
124
|
+
throw new Error(`Failed to abort session: ${o.status} ${e}`)}return n.info("Session aborted",{sessionId:e}),{content:[{type:"text",text:`Session ${e} has been aborted.`}]}}catch(e){throw new Error(`Failed to abort session: ${(0,k.getErrorMessage)(e)}`)}}async function m(e,t,n,s={}){const{activeOnly:o=!1,requestId:i,limit:r,minTokens:a,maxTokens:d,maxAgeHours:p}=s,g=await(0,$.getServerUrl)(e,t)
|
|
125
|
+
if(!g)throw new w.ValidationError("Could not determine OpenCode server URL")
|
|
126
|
+
t.info("Listing sessions",{filters:s})
|
|
127
|
+
try{const t=`${g.replace(/\/$/,"")}/session?limit=1000`,f=await fetch(t,{signal:AbortSignal.timeout(8e3)})
|
|
128
|
+
if(!f.ok)throw new Error(`Failed to fetch sessions: ${f.status}`)
|
|
129
|
+
let h=await f.json()
|
|
130
|
+
if(0===h.length)return{content:[{type:"text",text:"No sessions found."}]}
|
|
131
|
+
const m=h
|
|
132
|
+
s.parentSessionId&&(h=h.filter((e=>(e.parentId||e.parentID)===s.parentSessionId))),h.sort(((e,t)=>t.time.created-e.time.created))
|
|
133
|
+
const w=`${g.replace(/\/$/,"")}/session/status`,y=await fetch(w,{signal:AbortSignal.timeout(8e3)}),$=y.ok?await y.json():{}
|
|
134
|
+
let S=h
|
|
135
|
+
if(o&&(S=S.filter((e=>"idle"!==($[e.id]?.type||"idle")))),void 0!==p){const e=60*p*60*1e3,t=Date.now()-e
|
|
136
|
+
S=S.filter((e=>e.time.created>=t))}if(0===S.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
|
|
137
|
+
let I={}
|
|
138
|
+
const b=S.map((e=>e.id))
|
|
139
|
+
let k={},v=[]
|
|
140
|
+
if("specialist"===s.agentType){const e=new Set(b)
|
|
141
|
+
for(const t of m){const n=t.parentId||t.parentID
|
|
142
|
+
n&&e.has(n)&&(k[n]||(k[n]=[]),k[n].push(t.id),v.push(t.id))}}if(n)try{const e=n.getCLISessionClient()
|
|
143
|
+
if(e){const t=[...b,...v]
|
|
144
|
+
I=await e.batchEnrich(t)}}catch{}const x=g.replace(/\/$/,""),A=await Promise.allSettled(S.map((e=>fetch(`${x}/session/${e.id}/message`,{signal:AbortSignal.timeout(8e3)}).then((e=>e.ok?e.json():[])).catch((()=>[]))))),C=[]
|
|
145
|
+
for(let e=0;e<S.length;e++){const t=S[e],n=Date.now()-t.time.created
|
|
146
|
+
let o=0,r=null,l=[],p=u(null,t.time.updated)
|
|
147
|
+
const g=A[e],f="fulfilled"===g.status?g.value:[]
|
|
148
|
+
for(const e of f)"assistant"===e.info.role&&e.info.tokens&&(o+=(e.info.tokens.input||0)+(e.info.tokens.output||0)+(e.info.tokens.reasoning||0))
|
|
149
|
+
if(f.length>0){const e=c(f)
|
|
150
|
+
p=u(e.lastActivityTime,t.time.updated),r=e.current||null,l=e.recent}const h=p.status
|
|
151
|
+
if(void 0!==a&&o<a)continue
|
|
152
|
+
if(void 0!==d&&o>d)continue
|
|
153
|
+
const m=I[t.id]
|
|
154
|
+
if(s.agentType&&(!m||m.agentType!==s.agentType))continue
|
|
155
|
+
if("archived"===m?.status)continue
|
|
156
|
+
let w=""
|
|
157
|
+
const y=[],b=[]
|
|
158
|
+
m?.associations&&b.push(...m.associations)
|
|
159
|
+
const v=k[t.id]
|
|
160
|
+
if(v)for(const e of v){const t=I[e]
|
|
161
|
+
if(t?.associations){const n=$[e]?.type||"idle",s="idle"===n||"stalled"===n
|
|
162
|
+
for(const e of t.associations)s&&!e.endedAt?b.push({...e,endedAt:"idle"}):b.push(e)}}if(b.length>0){const e=new Map
|
|
163
|
+
for(const t of b){const n=e.get(t.requestLocalId)
|
|
164
|
+
void 0!==n&&(n||t.endedAt)||e.set(t.requestLocalId,!t.endedAt)}const t=[...e.entries()].filter((([,e])=>e)).map((([e])=>e)),n=[...e.entries()].filter((([,e])=>!e)).map((([e])=>e))
|
|
165
|
+
y.push(...t,...n)
|
|
166
|
+
const s=[]
|
|
167
|
+
if(t.length>0&&s.push(`active: ${t.join(", ")}`),n.length>0){const e=5,t=n.slice(0,e),o=n.length-t.length
|
|
168
|
+
s.push(`prev: ${t.join(", ")}${o>0?` +${o} more`:""}`)}s.length>0&&(w=` [${s.join(" | ")}]`)}if(i&&!y.includes(i))continue
|
|
169
|
+
const x=t.title||"Untitled",T=x.length>50?x.slice(0,47)+"...":x
|
|
170
|
+
C.push({id:t.id,title:T,status:h,age:n,totalTokens:o,requestsInfo:w,allRequestIds:y,statusDetail:p.detail,specialization:m?.specialization??null,currentAction:r,recentActions:l})}if(0===C.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
|
|
171
|
+
const T=r?C.slice(0,r):C
|
|
172
|
+
let j={}
|
|
173
|
+
if("specialist"===s.agentType&&n&&e.projectId){const t={},s=[]
|
|
174
|
+
for(const e of T)for(const n of e.allRequestIds)t[n]||(t[n]=!0,s.push(n))
|
|
175
|
+
if(s.length>0)try{const t=await n.proxyPost(`/api/projects/${e.projectId}/areas/by-requests`,{requestLocalIds:s}),o=t?.data?.areasByRequest??t?.areasByRequest
|
|
176
|
+
o&&"object"==typeof o&&(j=o)}catch{}}const E=T.map((e=>{const t=[]
|
|
177
|
+
for(const n of e.allRequestIds){const e=j[n]
|
|
178
|
+
if(Array.isArray(e)){for(const n of e){if(t.length>=5)break
|
|
179
|
+
"string"!=typeof n||t.includes(n)||t.push(n)}if(t.length>=5)break}}const n=(e.specialization||"").split(",").map((e=>e.trim())).filter(Boolean),o=n.length>0?n.slice(0,3):t.slice(0,3),i=o.length>0?`\n Areas: ${o.join(", ")}`:"",r=e.currentAction?`\n Current: ${e.currentAction}`:"",a="specialist"!==s.agentType&&e.recentActions.length>0?`\n Recent: ${e.recentActions.join(", ")}`:""
|
|
180
|
+
return`\u2022 ${e.id}\n ${e.title}${i}\n Status: ${e.status} (${e.statusDetail}) | Age: ${l(e.age)} | ${e.totalTokens.toLocaleString()} tokens${e.requestsInfo}${r}${a}`})),q=r&&C.length>r?` (showing ${r} of ${C.length})`:""
|
|
181
|
+
return{content:[{type:"text",text:`Found ${T.length} session${1!==T.length?"s":""}${q}:\n\n${E.join("\n\n")}`}]}}catch(e){throw new Error(`Failed to list sessions: ${(0,k.getErrorMessage)(e)}`)}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleSpawn=r,exports.handleContinue=a,exports.extractRecentActivity=c,exports.formatTimeSince=d,exports.deriveActivityStatus=u,exports.noteStatusCheck=g,exports.handleCheckStatus=f,exports.handleAbort=h,exports.handleListSessions=m,exports.handleSubagentOperations=async function(e,t,n,s,o,i){const l=n.child({tool:"subagent_operations"}),c=s.startTimer("tool_execution_time")
|
|
182
|
+
try{const n=e,{sessionId:d,operation:u}=n
|
|
183
|
+
let p
|
|
184
|
+
switch(l.info("Processing subagent_operations",{operation:u,sessionId:d}),u){case"spawn":p=await r({agent:n.agent,prompt:n.prompt,requestId:n.requestId,allowParallel:n.allowParallel,useExistingSession:n.useExistingSession},t,l,o,i)
|
|
185
|
+
break
|
|
186
|
+
case"continue":p=await a(d,n.prompt,t,l,o,void 0,i)
|
|
187
|
+
break
|
|
188
|
+
case"check-status":p=await f(d,t,l,o)
|
|
189
|
+
break
|
|
190
|
+
case"abort":p=await h(d,t,l)
|
|
191
|
+
break
|
|
192
|
+
case"read-messages":{const e=!(!n.compact&&null==n.select),s=!e&&null==n.limit,o=n.limit??(e?50:8)
|
|
193
|
+
p=await async function(e,t,n,s,o){const i=await(0,$.getServerUrl)(n,s)
|
|
194
|
+
if(!i)throw new w.ValidationError("Could not determine OpenCode server URL")
|
|
195
|
+
s.info("Reading messages from subagent session",{sessionId:e,limit:t,compact:o?.compact,select:o?.select})
|
|
196
|
+
try{const n=`${i.replace(/\/$/,"")}/session/${e}/message?limit=${t}`,s=await fetch(n,{signal:AbortSignal.timeout(8e3)})
|
|
197
|
+
if(!s.ok){if(404===s.status)return{content:[{type:"text",text:`Session ${e} not found.`}],isError:!0}
|
|
198
|
+
throw new Error(`Failed to fetch messages: ${s.status}`)}const r=await s.json()
|
|
199
|
+
if(0===r.length)return{content:[{type:"text",text:`Session ${e} has no messages yet.`}]}
|
|
200
|
+
const a=(e,t)=>e.length>t?e.slice(0,t)+"\u2026[truncated]":e,l=e=>"user"===e.info.role?"USER":"ASSISTANT",c=e=>new Date(e.info.time.created).toLocaleTimeString(),d=e=>`\u{1f527} ${e.tool||"tool"}${"error"===e.state?.status?" \u2717":""}`,u=e=>Array.isArray(e)?e.map((e=>"image"===e?.type?"[image]":"string"==typeof e?.text?e.text:JSON.stringify(e))).join(" "):"string"==typeof e?e:JSON.stringify(e??"")
|
|
201
|
+
if("number"==typeof o?.select){const t=r[o.select-1]
|
|
202
|
+
if(!t)return{content:[{type:"text",text:`No message #${o.select} (session has ${r.length}; use compact to index).`}],isError:!0}
|
|
203
|
+
const n=[`Session ${e}: message ${o.select} of ${r.length}: ${l(t)} (${c(t)}):`,""]
|
|
204
|
+
for(const e of t.parts)if("text"===e.type&&e.text)n.push(`TEXT: ${a(e.text,6e3)}`)
|
|
205
|
+
else if("tool"===e.type){const t=e.state||{}
|
|
206
|
+
n.push(`\u{1f527} ${e.tool||"tool"} [${t.status??"unknown"}]`),n.push(` input: ${a("string"==typeof t.input?t.input:JSON.stringify(t.input??{}),2500)}`)
|
|
207
|
+
const s=u(t.output)
|
|
208
|
+
s&&n.push(` output: ${a(s,1500)}`)}return{content:[{type:"text",text:n.join("\n")}]}}if(o?.compact){const t=r.map(((e,t)=>{const n=e.parts.filter((e=>"tool"===e.type)).map(d),s=e.parts.filter((e=>"text"===e.type&&e.text)).map((e=>e.text)).join(" ").replace(/\s+/g," ").trim().slice(0,140),o=[...n,s?`"${s}${s.length>=140?"\u2026":""}"`:""].filter(Boolean).join(" \xb7 ")
|
|
209
|
+
return`[${t+1}] ${"USER"===l(e)?"USER":"ASST"} (${c(e)}): ${o||"(empty)"}`}))
|
|
210
|
+
return{content:[{type:"text",text:`Session ${e}: ${r.length} messages (compact; use select:N for one in full):\n\n${t.join("\n")}`}]}}const p=(e,t)=>{const n=[`[${t+1}] ${l(e)} (${c(e)}):`]
|
|
211
|
+
for(const t of e.parts)if("text"===t.type&&t.text)n.push(a(t.text,5e3))
|
|
212
|
+
else if("tool"===t.type){const e=t.state||{},s=u(e.output).replace(/\s+/g," ").trim()
|
|
213
|
+
n.push(`\u{1f527} ${t.tool||"tool"} [${e.status??"unknown"}]${s?` \u2192 ${a(s,200)}`:""}`)}return n.length>1?n.join("\n"):`${n[0]}\n(no content)`}
|
|
214
|
+
if(o?.latest){const t=e=>e.parts.some((e=>"text"===e.type&&e.text||"tool"===e.type))
|
|
215
|
+
let n=r.length-1
|
|
216
|
+
for(;n>0&&!t(r[n]);)n--
|
|
217
|
+
const s=r.length-1-n
|
|
218
|
+
return{content:[{type:"text",text:`Session ${e}: latest message${s>0?` (skipped ${s} trailing empty message${1===s?"":"s"}; use compact:true for the full trajectory)`:""}:\n\n${p(r[n],n)}`}]}}const g=r.map(((e,t)=>p(e,t))).join("\n\n---\n\n")
|
|
219
|
+
return{content:[{type:"text",text:`Session ${e}: last ${r.length} messages:\n\n${g}`}]}}catch(e){throw new Error(`Failed to read messages: ${(0,k.getErrorMessage)(e)}`)}}(d,o,t,l,{compact:n.compact,select:n.select,latest:s})
|
|
220
|
+
break}case"list-sessions":const e={activeOnly:n.activeOnly,requestId:n.requestId,limit:n.limit,minTokens:n.minTokens,maxTokens:n.maxTokens,maxAgeHours:n.maxAgeHours,parentSessionId:i?.sessionId}
|
|
221
|
+
p=await m(t,l,o,e)
|
|
222
|
+
break
|
|
223
|
+
default:throw new w.ValidationError(`Unknown operation: ${u}`)}c({success:!0}),s.incrementCounter(`subagent_operations_${u}_success`,1)
|
|
224
|
+
const g=T[u]
|
|
225
|
+
return g?(0,C.withAlchemyKind)(p,g):p}catch(e){return l.error("Subagent operations failed",{error:e instanceof Error?e.stack:String(e)}),c({success:!1}),s.incrementCounter("subagent_operations_errors",1),{content:[{type:"text",text:`Subagent operation failed: ${(0,k.getErrorMessage)(e)}`}],isError:!0}}}
|
|
226
|
+
const w=t(98547),y=t(3928),$=t(16714),S=t(49725),I=t(7451),b=t(87907),k=t(21319),v=t(79896),x=t(16928),A=t(97734),C=t(62263),T={spawn:"spawn-subagent",continue:"continue-subagent","check-status":"check-subagent-status",abort:"abort-subagent","read-messages":"read-subagent-messages","list-sessions":"list-subagent-sessions"},j="true"===process.env.ALCHEMY_DEBUG_LOGS,E=(0,t(53603).getActionServerPort)(),q=6e4,P=3e5,R=new Map},16714:(e,exports,t)=>{Object.defineProperty(exports,"__esModule",{value:!0}),exports.invalidateServerUrlCache=function(){s=null},exports.getServerUrl=async function(e,o){const i=process.env.OPENCODE_TUI_URL
|
|
227
|
+
if(s&&s.basePath===e.basePath&&s.envUrl===i)return s.result
|
|
228
|
+
let r=i
|
|
229
|
+
if(!r)try{const{readFileSync:n}=await Promise.resolve().then(t.t.bind(t,79896,23)),{join:s}=await Promise.resolve().then(t.t.bind(t,16928,23)),i=n(s(e.basePath,".alchemy_lab","opencode.json"),"utf8"),a=JSON.parse(i)
|
|
230
|
+
a.port&&(r=`http://127.0.0.1:${a.port}`,o.debug("Discovered OpenCode server URL from .alchemy_lab/opencode.json",{serverUrl:r}))}catch(e){o.debug("Could not read .alchemy_lab/opencode.json",{error:e instanceof Error?e.message:String(e)})}if(r)try{(0,n.noteServe)(Number(new URL(r).port),(0,n.currentServePassword)(e.basePath))}catch{}return s={basePath:e.basePath,envUrl:i,result:r},r}
|
|
231
|
+
const n=t(7245)
|
|
232
|
+
let s=null},24019:(e,exports,t)=>{exports.formatLiveWorker=function(e){const t=e.agentType?e.agentType:"worker",n=[],s=!0===e.busy?"mid-turn":!1===e.busy?"between turns":"unknown"
|
|
233
|
+
return n.push(`- ${t} (${e.sessionId}): ${e.status}, ${e.detail}; ${s}`),e.current&&n.push(` now: ${e.current}`),e.recent.length>0&&n.push(` recently: ${e.recent.join(", ")}`),e.lastSaid&&n.push(` last said${e.lastSaidAgo?` (${e.lastSaidAgo})`:""}: "${e.lastSaid}"`),e.lastRefusal&&n.push(` last refused (its tool said): ${e.lastRefusal}`),n.join("\n")},exports.readLiveWorker=async function(e,t){if(!e)return null
|
|
234
|
+
const i=t.filter((e=>(0,s.isWorkerAgentType)(e.agentType??""))),r=(i.length>0?i:t).slice(-1)[0]
|
|
235
|
+
if(!r)return null
|
|
236
|
+
const a=e.replace(/\/$/,"")
|
|
237
|
+
try{const[e,t,s]=await Promise.all([fetch(`${a}/session/${r.sessionId}`,{signal:AbortSignal.timeout(5e3)}),fetch(`${a}/session/${r.sessionId}/message?limit=60`,{signal:AbortSignal.timeout(8e3)}),fetch(`${a}/session/status`,{signal:AbortSignal.timeout(5e3)}).catch((()=>null))])
|
|
238
|
+
if(!e.ok||!t.ok)return null
|
|
239
|
+
const i=await e.json(),l=await t.json()
|
|
240
|
+
let c=null
|
|
241
|
+
if(s&&s.ok)try{const e=await s.json(),t=e?.[r.sessionId]
|
|
242
|
+
c=void 0!==t&&"busy"===("string"==typeof t?t:t?.type)}catch{}const d=(0,n.extractRecentActivity)(l),u=(0,n.deriveActivityStatus)(d.lastActivityTime,i.time?.updated??Date.now())
|
|
243
|
+
let p=null,g=null
|
|
244
|
+
for(let e=l.length-1;e>=0;e--){const t=l[e]
|
|
245
|
+
if("assistant"!==t.info.role)continue
|
|
246
|
+
const s=(t.parts??[]).filter((e=>"text"===e.type&&e.text)).map((e=>e.text)).join(" ").replace(/\s+/g," ").trim()
|
|
247
|
+
if(s){p=s.length>220?s.slice(0,217)+"...":s,g=(0,n.formatTimeSince)(Date.now()-(t.info.time.completed??t.info.time.created))
|
|
248
|
+
break}}const f=function(e){for(let t=e.length-1;t>=0;t--){const n=e[t]
|
|
249
|
+
if("user"===n.info.role)return null
|
|
250
|
+
const s=n.parts??[]
|
|
251
|
+
for(let e=s.length-1;e>=0;e--){const t=s[e]
|
|
252
|
+
if("tool"!==t.type)continue
|
|
253
|
+
const n="error"===t.state?.status?String(t.state?.error??""):"string"==typeof t.state?.output?t.state.output:""
|
|
254
|
+
if("error"===t.state?.status||o.test(n)){const e=String(t.tool??"tool").replace(/^alchemy_/,""),s=n.replace(/\s+/g," ").trim()
|
|
255
|
+
return`${e}: ${s.length>240?s.slice(0,237)+"...":s}`}}}return null}(l)
|
|
256
|
+
return{sessionId:r.sessionId,agentType:r.agentType,busy:c,status:u.status,detail:u.detail,current:d.current,recent:d.recent,lastSaid:p,lastSaidAgo:g,lastRefusal:f}}catch{return null}}
|
|
257
|
+
const n=t(16554),s=t(47221),o=/^\s*(?:Error:|\u26d4|\u274c|Not filed|Not started|Not kept|Cannot |STOP:)/},49725:(e,exports,t)=>{Object.defineProperty(exports,"__esModule",{value:!0}),exports.deliverToSession=async function(e,t,s={}){const o=s.noVerify?0:s.verifySeconds??20
|
|
258
|
+
try{const i=await fetch(`${(0,n.getActionServerBaseUrl)()}/api/sessions/${encodeURIComponent(e)}/deliver`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:t,kind:s.kind,partPayload:s.partPayload,noVerify:s.noVerify,verifySeconds:s.verifySeconds}),signal:AbortSignal.timeout(1e3*(o+25))}),r=await i.json().catch((()=>null))
|
|
259
|
+
return i.ok?r?.data??{ok:!1,error:"deliver_route",detail:"empty response"}:{ok:!1,error:"deliver_route",detail:r?.error||`HTTP ${i.status}`}}catch(e){return{ok:!1,error:"deliver_route",detail:e instanceof Error?e.message:String(e)}}},exports.deliveryFailureText=function(e,t){return`\u274c NOT delivered to session ${e}: ${t.detail||t.error||"unknown reason"}. The session did not receive this and nothing will act on it.`}
|
|
260
|
+
const n=t(53603)}}
|
package/dist/{800.js → 237.js}
RENAMED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
"use strict"
|
|
2
|
-
exports.id=
|
|
2
|
+
exports.id=237,exports.ids=[237],exports.modules={80237:function(e,exports,t){var r,o=this&&this.__createBinding||(Object.create?function(e,t,r,o){void 0===o&&(o=r)
|
|
3
3
|
var i=Object.getOwnPropertyDescriptor(t,r)
|
|
4
|
-
i&&!("get"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:()
|
|
5
|
-
for(var r in e)
|
|
4
|
+
i&&!("get"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,o,i)}:function(e,t,r,o){void 0===o&&(o=r),e[o]=t[r]}),i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),c=this&&this.__importStar||(r=function(e){return r=Object.getOwnPropertyNames||function(e){var t=[]
|
|
5
|
+
for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
|
|
6
6
|
return t},r(e)},function(e){if(e&&e.__esModule)return e
|
|
7
7
|
var t={}
|
|
8
8
|
if(null!=e)for(var c=r(e),s=0;s<c.length;s++)"default"!==c[s]&&o(t,e,c[s])
|
|
9
9
|
return i(t,e),t})
|
|
10
10
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.SimpleProjectCreationLock=void 0
|
|
11
|
-
const s=c(t(
|
|
11
|
+
const s=c(t(91943)),a=c(t(16928)),n=t(21992)
|
|
12
12
|
exports.SimpleProjectCreationLock=class{constructor(e,t){this.maxWaitMs=3e4,this.checkIntervalMs=500,this.lockFilePath=a.join(e,".alchemy-project-creation.lock"),this.projectFilePath=a.join(e,n.ALCHEMY_DIR_NAME,"project.json"),this.logger=t.child({component:"simple-project-creation-lock"})}async acquireLock(){this.logger.info("Attempting to acquire project creation lock",{processId:process.pid,lockFile:this.lockFilePath})
|
|
13
13
|
const e=await this.loadExistingProject()
|
|
14
14
|
if(e)return this.logger.info("Project already exists, no creation needed",{projectId:e.id}),{acquired:!1,shouldCreate:!1,projectData:e}
|
|
15
15
|
try{const e={processId:process.pid,timestamp:Date.now(),lockFile:this.lockFilePath}
|
|
16
16
|
return await s.writeFile(this.lockFilePath,JSON.stringify(e,null,2),{flag:"wx"}),this.logger.info("Successfully acquired project creation lock",{processId:process.pid,lockFile:this.lockFilePath}),{acquired:!0,shouldCreate:!0}}catch(e){return"EEXIST"===e.code?(this.logger.info("Lock already held by another process, will wait",{processId:process.pid}),await this.waitForProjectCreation()):(this.logger.error("Error acquiring project creation lock",{error:e.message}),{acquired:!0,shouldCreate:!0})}}async releaseLock(){try{await s.unlink(this.lockFilePath),this.logger.info("Released project creation lock",{processId:process.pid})}catch(e){"ENOENT"!==e.code&&this.logger.warn("Failed to release project creation lock",{error:e.message})}}async waitForProjectCreation(){const e=Math.floor(this.maxWaitMs/this.checkIntervalMs)
|
|
17
17
|
this.logger.info("Waiting for project creation by another process",{maxAttempts:e,processId:process.pid})
|
|
18
|
-
for(let t=1;e
|
|
18
|
+
for(let t=1;t<=e;t++){await this.sleep(this.checkIntervalMs)
|
|
19
19
|
const r=await this.loadExistingProject()
|
|
20
20
|
if(r)return this.logger.info("Project creation completed by another process",{projectId:r.id,attempt:t,processId:process.pid}),{acquired:!1,shouldCreate:!1,projectData:r}
|
|
21
21
|
if(!await this.lockFileExists()&&(this.logger.warn("Lock disappeared but no project found, creation may have failed",{attempt:t,processId:process.pid}),t>e/2))return this.logger.info("Attempting to acquire lock after apparent creation failure"),{acquired:!0,shouldCreate:!0}}return this.logger.error("Timeout waiting for project creation",{maxWaitMs:this.maxWaitMs,processId:process.pid}),{acquired:!0,shouldCreate:!0}}async loadExistingProject(){try{const e=await s.readFile(this.projectFilePath,"utf8"),t=JSON.parse(e)
|
package/dist/301.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
exports.id=301,exports.ids=[301],exports.modules={27301:function(t,exports,e){var r=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}}
|
|
3
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.InteractiveShell=void 0
|
|
4
|
+
const s=r(e(23785)),a=e(35317),i=e(83807),o=r(e(16928)),n=r(e(79896)),l=r(e(96895)),c=e(21992),u=e(56997),h=e(47499),d=e(53189),f=e(53603)
|
|
5
|
+
exports.InteractiveShell=class{constructor(t){if(this.currentBranch="main",this.currentStatus=null,this.history=[],this.statusCache={map:new Map,ts:0},this.pathExecutableCache=null,this.projectPath=t.projectPath,this.currentSymlink=o.default.join(t.projectPath,c.ALCHEMY_DIR_NAME,"current"),this.currentDir=t.projectPath,this.logger=t.logger,this.historyFile=o.default.join(t.projectPath,c.ALCHEMY_DIR_NAME,"shell-history.txt"),this.projectId=t.projectId,this.remoteServerUrl=t.remoteServerUrl,this.authToken=t.authToken,t.remoteServerUrl){const e=(0,h.createMetricsCollector)(!1)
|
|
6
|
+
this.apiClient=new u.BaseApiClient(t.remoteServerUrl,t.logger,e,t.authToken)}}async publishPr(t,e){if(!t||!/^req\d{6}$/.test(t))return void console.log(l.default.red("Usage: publish req###### [base-branch]"))
|
|
7
|
+
if(!this.projectId||!this.remoteServerUrl)return void console.log(l.default.red("Project is not registered with the remote yet."))
|
|
8
|
+
const r=this.projectId,s={"X-Client-Type":"alchemy-local-service"}
|
|
9
|
+
this.authToken&&(s.Authorization=`Bearer ${this.authToken}`)
|
|
10
|
+
try{console.log(l.default.gray(`Publishing ${t} as a GitHub PR (uses your local gh auth; no token is stored)...`))
|
|
11
|
+
const a=await(0,d.publishRequestPr)({projectPath:this.projectPath,requestLocalId:t,base:e},{run:(0,d.defaultRunner)(),fetchRequest:async t=>{const e=await(this.apiClient?.get(`/api/projects/${r}/requests/${t}`))
|
|
12
|
+
return e?.data??e??null},fetchReceiptMarkdown:async t=>{const e=await fetch(`${this.remoteServerUrl}/api/projects/${r}/requests/${t}/receipt?format=md`,{headers:s})
|
|
13
|
+
if(!e.ok)throw new Error(`Receipt fetch failed: HTTP ${e.status}`)
|
|
14
|
+
return await e.text()}})
|
|
15
|
+
console.log(l.default.green(`${a.created?"PR created":"Receipt added to the existing PR"}: ${a.prUrl}`)),a.statusPosted&&console.log(l.default.gray("Commit status alchemylab/gates set on the head commit."))}catch(t){console.log(l.default.red(t instanceof Error?t.message:String(t)))}}async linearImport(t){if(t&&/^[A-Za-z][A-Za-z0-9]{1,9}-\d{1,6}$/.test(t))try{const e=(0,f.getActionServerBaseUrl)(),r=await fetch(`${e}/api/connections/linear/import`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({issueKey:t.toUpperCase()})}),s=await r.json()
|
|
16
|
+
s.success&&s.data?.requestId?console.log(l.default.green(`Imported ${s.data.issue} as ${s.data.requestId}.`)):console.log(l.default.red(s.error||"Import failed."))}catch(t){console.log(l.default.red(t instanceof Error?t.message:String(t)))}else console.log(l.default.red("Usage: linear <issue key> e.g. linear ENG-123"))}async start(t){return this.shutdownCallback=t,this.currentBranch="main",this.updateCurrentSymlink(),this.loadHistory(),this.rl=s.default.createInterface({input:process.stdin,output:process.stdout,prompt:this.buildPrompt(),historySize:500,removeHistoryDuplicates:!0,completer:t=>this.completer(t)}),this.history.length>0&&(this.rl.history=[...this.history].reverse()),this.printHelp(!0),this.rl.prompt(),new Promise((t=>{this.rl.on("line",(async e=>{const r=e.trim()
|
|
17
|
+
if(r){if(this.appendHistory(r),await this.processCommand(r))return this.rl.close(),void t()
|
|
18
|
+
this.rl.prompt()}else this.rl.prompt()}))
|
|
19
|
+
let e,r=!1
|
|
20
|
+
this.rl.on("SIGINT",(()=>{r?(e&&clearTimeout(e),this.rl.close()):(r=!0,console.log(""),console.log(l.default.gray("Press Ctrl+C again to exit, or continue typing.")),this.rl.prompt(),e=setTimeout((()=>{r=!1}),2e3))})),this.rl.on("close",(async()=>{this.shutdownCallback&&await this.shutdownCallback(),t()}))}))}buildPrompt(){const t=l.default.green(this.currentBranch),e=this.currentStatus&&"main"!==this.currentBranch?` ${l.default.gray("|")} ${this.colorStatus(this.currentStatus)}`:""
|
|
21
|
+
return`${l.default.cyan("[")}${t}${e}${l.default.cyan("]")} ${l.default.yellow("alchemy")}${l.default.gray(">")} `}colorStatus(t){switch(t){case"completed":return l.default.green(t)
|
|
22
|
+
case"in_progress":return l.default.yellow(t)
|
|
23
|
+
case"open":return l.default.blue(t)
|
|
24
|
+
case"ready_for_review":return l.default.magenta("review")
|
|
25
|
+
case"canceled":default:return l.default.gray(t)
|
|
26
|
+
case"reverted":return l.default.red(t)}}async fetchRequestStatus(t){if(!this.apiClient||!this.projectId)return null
|
|
27
|
+
try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/requests/${t}`),r=e?.data||e
|
|
28
|
+
return r?.status||null}catch{return null}}async fetchBulkStatuses(){if(Date.now()-this.statusCache.ts<1e4&&this.statusCache.map.size>0)return this.statusCache.map
|
|
29
|
+
if(!this.apiClient||!this.projectId)return new Map
|
|
30
|
+
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/requests?limit=100&sortBy=updated&sortDirection=desc`),e=t?.data||t||[],r=new Map
|
|
31
|
+
for(const t of Array.isArray(e)?e:[])t.localId&&t.status&&r.set(t.localId,t.status)
|
|
32
|
+
return this.statusCache={map:r,ts:Date.now()},r}catch{return this.statusCache.map}}formatRequestWithStatus(t,e,r){return`${r?l.default.green(t):t}${e?` ${this.colorStatus(e)}`:""}`}updatePrompt(){this.rl.setPrompt(this.buildPrompt())}getPathExecutables(){if(this.pathExecutableCache)return this.pathExecutableCache
|
|
33
|
+
const t=new Set,e=(process.env.PATH||"").split(o.default.delimiter)
|
|
34
|
+
for(const r of e)try{if(!n.default.existsSync(r))continue
|
|
35
|
+
for(const e of n.default.readdirSync(r))t.add(e)}catch{}return this.pathExecutableCache=Array.from(t).sort(),this.pathExecutableCache}completer(t){const e=["br","branch","logs","pwd","cd ","publish ","linear ","help","exit","quit"],r=t.includes(" "),s=t.indexOf(" "),a=r?t.slice(0,s).toLowerCase():""
|
|
36
|
+
if(r&&("br"===a||"branch"===a))return setImmediate((()=>this.rl.write("\n"))),[[],""]
|
|
37
|
+
if(!r){const r=e.filter((e=>e.startsWith(t))),s=t?this.getPathExecutables().filter((e=>e.startsWith(t))):[],a=[...new Set([...r,...s])]
|
|
38
|
+
if(a.length>0)return[a,t]
|
|
39
|
+
if(!t)return[e,t]}const i=t.lastIndexOf(" "),l=i>=0?t.slice(i+1):t,c=i>=0?t.slice(0,i+1):""
|
|
40
|
+
try{const e=l.includes(o.default.sep)||l.includes("/"),r=e?o.default.resolve(this.currentDir,o.default.dirname(l)):this.currentDir,s=e?o.default.basename(l):l,a=e?o.default.dirname(l)+"/":""
|
|
41
|
+
if(!n.default.existsSync(r))return[[],t]
|
|
42
|
+
const i=n.default.readdirSync(r,{withFileTypes:!0}).filter((t=>t.name.startsWith(s)&&!t.name.startsWith("."))).map((t=>{const e=t.isDirectory()?t.name+"/":t.name
|
|
43
|
+
return c+a+e}))
|
|
44
|
+
if(i.length>0)return[i,t]}catch{}return[[],t]}async detectCurrentBranch(){try{const t=(0,i.simpleGit)(this.currentDir),e=(await t.branch()).current||"main"
|
|
45
|
+
this.currentBranch=this.extractRequestId(e)||e}catch{this.currentBranch="main"}}async processCommand(t){const e=t.split(/\s+/),r=e[0].toLowerCase(),s=e.slice(1)
|
|
46
|
+
switch(r){case"exit":case"quit":return this.shutdownCallback&&await this.shutdownCallback(),!0
|
|
47
|
+
case"help":return this.printHelp(!1),!1
|
|
48
|
+
case"br":case"branch":return await this.listBranches(s.length>0?s.join(" "):void 0),!1
|
|
49
|
+
case"logs":return await this.tailLogs(s),!1
|
|
50
|
+
case"pwd":return console.log(this.getVirtualCwd()),!1
|
|
51
|
+
case"cd":return this.changeDirectory(s[0]||"~"),!1
|
|
52
|
+
case"publish":return await this.publishPr(s[0],s[1]),!1
|
|
53
|
+
case"linear":return await this.linearImport(s[0]),!1
|
|
54
|
+
default:return this.executeCommand(t),!1}}changeDirectory(t){try{let e
|
|
55
|
+
if(e="~"===t||""===t?this.currentSymlink:o.default.isAbsolute(t)?t:o.default.resolve(this.currentDir,t),!n.default.existsSync(e)||!n.default.statSync(e).isDirectory())return void console.log(l.default.red(`cd: no such directory: ${t}`))
|
|
56
|
+
this.currentDir=e}catch(t){console.log(l.default.red(`cd: ${t.message}`))}}async tailLogs(t){const r=o.default.join(this.projectPath,c.ALCHEMY_DIR_NAME,"server.log")
|
|
57
|
+
if(!n.default.existsSync(r))return void console.log(l.default.gray("No log file found yet. Logs will appear once the server processes requests."))
|
|
58
|
+
const s=t[0]&&/^\d+$/.test(t[0])?parseInt(t[0],10):50
|
|
59
|
+
console.log(l.default.gray(`Tailing ${r} (last ${s} lines, Ctrl+C to stop)`))
|
|
60
|
+
const{spawn:a}=await Promise.resolve().then(e.t.bind(e,35317,23)),i=a("tail",["-n",String(s),"-f",r],{stdio:["ignore","inherit","inherit"]})
|
|
61
|
+
return new Promise((t=>{const e=process.stdin,r=e.isRaw
|
|
62
|
+
this.rl.pause(),e.setRawMode(!0),e.resume()
|
|
63
|
+
const s=()=>{e.removeListener("data",a)
|
|
64
|
+
try{i.kill("SIGTERM")}catch{}try{e.setRawMode(r??!1)}catch{}this.rl.resume()},a=t=>{3!==t[0]&&"q"!==t.toString()||i.kill("SIGTERM")}
|
|
65
|
+
e.on("data",a),i.on("close",(()=>{s(),t()}))}))}updateCurrentSymlink(){try{let t
|
|
66
|
+
if("main"===this.currentBranch)t=this.projectPath
|
|
67
|
+
else{const e=o.default.join(this.projectPath,c.ALCHEMY_DIR_NAME,"worktrees",this.currentBranch)
|
|
68
|
+
t=n.default.existsSync(e)?e:this.projectPath}try{n.default.unlinkSync(this.currentSymlink)}catch{}n.default.symlinkSync(t,this.currentSymlink),this.currentDir=this.currentSymlink}catch(t){this.logger.warn("Failed to update current symlink",{error:t.message})}}getVirtualCwd(){return this.currentDir}executeCommand(t){try{(0,a.execSync)(t,{cwd:this.currentDir,stdio:"inherit",env:{...process.env,PWD:this.currentDir,GIT_DIR:void 0,GIT_WORK_TREE:void 0}})}catch(t){void 0!==t.status&&null!==t.status||console.log(l.default.red(`Error: ${t.message}`))}}getWorktreeRequestIds(){const t=o.default.join(this.projectPath,c.ALCHEMY_DIR_NAME,"worktrees"),e=new Set
|
|
69
|
+
if(n.default.existsSync(t))for(const r of n.default.readdirSync(t,{withFileTypes:!0}))r.isDirectory()&&e.add(r.name)
|
|
70
|
+
return e}extractRequestId(t){const e=t.match(/^(?:requests\/)?(req\d+)/)
|
|
71
|
+
return e?e[1]:null}async listBranches(t){try{const e=(0,i.simpleGit)(this.projectPath),r=await e.branch(),s=this.getWorktreeRequestIds(),a=await this.fetchBulkStatuses(),o=new Set,n=[],c=[]
|
|
72
|
+
for(const t of r.all){if(t.startsWith("remotes/"))continue
|
|
73
|
+
if("main"===t)continue
|
|
74
|
+
const e=this.extractRequestId(t)
|
|
75
|
+
e&&!o.has(e)&&(o.add(e),n.push(e))}for(const t of s)o.has(t)||(o.add(t),n.push(t))
|
|
76
|
+
n.sort(((t,e)=>t.localeCompare(e)))
|
|
77
|
+
for(const t of n){const e=this.currentBranch===t||this.currentBranch===`requests/${t}`
|
|
78
|
+
c.push(this.formatRequestWithStatus(t,a.get(t),e))}if(n.push("main"),c.push("main"===this.currentBranch?l.default.green("main"):"main"),0===n.length)return void console.log(l.default.gray("No branches found."))
|
|
79
|
+
if(t&&n.includes(t))return void(t!==this.currentBranch&&await this.switchBranch(t))
|
|
80
|
+
let u=n.indexOf(this.currentBranch)
|
|
81
|
+
u<0&&(u=n.length-1)
|
|
82
|
+
const h=await this.selectFromList(n,"Select branch:",c,t?0:u,!0,t)
|
|
83
|
+
this.rl.pause(),h&&h!==this.currentBranch?await this.switchBranch(h):h||console.log(l.default.gray("Cancelled.")),this.rl.resume()}catch(t){console.log(l.default.red(`Failed to list branches: ${t.message}`))}}async ensureWorktree(t,e){const r=o.default.join(this.projectPath,c.ALCHEMY_DIR_NAME,"worktrees"),s=o.default.join(r,t)
|
|
84
|
+
if(n.default.existsSync(s)&&n.default.statSync(s).isDirectory())return s
|
|
85
|
+
console.log(l.default.gray(`Creating worktree for ${t}...`)),n.default.mkdirSync(r,{recursive:!0})
|
|
86
|
+
const a=(0,i.simpleGit)(this.projectPath)
|
|
87
|
+
return await a.raw(["worktree","add",s,e]),console.log(l.default.gray(`Worktree created at ${s}`)),s}async selectFromList(t,e,r,s,a,i){const o=t,n=r||t
|
|
88
|
+
return new Promise((t=>{let r=0
|
|
89
|
+
const c=process.stdin,u=c.isRaw
|
|
90
|
+
let h=!1,d=i||"",f=o.map(((t,e)=>e))
|
|
91
|
+
null!=s&&s>=0&&s<o.length&&(r=s)
|
|
92
|
+
const p=()=>{if(!d)return void(f=o.map(((t,e)=>e)))
|
|
93
|
+
const t=d.toLowerCase()
|
|
94
|
+
let e=o.map(((t,e)=>({item:t,i:e}))).filter((({item:e})=>e.toLowerCase().startsWith(t)))
|
|
95
|
+
0===e.length&&/^\d+$/.test(t)&&(e=o.map(((t,e)=>({item:t,i:e}))).filter((({item:e})=>e.replace(/\D/g,"").endsWith(t)))),0===e.length&&(e=o.map(((t,e)=>({item:t,i:e}))).filter((({item:e})=>e.toLowerCase().includes(t)))),f=e.map((t=>t.i))},g=process.stdout.rows||24,y=Math.max(3,g-4)
|
|
96
|
+
let m=0,w=0,$=0
|
|
97
|
+
this.rl.pause()
|
|
98
|
+
const S=()=>{const t=f.length,s=t>y
|
|
99
|
+
s?r<m?m=r:r>=m+y&&(m=r-y+1):m=0
|
|
100
|
+
const i=(()=>{const t=f.length
|
|
101
|
+
if(0===t)return 1
|
|
102
|
+
const e=t>y
|
|
103
|
+
let r=e?Math.min(y,t-m):t
|
|
104
|
+
return e&&m>0&&r++,e&&m+y<t&&r++,r})(),o=i+1
|
|
105
|
+
C&&$>1&&process.stdout.write(`\r\x1b[${$-1}A\r`),o>w&&(w=o),$=w
|
|
106
|
+
const c=w-o
|
|
107
|
+
for(let t=0;t<c;t++)process.stdout.write("\x1b[2K\n")
|
|
108
|
+
if(0===t)process.stdout.write(`\x1b[2K ${l.default.gray("no matches")}\n`)
|
|
109
|
+
else{const e=s&&m>0,a=s&&m+y<t,i=s?Math.min(m+y,t):t,o=s?m:0
|
|
110
|
+
e&&process.stdout.write(`\x1b[2K ${l.default.gray(`\u25b2 ${m} more`)}\n`)
|
|
111
|
+
for(let t=o;t<i;t++){const e=f[t],s=t===r?l.default.cyan("\u276f "):" ",a=t===r?l.default.cyan(n[e]):n[e]
|
|
112
|
+
process.stdout.write(`\x1b[2K${s}${a}\n`)}a&&process.stdout.write(`\x1b[2K ${l.default.gray(`\u25bc ${t-i} more`)}\n`)}a&&d?process.stdout.write(`\x1b[2K${l.default.gray(">")} ${l.default.yellow(d)}${l.default.gray("_")}`):a?process.stdout.write(`\x1b[2K${l.default.gray(">")} ${l.default.gray("type to filter...")}`):process.stdout.write(`\x1b[2K${l.default.gray(e)}`),C=!0}
|
|
113
|
+
d&&(p(),r=0)
|
|
114
|
+
let C=!1
|
|
115
|
+
S(),c.setRawMode(!0),c.resume()
|
|
116
|
+
const b=()=>{if(!h){h=!0,c.removeListener("data",v)
|
|
117
|
+
try{c.setRawMode(u??!1)}catch{}C&&$>1&&process.stdout.write(`\r\x1b[${$-1}A\r`),process.stdout.write("\x1b[J"),this.rl.resume()}},v=e=>{if(!h)try{const s=e.toString()
|
|
118
|
+
"\x1b[A"===s?f.length>0&&(r=Math.max(0,r-1),S()):"\x1b[B"===s?f.length>0&&(r=Math.min(f.length-1,r+1),S()):"\r"===s||"\n"===s?f.length>0&&(b(),t(o[f[r]])):"\x1b"===s||"\x03"===s?(b(),t(null)):a&&"\x7f"===s?d.length>0&&(d=d.slice(0,-1),p(),r=0,m=0,S()):a&&1===s.length&&s>=" "&&s<="~"&&(d+=s,p(),r=0,m=0,S())}catch{b(),t(null)}}
|
|
119
|
+
c.on("data",v)}))}findMatchingBranches(t,e,r){const s=new Set
|
|
120
|
+
for(const t of e){const e=this.extractRequestId(t)
|
|
121
|
+
e&&s.add(e)}for(const t of r)s.add(t)
|
|
122
|
+
const a=Array.from(s)
|
|
123
|
+
if(s.has(t))return[t]
|
|
124
|
+
const i=a.filter((e=>e.startsWith(t)))
|
|
125
|
+
if(i.length>0)return i.sort()
|
|
126
|
+
if(/^\d+$/.test(t)){const e=a.filter((e=>e.replace(/\D/g,"").endsWith(t)))
|
|
127
|
+
if(e.length>0)return e.sort()}return[]}async switchBranch(t){try{if("main"===t)return this.currentBranch="main",this.currentStatus=null,this.updateCurrentSymlink(),this.updatePrompt(),console.log(l.default.green("Switched to main")),void console.log(l.default.gray(` ${this.currentDir}`))
|
|
128
|
+
const e=(0,i.simpleGit)(this.projectPath),r=await e.branch(),s=this.getWorktreeRequestIds(),a=this.findMatchingBranches(t,r.all,s)
|
|
129
|
+
if(0===a.length)return console.log(l.default.red(`No branch matching: ${t}`)),void console.log(l.default.gray(' Use "branches" to see available branches'))
|
|
130
|
+
let o
|
|
131
|
+
if(1===a.length)o=a[0]
|
|
132
|
+
else{const e=await this.fetchBulkStatuses(),r=a.map((t=>this.formatRequestWithStatus(t,e.get(t),!1))),s=await this.selectFromList(a,`Multiple matches for "${t}" \u2014 select one:`,r,void 0,!0)
|
|
133
|
+
if(!s)return void console.log(l.default.gray("Cancelled."))
|
|
134
|
+
o=s}const n=r.all.includes(`requests/${o}`)?`requests/${o}`:o
|
|
135
|
+
await this.ensureWorktree(o,n),this.currentBranch=o,this.updateCurrentSymlink(),this.currentStatus=await this.fetchRequestStatus(o),this.updatePrompt()
|
|
136
|
+
const c=this.currentStatus?` ${l.default.gray("|")} ${this.colorStatus(this.currentStatus)}`:""
|
|
137
|
+
console.log(l.default.green(`Switched to ${o}`)+c),console.log(l.default.gray(` ${this.getVirtualCwd()}`))}catch(t){console.log(l.default.red(`Failed to switch: ${t.message}`))}}printHelp(t){t?(console.log(""),console.log(l.default.gray('Type "help" for available commands. Commands run in the current branch directory.')),console.log("")):(console.log(""),console.log(l.default.bold("Interactive Shell Commands:")),console.log(` ${l.default.yellow("branch")} / ${l.default.yellow("br")} Browse and switch branches`),console.log(` ${l.default.yellow("logs")} ${l.default.gray("[N]")} Tail action server log (Ctrl+C to stop, N = lines of history)`),console.log(` ${l.default.yellow("cd")} ${l.default.gray("<dir>")} Change directory within current branch`),console.log(` ${l.default.yellow("pwd")} Show current working directory`),console.log(` ${l.default.yellow("publish")} ${l.default.gray("<req id>")} Push the request branch and open a GitHub PR with its receipt`),console.log(` ${l.default.yellow("linear")} ${l.default.gray("<ENG-123>")} Import a Linear issue as a request`),console.log(` ${l.default.yellow("help")} Show this help`),console.log(` ${l.default.yellow("exit")} / ${l.default.yellow("quit")} Stop the server and exit`),console.log(""),console.log(l.default.gray(" Any other input is executed as a shell command in the current directory.")),console.log(""))}loadHistory(){try{if(n.default.existsSync(this.historyFile)){const t=n.default.readFileSync(this.historyFile,"utf-8")
|
|
138
|
+
this.history=t.split("\n").filter((t=>""!==t.trim())),this.history.length>500&&(this.history=this.history.slice(-500))}}catch{}}appendHistory(t){try{this.history.push(t),n.default.appendFileSync(this.historyFile,t+"\n")}catch{}}}}}
|
package/dist/380.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
exports.id=380,exports.ids=[380],exports.modules={94380:function(e,exports,t){function r(e){return i.default.createHash("md5").update(e).digest("hex")}async function s(e){try{return r(await o.default.promises.readFile(e))}catch{return null}}var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
3
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.syncPluginsGlobal=async function(e,t,n){const i={synced:[],skipped:[],errors:[]},l=n||function(){const e=process.argv[1]
|
|
4
|
+
let t=e
|
|
5
|
+
try{t=o.default.realpathSync(e)}catch{}return a.default.dirname(t)}(),d=a.default.join((0,u.getGlobalConfigDir)(e),"plugin")
|
|
6
|
+
try{await o.default.promises.mkdir(d,{recursive:!0})}catch(e){return t.error("Failed to create global plugin directory",{destDir:d,error:e instanceof Error?e.message:"Unknown error"}),i}const f=a.default.join(d,"package.json"),p='{"type":"module"}\n'
|
|
7
|
+
try{await o.default.promises.readFile(f,"utf8").catch((()=>null))!==p&&await o.default.promises.writeFile(f,p,"utf8")}catch(e){t.warn("Failed to write global plugin package.json",{error:e instanceof Error?e.message:"Unknown error"})}for(const e of c){const n=a.default.join(l,e.sourceName),u=a.default.join(d,e.destName)
|
|
8
|
+
try{if(!o.default.existsSync(n)){t.warn(`Plugin source not found: ${e.sourceName}`,{sourcePath:n}),i.errors.push(e.destName)
|
|
9
|
+
continue}const a=await o.default.promises.readFile(n,"utf8").catch((()=>null))
|
|
10
|
+
if(null===a){t.warn(`Could not read plugin source: ${e.sourceName}`),i.errors.push(e.destName)
|
|
11
|
+
continue}const c=r(a),l=await s(u)
|
|
12
|
+
null===l||c!==l?(await o.default.promises.writeFile(u,a,"utf8"),t.info(`Synced plugin: ${e.destName}`,{destDir:d,reason:null===l?"not installed":"updated"}),i.synced.push(e.destName)):i.skipped.push(e.destName)}catch(r){t.error(`Failed to sync plugin: ${e.destName}`,{error:r instanceof Error?r.message:"Unknown error"}),i.errors.push(e.destName)}}return i.synced.length>0&&t.info("Synced global plugins",{destDir:d,synced:i.synced}),i},exports.cleanupLegacyProjectPlugins=async function(e,t){const r=a.default.join(e,".opencode","plugin"),s=[]
|
|
13
|
+
if(!o.default.existsSync(r))return{removed:s}
|
|
14
|
+
for(const e of l){const t=a.default.join(r,e)
|
|
15
|
+
if(o.default.existsSync(t))try{await o.default.promises.rm(t,{recursive:!0,force:!0}),s.push(e)}catch{}}s.length>0&&t.info("Removed legacy per-project plugin copies",{projectPluginDir:r,removed:s})
|
|
16
|
+
try{0===(await o.default.promises.readdir(r)).length&&await o.default.promises.rmdir(r)}catch{}return{removed:s}}
|
|
17
|
+
const o=n(t(79896)),a=n(t(16928)),i=n(t(76982)),u=t(41306),c=[{sourceName:"plugins/hooks/plugin.js",destName:"event-hooks.js"},{sourceName:"plugins/inject-session.js",destName:"inject-session.js"},{sourceName:"plugins/system-logger.js",destName:"system-logger.js"}],l=[...c.map((e=>e.destName)),"package.json","lib"]}}
|
package/dist/403.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
exports.id=403,exports.ids=[403],exports.modules={15403:(t,exports)=>{exports.formatCombinedOutput=function(t,e){const n=e.maxOut??o,u=t.length>n
|
|
3
|
+
return{output:u?"\u2026"+t.slice(-n):t,truncatedNote:u?`\n_(output truncated: view full with \`log:${e.invocationId}\`)_`:"",logFooter:`Log: \`log:${e.invocationId}\``}}
|
|
4
|
+
const o=3e3}}
|
package/dist/491.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
exports.id=491,exports.ids=[491],exports.modules={23491:function(e,exports,t){function s(e,t="",n=0,r=[]){if(n>4||r.length>=i)return r
|
|
3
|
+
let c=[]
|
|
4
|
+
try{c=a.default.readdirSync(l.default.join(e,t),{withFileTypes:!0})}catch{return r}for(const f of c.sort(((e,t)=>e.name.localeCompare(t.name)))){if(f.name.startsWith(".")||o.has(f.name))continue
|
|
5
|
+
const c=t?`${t}/${f.name}`:f.name
|
|
6
|
+
if(f.isDirectory())s(e,c,n+1,r)
|
|
7
|
+
else if(f.isFile())try{r.push({rel:c,size:a.default.statSync(l.default.join(e,c)).size})}catch{}if(r.length>=i)break}return r}function n(e,t){let s=""
|
|
8
|
+
try{s=a.default.readFileSync(e,"utf8")}catch{return[]}if(s.length>4e5)return[]
|
|
9
|
+
const n=[],r=e=>{e?.[1]&&!n.includes(e[1])&&n.push(e[1])},l=s.split("\n")
|
|
10
|
+
for(const e of l)if(".py"!==t){if(r(e.match(/^\s{0,2}(?:export\s+)?(?:async\s+)?function\s*\*?\s*([A-Za-z_$][\w$]*)/)),r(e.match(/^\s{0,2}(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/)),r(e.match(/^\s{0,2}(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/)),n.length>=30)break}else r(e.match(/^(?:async\s+)?def\s+([A-Za-z_]\w*)/)),r(e.match(/^class\s+([A-Za-z_]\w*)/))
|
|
11
|
+
return n}var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
12
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.generateProjectMap=function(e){if(!e||!a.default.existsSync(e))return null
|
|
13
|
+
const t=s(e)
|
|
14
|
+
if(0===t.length)return null
|
|
15
|
+
const r=[]
|
|
16
|
+
r.push("Files (path, size):")
|
|
17
|
+
for(const e of t)r.push(` ${e.rel} (${e.size>=1024?`${Math.round(e.size/1024)} KB`:`${e.size} B`})`)
|
|
18
|
+
const o=t.filter((e=>c.has(l.default.extname(e.rel))))
|
|
19
|
+
for(const t of o.slice(0,12)){const s=n(l.default.join(e,t.rel),l.default.extname(t.rel))
|
|
20
|
+
s.length&&r.push(`${t.rel}: ${s.join(", ")}`)}const i=function(e,t){const s=new Set
|
|
21
|
+
for(const n of t){if(!/\.(m?js|cjs|ts|tsx|jsx)$/.test(n.rel))continue
|
|
22
|
+
let t=""
|
|
23
|
+
try{t=a.default.readFileSync(l.default.join(e,n.rel),"utf8")}catch{continue}for(const e of t.matchAll(/localStorage\.(?:getItem|setItem)\(\s*['"`]([^'"`]{1,80})['"`]/g))s.add(e[1])
|
|
24
|
+
for(const e of t.matchAll(/(?:NYCKEL|KEY|STORAGE)[A-Z_]*\s*=\s*['"`]([^'"`]{1,80})['"`]/g))s.add(e[1])
|
|
25
|
+
if(s.size>20)break}return[...s]}(e,t)
|
|
26
|
+
i.length&&r.push(`Storage keys: ${i.join(", ")}`)
|
|
27
|
+
const u=t.find((e=>/^readme\.md$/i.test(e.rel)))
|
|
28
|
+
if(u)try{const t=a.default.readFileSync(l.default.join(e,u.rel),"utf8").split("\n").filter((e=>/^#{1,3}\s/.test(e))).slice(0,12).map((e=>e.replace(/^#+\s*/,"").trim()))
|
|
29
|
+
t.length&&r.push(`README sections: ${t.join(" | ")}`)}catch{}let h=r.join("\n")
|
|
30
|
+
return h.length>f&&(h=h.slice(0,f)+"\n[... map clipped]"),`Project map (generated from the worktree at pickup; read this, then open only the files you will change):\n${h}`}
|
|
31
|
+
const a=r(t(79896)),l=r(t(16928)),o=new Set(["node_modules",".git",".alchemy",".alchemy_lab","dist","build","coverage",".next","vendor","__pycache__"]),c=new Set([".js",".mjs",".cjs",".ts",".tsx",".jsx",".py",".go",".rs",".rb",".java",".kt",".swift"]),i=80,f=3500}}
|