alchemylab 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +35 -33
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -398,7 +398,7 @@ r>0&&(t=e.substring(0,r+5))}else{const r=e.lastIndexOf("/dist-dev/")
|
|
|
398
398
|
if(r>0)t=e.substring(0,r+9)
|
|
399
399
|
else{const r=e.lastIndexOf("/dist/")
|
|
400
400
|
r>0&&(t=e.substring(0,r+5))}}if(!t){const e=process.cwd()
|
|
401
|
-
t=e.includes("alchemy-local")||e.includes("alchemy")?s.default.join(e,"dist-dev"):s.default.join(e,"dist")}const r=t.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost")||!1,o=
|
|
401
|
+
t=e.includes("alchemy-local")||e.includes("alchemy")?s.default.join(e,"dist-dev"):s.default.join(e,"dist")}const r=t.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost")||!1,o=s.default.join(t,"mcp-direct.js"),i=process.env.REMOTE_SERVER_URL||"https://api.alchemylabcode.com"
|
|
402
402
|
return n={installPath:t,scriptPath:o,nodePath:process.execPath,isDevMode:r,serverName:"alchemy",remoteServerUrl:i},n}var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
403
403
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.getInstallContext=r,exports.clearInstallContextCache=function(){n=null},exports.isDevMode=function(e){return e?e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost")||!1:r().isDevMode},exports.getServerName=function(e){return e??r().isDevMode,"alchemy"}
|
|
404
404
|
const s=o(t(6928))
|
|
@@ -530,29 +530,29 @@ if(!u)return{success:!1,message:"requestId is required",error:"Missing requestId
|
|
|
530
530
|
m.info("Starting run-tests action",{requestId:u,testProcessIds:g})
|
|
531
531
|
try{const d=await y.get(`/api/projects/${v}/requests/${u}`)
|
|
532
532
|
if(!d.success||!d.data)return{success:!1,message:`Request ${u} not found`,error:"Request not found"}
|
|
533
|
-
const p=d.data,
|
|
534
|
-
let
|
|
535
|
-
if(g?.length)
|
|
533
|
+
const p=d.data,I=p.branchName||"requests/"+u
|
|
534
|
+
let E
|
|
535
|
+
if(g?.length)E=(await Promise.all(g.map((e=>y.get(`/api/projects/${v}/test-processes/${e}`))))).filter((e=>e.success&&e.data)).map((e=>e.data))
|
|
536
536
|
else{const e=await y.get(`/api/projects/${v}/test-processes/active`)
|
|
537
|
-
|
|
537
|
+
E=e.success&&e.data?Array.isArray(e.data)?e.data:[e.data]:[]}if(0===E.length)return{success:!1,message:"No test processes configured for this project. Set up testing first.",error:"No test processes"}
|
|
538
538
|
const b=(0,s.join)(w,c,l),k=(0,s.join)(b,u),j=(0,n.simpleGit)(w)
|
|
539
539
|
if((0,o.existsSync)(k))try{const e=(0,n.simpleGit)(k)
|
|
540
|
-
await e.raw(["reset","--hard",
|
|
540
|
+
await e.raw(["reset","--hard",I]),m.info("Reset test worktree to latest branch HEAD",{worktreePath:k,branchName:I})}catch(e){m.warn("Could not reset test worktree, recreating",{error:(0,i.getErrorMessage)(e)})
|
|
541
541
|
try{await j.raw(["worktree","remove","--force",k])}catch{}}if(!(0,o.existsSync)(k)){(0,o.mkdirSync)(b,{recursive:!0})
|
|
542
|
-
try{if(!(await j.branch()).all.includes(
|
|
543
|
-
await j.raw(["worktree","add","--detach",k,
|
|
542
|
+
try{if(!(await j.branch()).all.includes(I))return{success:!1,message:`Branch ${I} not found`,error:"Branch not found"}
|
|
543
|
+
await j.raw(["worktree","add","--detach",k,I]),m.info("Created test worktree (detached)",{worktreePath:k,branchName:I})}catch(e){if(!(e+"").includes("already exists"))return{success:!1,message:"Failed to create test worktree: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}
|
|
544
544
|
m.info("Test worktree already exists, using as-is",{worktreePath:k})}}const{execSync:T}=await Promise.resolve().then(t.t.bind(t,5317,23))
|
|
545
|
-
for(const e of
|
|
545
|
+
for(const e of E){const t=e.config?.setup||[]
|
|
546
546
|
if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length})
|
|
547
547
|
for(const r of t)try{m.info("Setup command",{command:r,worktreePath:k}),T(r,{cwd:k,timeout:12e4,encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"/bin/sh",env:{...process.env,FORCE_COLOR:"0"}})}catch(t){m.warn("Setup command failed",{command:r,processName:e.name,exitCode:t.status,stderr:(t.stderr||"").slice(-500)})}}}const S=[],P=[]
|
|
548
|
-
for(const process of
|
|
548
|
+
for(const process of E){let t
|
|
549
549
|
const o=e.testRunIds?.[S.length]
|
|
550
550
|
try{o?(await y.patch(`/api/projects/${v}/test-runs/${o}`,{worktreePath:k}),t=(await y.get(`/api/projects/${v}/test-runs/${o}`)).data,S.push(t.id)):(t=(await y.post(`/api/projects/${v}/test-runs`,{requestId:u,testProcessId:process.id,worktreePath:k,parentSessionId:h})).data,S.push(t.id))}catch(e){m.error("Failed to get/create test run record",{requestId:u,processId:process.id,preCreatedId:o,error:(0,i.getErrorMessage)(e)})
|
|
551
551
|
continue}const s=r(p,process,0,v,f),n=f?"tester":"auto-tester"
|
|
552
552
|
try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:s,cwd:k,projectPath:w,projectId:v,title:`[${u}] Test: ${process.name}`,requestId:u,parentSessionId:h,allowParallel:!0,apiClient:y,logger:m,trackChildPid:C})
|
|
553
553
|
if(!e.success)throw Error(e.error||"Spawn failed")
|
|
554
554
|
P.push(t.id),e.sessionId&&y.patch(`/api/projects/${v}/test-runs/${t.id}`,{testerSessionId:e.sessionId}).catch((e=>m.warn("Failed to set testerSessionId on test run",{error:(0,i.getErrorMessage)(e)}))),m.info("Spawned test-runner agent",{testRunId:t.id,processName:process.name,worktreePath:k,pid:e.pid,sessionId:e.sessionId})}catch(e){m.error("Failed to spawn test-runner agent",{testRunId:t.id,error:(0,i.getErrorMessage)(e)})
|
|
555
|
-
try{await y.patch(`/api/projects/${v}/test-runs/${t.id}`,{status:"error",results:{steps:[],summary:"Failed to spawn test agent: "+(0,i.getErrorMessage)(e)}})}catch{}}}return 0===P.length?{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}:{success:!0,message:`Started ${P.length} test run(s) for request ${u}`,data:{testRunIds:S,agentsSpawned:P.length,processCount:
|
|
555
|
+
try{await y.patch(`/api/projects/${v}/test-runs/${t.id}`,{status:"error",results:{steps:[],summary:"Failed to spawn test agent: "+(0,i.getErrorMessage)(e)}})}catch{}}}return 0===P.length?{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}:{success:!0,message:`Started ${P.length} test run(s) for request ${u}`,data:{testRunIds:S,agentsSpawned:P.length,processCount:E.length,worktreePath:k}}}catch(e){const t=(0,i.getErrorMessage)(e)
|
|
556
556
|
return m.error("run-tests action failed",{requestId:u,error:t}),{success:!1,message:"Failed to run tests: "+t,error:t}}}
|
|
557
557
|
const o=t(9896),s=t(6928),n=t(3807),i=t(8824),a=t(8148),c=".alchemy_lab",l="test-worktrees"},6636(e,exports,t){Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRevertRequestAction=async function(e,t){const{requestId:s,reason:n}=e,{logger:i,apiClient:a,projectPath:c,projectId:l}=t
|
|
558
558
|
if(!s)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
@@ -640,9 +640,9 @@ let v
|
|
|
640
640
|
if(n.ALCHEMY_SESSION_TAG=`mcp-${m}-${y}`,r(c,"Spawn env setup",{agent:t,initialBranch:h||"none",hasParentServer:!!l,sessionId:u||"new",attachMode:!!p}),v=p&&u&&o?["run","--attach",p,"--session",u,"--agent",t,o]:u&&o?["run","--session",u,"--agent",t,o]:o?["run","--agent",t,o]:["run","--agent",t],g){const e=v.findIndex((e=>e===o))
|
|
641
641
|
e>0?v.splice(e,0,"--model",g):v.push("--model",g)}r(c,"Spawning opencode",{args:v.map((e=>e===o?`[prompt:${e.length}chars]`:e)),model:g||"not specified",promptLength:o?.length,promptPreview:o?.slice(0,100),cwd:c,hasSessionId:!!u,hasParentServerUrl:!!l})
|
|
642
642
|
const w=(0,a.join)(c||process.cwd(),".alchemy_lab"),C=(0,a.join)(w,"spawn-stderr.log")
|
|
643
|
-
let
|
|
644
|
-
try{
|
|
645
|
-
return
|
|
643
|
+
let I
|
|
644
|
+
try{I=(0,i.openSync)(C,"a")}catch{}const E=(0,s.spawn)("opencode",v,{detached:!0,stdio:["ignore","ignore",I??"ignore"],cwd:c||process.cwd(),env:n})
|
|
645
|
+
return E.unref(),f&&f(E.pid),{success:!0,pid:E.pid}}catch(e){const t=(0,n.getErrorMessage)(e)
|
|
646
646
|
return m&&e instanceof Error&&m(e),{success:!1,error:t}}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.spawnDetachedSubagent=o,exports.spawnJournalSubagent=function(e,t,r,s){return o({agent:"journal",prompt:`Generate a journal entry for request ${e}.\n\nSteps:\n1. Use ${t}__generate_request_journal tool with requestId: ${e}\n2. Write a concise narrative journal entry based on the returned data covering:\n - What was implemented and why\n - Key decisions and trade-offs made\n - Challenges encountered and solutions\n - Files modified and their purposes\n - How acceptance criteria were met\n3. Save as JOURNAL-${e}.md using ${t}__document_operations (operation: create, type: Report)\n\nKeep it story-focused and informative.`,cwd:r,parentServerUrl:s})},exports.spawnWorkSubagent=function(e,t,r){return o({agent:"work",prompt:e,cwd:t,parentServerUrl:r})}
|
|
647
647
|
const s=t(5317),n=t(8824),i=t(9896),a=t(6928)},8148(e,exports,t){function r(e,t){try{const t=(0,s.join)(e,a,"opencode.json"),r=(0,o.readFileSync)(t,"utf8"),n=JSON.parse(r)
|
|
648
648
|
if(n.port)return"http://127.0.0.1:"+n.port}catch(e){t.debug("Could not read opencode.json for server URL",{error:(0,i.getErrorMessage)(e)})}return null}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getOpenCodeServerUrl=r,exports.spawnActionAgent=async function(e){const{agent:t,prompt:o,cwd:s,projectPath:a,title:c,requestId:l,parentSessionId:d,allowParallel:u,apiClient:g,logger:h,trackChildPid:p}=e,f=r(a,h)
|
|
@@ -708,25 +708,25 @@ return` ${o.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${s}`}: ${r}`})).join("\n")
|
|
|
708
708
|
let i=`---\ndescription: "${e.description||e.name}"\nmode: "${"utility"===e.mode?"primary":e.mode||"primary"}"\nmodel: "${r}"\n`
|
|
709
709
|
if(e.color&&(i+=`color: "${e.color}"\n`),i+=`tools:\n${n}\n`,e.opencodePermission&&Object.keys(e.opencodePermission).length>0){i+="permission:\n"
|
|
710
710
|
for(const[t,r]of Object.entries(e.opencodePermission))if("object"==typeof r&&null!==r){i+=` ${t}:\n`
|
|
711
|
-
for(const[e,t]of Object.entries(r))i+=` ${e}: ${t}\n`}else i+=` ${t}: ${r}\n`}return void 0!==e.temperature&&(i+=`temperature: ${e.temperature}\n`),e.name.endsWith("-thinking")&&(i+="thinking:\n type: enabled\n budgetTokens: 10000\n"),"journal"===e.name&&(i+=`mcp:\n ${t}:\n env:\n ALCHEMY_MODE: "journal"\n`),i+="---\n\n",s&&(i+=s.trim()+"\n\n"),e.prompt&&(i+=e.prompt),e.promptAddition&&(i+="\n\n"+e.promptAddition),i}async configureOpenCode(e,s,n="node",i,a){try{const r=await Promise.resolve().then(t.t.bind(t,1943,23)),c=await Promise.resolve().then(t.t.bind(t,6928,23)),l=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),d="alchemy",u=
|
|
711
|
+
for(const[e,t]of Object.entries(r))i+=` ${e}: ${t}\n`}else i+=` ${t}: ${r}\n`}return void 0!==e.temperature&&(i+=`temperature: ${e.temperature}\n`),e.name.endsWith("-thinking")&&(i+="thinking:\n type: enabled\n budgetTokens: 10000\n"),"journal"===e.name&&(i+=`mcp:\n ${t}:\n env:\n ALCHEMY_MODE: "journal"\n`),i+="---\n\n",s&&(i+=s.trim()+"\n\n"),e.prompt&&(i+=e.prompt),e.promptAddition&&(i+="\n\n"+e.promptAddition),i}async configureOpenCode(e,s,n="node",i,a){try{const r=await Promise.resolve().then(t.t.bind(t,1943,23)),c=await Promise.resolve().then(t.t.bind(t,6928,23)),l=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),d="alchemy",u=c.join(e,"mcp-direct.js")
|
|
712
712
|
this.logger.info("Configuring OpenCode",{isDevMode:l,serverName:d,scriptPath:u,projectPath:s})
|
|
713
713
|
const g=c.join(s,"opencode.json")
|
|
714
714
|
let h=null,p=null,f=null,m=null,y=!1,v=!1
|
|
715
715
|
const w=c.join(s,".alchemy_lab","config.json")
|
|
716
716
|
if(!i){try{const e=await r.readFile(w,"utf-8"),t=JSON.parse(e)
|
|
717
717
|
t?.openCodeTools?(h=t.openCodeTools,this.logger.info("Found tool settings in project config",{projectConfigTools:h})):v=!0,t?.openCodeModel&&(p=t.openCodeModel)}catch(e){this.logger.debug("No project config found, will create with defaults"),v=!0}try{const e=await r.readFile(g,"utf-8"),t=JSON.parse(e)
|
|
718
|
-
t?.mode?.alchemy?.tools&&(f=t.mode.alchemy.tools,this.logger.info("Found existing tool settings in opencode.json",{existingTools:f})),t?.model&&(m=t.model),t?.mode?.journal&&(y=!0)}catch(e){this.logger.info("No existing opencode.json found, will use defaults")}}const C=i?.openCodeTools||h||f||{glob:!1,grep:!1,ls:!1,list:!1,view:!1,read:!1,write:!1,edit:!1,patch:!1,diagnostics:!0,bash:!0===i?.enableBash,fetch:!1,sourcegraph:!0,agent:!1!==a,todoread:!1,todowrite:!1},
|
|
719
|
-
i?.openCodeModel?
|
|
718
|
+
t?.mode?.alchemy?.tools&&(f=t.mode.alchemy.tools,this.logger.info("Found existing tool settings in opencode.json",{existingTools:f})),t?.model&&(m=t.model),t?.mode?.journal&&(y=!0)}catch(e){this.logger.info("No existing opencode.json found, will use defaults")}}const C=i?.openCodeTools||h||f||{glob:!1,grep:!1,ls:!1,list:!1,view:!1,read:!1,write:!1,edit:!1,patch:!1,diagnostics:!0,bash:!0===i?.enableBash,fetch:!1,sourcegraph:!0,agent:!1!==a,todoread:!1,todowrite:!1},I={tools:C},E=i?.openCodeModel||p||m||"anthropic/claude-opus-4-5"
|
|
719
|
+
i?.openCodeModel?I.model=i.openCodeModel:p?I.model=p:m&&(I.model=m)
|
|
720
720
|
const b=c.join(s,".opencode","agent")
|
|
721
721
|
await r.mkdir(b,{recursive:!0})
|
|
722
|
-
const k=
|
|
722
|
+
const k=E||"anthropic/claude-opus-4-5-20251101"
|
|
723
723
|
let j,T=null,S=null
|
|
724
724
|
if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),S=await this.fetchProjectFromRemote(i.projectId),j=S?.metadata?.globalSystemPrompt,j&&this.logger.info("Using global system prompt from project metadata",{projectId:i.projectId,promptLength:j.length}),T=await this.fetchAgentsFromRemote(i.projectId),T&&T.length>0){this.logger.info("Using agents from remote database",{count:T.length})
|
|
725
725
|
for(const e of T){if(!e.enabled)continue
|
|
726
726
|
const t=this.generateAgentMarkdown(e,d,k,j),o=c.join(b,e.name+".md")
|
|
727
727
|
await r.writeFile(o,t,"utf8"),this.logger.info(`Created ${e.name} agent file from remote config`,{agentPath:o,mode:e.mode})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
|
|
728
728
|
else this.logger.warn("No projectId provided - agents must be configured via database")
|
|
729
|
-
const P={$schema:"https://opencode.ai/config.json",model:
|
|
729
|
+
const P={$schema:"https://opencode.ai/config.json",model:E,mcp:{[d]:{type:"local",command:[n,u,s],enabled:!0}}}
|
|
730
730
|
if(l&&process.env.REMOTE_SERVER_URL&&(P.mcp[d].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL}),T&&T.length>0){P.agent={}
|
|
731
731
|
for(const e of T){if(!e.enabled)continue
|
|
732
732
|
const t={}
|
|
@@ -794,11 +794,13 @@ if(!s.default.existsSync(t))return null
|
|
|
794
794
|
try{return"http://localhost:"+JSON.parse(s.default.readFileSync(t,"utf-8")).port}catch{return null}}(t.projectPath)
|
|
795
795
|
if(!l)return{success:!1,message:"OpenCode not running (no opencode.json found)"}
|
|
796
796
|
const d=`${l}${i.startsWith("/")?i:"/"+i}`
|
|
797
|
-
try{const e={"Content-Type":"application/json",...c},
|
|
798
|
-
"GET"!==r.toUpperCase()&&"HEAD"!==r.toUpperCase()&&void 0!==a
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
797
|
+
try{const e={"Content-Type":"application/json",...c},o={method:r.toUpperCase(),headers:e}
|
|
798
|
+
if("GET"!==r.toUpperCase()&&"HEAD"!==r.toUpperCase()&&void 0!==a){let e=a
|
|
799
|
+
if("object"==typeof a&&null!==a&&"_alchemyAgentType"in a){const{_alchemyAgentType:t,...r}=a
|
|
800
|
+
e=r}o.body="string"==typeof e?e:JSON.stringify(e)}const s=await fetch(d,o),l=s.headers.get("content-type")||""
|
|
801
|
+
let u
|
|
802
|
+
if(u=l.includes("application/json")?await s.json():await s.text(),"POST"===r.toUpperCase()&&"/session"===i&&s.status>=200&&300>s.status&&u?.id)try{const e=u.id,r=a?.parentID
|
|
803
|
+
await t.apiClient.post(`/api/projects/${t.projectId}/cli-sessions`,{externalSessionId:e,providerId:"opencode",...r?{parentExternalSessionId:r}:{},agentType:a?._alchemyAgentType||"unknown",status:"active"}),t.logger.info("Registered proxy-created session in DB",{sessionId:e,parentSessionId:r})}catch(e){t.logger.warn("Failed to register proxy-created session",{error:(0,n.getErrorMessage)(e)})}return{success:!0,message:"Proxy request completed",data:{status:s.status,contentType:l,body:u}}}catch(e){return{success:!1,message:"Proxy request failed: "+(0,n.getErrorMessage)(e),error:(0,n.getErrorMessage)(e)}}}
|
|
802
804
|
const o=r(t(6928)),s=r(t(9896)),n=t(8824)},9278(module){module.exports=require("net")},9735(e,exports,t){Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleApproveRequestAction=async function(e,t){const{requestId:o}=e,{logger:s,apiClient:n,projectId:i}=t
|
|
803
805
|
if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
804
806
|
s.info("Starting approve-request action",{requestId:o})
|
|
@@ -822,18 +824,18 @@ for(var a=0;a<s.length;a++)o[s[a]]=1})(require("./"+e.u(t)))}
|
|
|
822
824
|
var i={};(()=>{function t(){const e=[(0,g.join)(__dirname,"..","package.json"),(0,g.join)(__dirname,"..","..","package.json")]
|
|
823
825
|
for(const t of e)try{const e=JSON.parse((0,d.readFileSync)(t,"utf-8"))
|
|
824
826
|
if(e.version)return e.version}catch{}return"unknown"}function r(){const e=t()
|
|
825
|
-
console.log(`\nAlchemyLab CLI v${e}\n\nUSAGE:\n alchemy [command] [options]\n\nCOMMANDS:\n (none) Start action server + launch OpenCode (default)\n serve Start the action server only (for GUI-triggered actions)\n logout Log out and remove stored auth token\n\nOPTIONS:\n --port <n> Action server port (default: auto)\n --help, -h Show this help message\n --version, -v Show version information\n\nEXAMPLES:\n alchemy # Start server + OpenCode (default)\n alchemy serve # Start action server only\n alchemy logout # Log out\nFor more information, visit: https://alchemylabcode.com/\n`)}function o(e){const t=(0,g.join)(e,
|
|
827
|
+
console.log(`\nAlchemyLab CLI v${e}\n\nUSAGE:\n alchemy [command] [options]\n\nCOMMANDS:\n (none) Start action server + launch OpenCode (default)\n serve Start the action server only (for GUI-triggered actions)\n logout Log out and remove stored auth token\n\nOPTIONS:\n --port <n> Action server port (default: auto)\n --help, -h Show this help message\n --version, -v Show version information\n\nEXAMPLES:\n alchemy # Start server + OpenCode (default)\n alchemy serve # Start action server only\n alchemy logout # Log out\nFor more information, visit: https://alchemylabcode.com/\n`)}function o(e){const t=(0,g.join)(e,E.ALCHEMY_DIR_NAME,"project.json")
|
|
826
828
|
return(0,d.existsSync)(t)}function s(e){const t=(0,p.platform)(),r="darwin"===t?"open":"win32"===t?"cmd":"xdg-open",o="win32"===t?["/c","start","",e]:[e];(0,h.spawn)(r,o,{detached:!0,stdio:"ignore"}).unref()}async function n(e,t=3e5){return new Promise((r=>{const o=setInterval((async()=>{await e.hasValidToken()&&(clearInterval(o),r(!0))}),2e3)
|
|
827
829
|
setTimeout((()=>{clearInterval(o),r(!1)}),t)}))}async function a(t){if(process.env.REMOTE_SERVER_URL)return process.env.REMOTE_SERVER_URL
|
|
828
830
|
try{const{createConfigManager:r}=await e.e(60).then(e.t.bind(e,9060,23)),o=r(t)
|
|
829
831
|
return(await o.getInstallationConfig()).remoteServerUrl||"https://api.alchemylabcode.com"}catch{return"https://api.alchemylabcode.com"}}async function c(e,t,r,o,s="cli-init"){try{const n=(0,g.basename)(r)||"Local Project",i=await fetch(e+"/api/projects",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer "+t,"X-Client-Type":"alchemy-local-service","X-Service-Secret":process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"},body:JSON.stringify({name:n,description:"Project for "+n,status:"active",metadata:{basePath:r,createdVia:s}})})
|
|
830
832
|
if(i.ok){const e=await i.json()
|
|
831
833
|
return e.data?.id||e.id}o.info(`Project registration failed (HTTP ${i.status})`)}catch(e){o.info("Project registration failed",{error:(0,C.getErrorMessage)(e)})}}Object.defineProperty(i,"__esModule",{value:!0})
|
|
832
|
-
const l=e(3444),d=e(9896),u=e(1943),g=e(6928),h=e(5317),p=e(857),f=e(1253),m=e(8984),y=e(2366),v=e(3962),w=e(1187),C=e(8824),
|
|
834
|
+
const l=e(3444),d=e(9896),u=e(1943),g=e(6928),h=e(5317),p=e(857),f=e(1253),m=e(8984),y=e(2366),v=e(3962),w=e(1187),C=e(8824),I=e(6800),E=e(4139);(async function(){const i=process.argv.slice(2)
|
|
833
835
|
let p
|
|
834
836
|
const b=i.indexOf("--port");-1!==b&&i[b+1]&&(p=parseInt(i[b+1],10),isNaN(p)&&(console.error("\u274c Invalid port number"),process.exit(1)))
|
|
835
837
|
const k=i.filter(((e,t)=>"--port"!==e&&(-1===b||t!==b+1)))
|
|
836
|
-
if(0===k.length)return void await async function(r={}){const i=process.cwd(),l=(0,g.join)(i,
|
|
838
|
+
if(0===k.length)return void await async function(r={}){const i=process.cwd(),l=(0,g.join)(i,E.ALCHEMY_DIR_NAME)
|
|
837
839
|
await(0,u.mkdir)(l,{recursive:!0})
|
|
838
840
|
const p=(0,g.join)(l,"server.log"),{createLogger:b}=await Promise.resolve().then(e.t.bind(e,2782,23)),k=b({serviceName:"alchemy-server",console:!1,logToFile:!0,logFilePath:p,baseDir:i}),j=await a(k),T=new f.TokenManager(k,i,{remoteServerUrl:j,allowInteractiveAuth:!1})
|
|
839
841
|
await T.initialize()
|
|
@@ -843,16 +845,16 @@ const e=await T.startAuthenticationFlow()
|
|
|
843
845
|
console.log("\u{1f310} "+e.authUrl),s(e.authUrl),console.log(""),console.log("\u{1f446} Browser window opened. Complete login there."),console.log("\u23f1\ufe0f Waiting for authentication..."),await n(T)||(console.error("\u274c Authentication timed out. Run `alchemy` again to retry."),process.exit(1)),S=await T.getToken(),console.log(`\u2705 Welcome, ${S?.username||"user"}!`),console.log("")}let P=S?.token
|
|
844
846
|
await async function(e,t,r,s){if(o(e))return
|
|
845
847
|
console.log("\u{1f4c1} Initializing AlchemyLab project..."),console.log("")
|
|
846
|
-
const n=(0,g.join)(e,
|
|
848
|
+
const n=(0,g.join)(e,E.ALCHEMY_DIR_NAME)
|
|
847
849
|
let i
|
|
848
850
|
await(0,u.mkdir)(n,{recursive:!0}),r?(console.log(`\u{1f4cb} Registering project "${(0,g.basename)(e)||"Local Project"}"...`),i=await c(t,r,e,s),console.log(i?"\u2705 Project registered: "+i:"\u26a0\ufe0f Project registration failed (will retry next run)")):console.log("\u26a0\ufe0f No auth token \u2014 skipping project registration")
|
|
849
851
|
const a=(0,g.join)(n,"project.json");(0,d.writeFileSync)(a,JSON.stringify({id:i||null,name:(0,g.basename)(e)||"Local Project",status:"active",registeredRemotely:!!i,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:n}},null,2),"utf-8"),console.log("")}(i,j,P,k)
|
|
850
|
-
const A=(0,g.join)(i,
|
|
852
|
+
const A=(0,g.join)(i,E.ALCHEMY_DIR_NAME,"project.json")
|
|
851
853
|
let M
|
|
852
854
|
try{const e=JSON.parse((0,d.readFileSync)(A,"utf-8"))
|
|
853
855
|
if(M=e.id||void 0,!1===e.registeredRemotely&&P){const t=await c(j,P,i,k,"cli-init-retry")
|
|
854
856
|
t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(A,JSON.stringify(e,null,2),"utf-8"),M=t,console.log("\u2705 Project registered: "+t))}}catch{k.warn("Could not read project.json")}await async function(t){try{return void(0,h.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{}let r="latest"
|
|
855
|
-
try{const e=(0,g.join)(t,
|
|
857
|
+
try{const e=(0,g.join)(t,E.ALCHEMY_DIR_NAME,"config.json")
|
|
856
858
|
if((0,d.existsSync)(e)){const t=JSON.parse((0,d.readFileSync)(e,"utf-8"))
|
|
857
859
|
t.opencodeVersion&&(r=t.opencodeVersion)}}catch{}console.log(""),console.log("\u{1f4e6} OpenCode not found in PATH. Installing...")
|
|
858
860
|
const o="latest"===r?"latest":"v"+r
|
|
@@ -866,7 +868,7 @@ const s=(0,g.join)(o,"opencode")
|
|
|
866
868
|
return(0,h.execFileSync)("curl",["-fsSL","-o",s,t],{stdio:"inherit",timeout:6e4}),(0,h.execFileSync)("chmod",["+x",s],{stdio:"pipe"}),(process.env.PATH||"").split(":").includes(o)||(console.log(` \u26a0\ufe0f Add ${o} to your PATH:`),console.log(` export PATH="${o}:$PATH"`),console.log(""),process.env.PATH=`${o}:${process.env.PATH}`),void console.log("\u2705 OpenCode installed successfully")}}catch(e){console.log("\u26a0\ufe0f Binary download failed: "+(0,C.getErrorMessage)(e))}console.log(""),console.log("\u26a0\ufe0f Could not auto-install OpenCode."),console.log(""),console.log(" Install manually:"),console.log(" go install github.com/sst/opencode@latest"),console.log(" Or download from:"),console.log(" https://github.com/sst/opencode/releases"),console.log("")}(i)
|
|
867
869
|
const _=t()
|
|
868
870
|
console.log(["\n .oo 8 8 o 8 \n .P 8 8 8 8 8 \n .P 8 8 .oPYo. 8oPYo. .oPYo. ooYoYo. o o 8 .oPYo. 8oPYo. \n oPooo8 8 8 ' 8 8 8oooo8 8' 8 8 8 8 8 .oooo8 8 8 \n .P 8 8 8 . 8 8 8. 8 8 8 8 8 8 8 8 8 8 \n.P 8 8 `YooP' 8 8 `Yooo' 8 8 8 `YooP8 8oooo `YooP8 `YooP' \n..:::::....:.....:..:::..:.....:..:..:..:....8 ......:.....::.....:","::::::::::::::::::::::::::::::::::::::::::ooP'.::::::::::::::::::: v"+_,"::::::::::::::::::::::::::::::::::::::::::...::::::::::::::::::::::\n"].join("\n")),console.log("\u{1f4c1} Project: "+i)
|
|
869
|
-
const O=r.port||await(0,
|
|
871
|
+
const O=r.port||await(0,I.findAvailablePort)(3456),x=new y.AlchemyActionServer(k,{projectPath:i,remoteServerUrl:j,port:O,authToken:P})
|
|
870
872
|
try{await x.start()
|
|
871
873
|
let t=process.env.FRONTEND_URL
|
|
872
874
|
if(!t)if(j.includes("api.alchemylabcode.com"))t="https://alchemylabcode.com"
|
|
@@ -875,7 +877,7 @@ if(e.ok){const r=await e.json()
|
|
|
875
877
|
t=r?.data?.frontendUrl}}catch{}t||(t=j.replace(/:\d+/,":3000"))}console.log(""),console.log(" Action server .... port "+x.getPort())
|
|
876
878
|
try{const e=(0,v.getInstallContext)(),t=new m.MCPClientConfigurer(k,j,P),r=new w.UnifiedLauncher(k,e,t),o=await r.launch({projectPath:i,settings:M?{projectId:M}:void 0})
|
|
877
879
|
if(o.success&&o.port){o.pid&&x.trackChildPid(o.pid),x.setOpenCodePort(o.port),console.log(" OpenCode serve ... port "+o.port)
|
|
878
|
-
const e=(0,g.join)(i,
|
|
880
|
+
const e=(0,g.join)(i,E.ALCHEMY_DIR_NAME,"opencode.json");(0,d.writeFileSync)(e,JSON.stringify({port:o.port,pid:o.pid,startedAt:(new Date).toISOString()},null,2)),k.info("Wrote OpenCode config",{path:e,port:o.port})}else{if(!o.success)throw Error(o.error||"Launch failed")
|
|
879
881
|
console.log(" OpenCode serve ... started")}}catch(e){k.warn("Failed to launch OpenCode serve",{error:(0,C.getErrorMessage)(e)}),console.log(" OpenCode serve ... failed (run manually: opencode serve)")}console.log(""),console.log(` \u2192 Alchemy UI: \x1b]8;;${t}\x07${t}\x1b]8;;\x07`),console.log("")
|
|
880
882
|
let r=!1,o=null
|
|
881
883
|
const a=async()=>{r||(r=!0,o&&clearInterval(o),console.log(""),console.log("Shutting down..."),await x.stop(),process.exit(0))}
|
|
@@ -895,7 +897,7 @@ const i=await a(s),c=new f.TokenManager(s,r,{remoteServerUrl:i,allowInteractiveA
|
|
|
895
897
|
await c.initialize()
|
|
896
898
|
const d=await c.getToken(),u=d?.token
|
|
897
899
|
u||console.log("\u26a0\ufe0f No auth token. Some features may be limited. Run `alchemy` to authenticate.")
|
|
898
|
-
const g=e.port||await(0,
|
|
900
|
+
const g=e.port||await(0,I.findAvailablePort)(3456),h=new y.AlchemyActionServer(s,{projectPath:r,remoteServerUrl:i,port:g,authToken:u})
|
|
899
901
|
try{await h.start(),console.log(""),console.log("\u2705 Action server running!"),console.log(` Health: http://127.0.0.1:${h.getPort()}/health`),console.log(""),console.log("Press Ctrl+C to stop the server."),console.log("")
|
|
900
902
|
let e=!1
|
|
901
903
|
const t=async()=>{e||(e=!0,console.log(""),console.log("Shutting down action server..."),await h.stop(),process.exit(0))}
|