alchemylab 1.0.0 → 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/60.js +1 -1
- package/dist/cli.js +107 -97
- package/dist/http-server.js +154 -155
- package/dist/index.js +352 -353
- package/dist/mcp-direct.js +154 -155
- package/package.json +4 -4
package/dist/60.js
CHANGED
|
@@ -3,7 +3,7 @@ exports.id=60,exports.ids=[60],exports.modules={9060(e,exports,t){var i=this&&th
|
|
|
3
3
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.ConfigManager=void 0,exports.createConfigManager=function(e){return new c(e)}
|
|
4
4
|
const r=i(t(1943)),a=i(t(6928)),n=i(t(857)),o=t(4139),s=t(8824),l={multiAgent:!1,lockOverride:{enabled:!0,passwordValidityMinutes:5}},d={remoteServerUrl:"https://api.alchemylabcode.com",nodePath:"node"}
|
|
5
5
|
class c{constructor(e){this.logger=e.child({component:"config-manager"}),this.installationConfigDir=this.getInstallationConfigDir()}getInstallationConfigDir(){const e=__dirname||process.cwd(),t=n.default.homedir()
|
|
6
|
-
return e.includes("node_modules")?a.default.join(t,".
|
|
6
|
+
return e.includes("node_modules")?a.default.join(t,".alchemy_lab","config"):a.default.join(t,".alchemy_lab","config-dev")}async ensureDirectory(e){try{await r.default.access(e)}catch(t){await r.default.mkdir(e,{recursive:!0}),this.logger.debug("Created directory",{dirPath:e})}}async readConfig(e,t){try{const i=await r.default.readFile(e,"utf8"),a=JSON.parse(i)
|
|
7
7
|
return{...t,...a}}catch(i){return"ENOENT"===i.code||this.logger.warn("Error reading config file, using defaults",{filePath:e,error:(0,s.getErrorMessage)(i)}),t}}async writeConfig(e,t){await this.ensureDirectory(a.default.dirname(e)),await r.default.writeFile(e,JSON.stringify(t,null,2),"utf8"),this.logger.debug("Wrote config file",{filePath:e})}async getProjectConfig(e){const t=a.default.join(e,o.ALCHEMY_DIR_NAME,"config.json")
|
|
8
8
|
return this.readConfig(t,l)}async updateProjectConfig(e,t){const i=a.default.join(e,o.ALCHEMY_DIR_NAME,"config.json"),r={...await this.getProjectConfig(e),...t}
|
|
9
9
|
await this.writeConfig(i,r),this.logger.info("Updated project config",{projectPath:e,updates:t})}async getProjectMetadata(e){const t=a.default.join(e,o.ALCHEMY_DIR_NAME,"project.json")
|
package/dist/cli.js
CHANGED
|
@@ -45,20 +45,20 @@ if(o&&!a){const r=parseInt(o[1],10)
|
|
|
45
45
|
a=!0,this.logger.info("UnifiedLauncher: Discovered opencode serve port",{pid:n.pid,port:r}),n.unref(),e({success:!0,pid:n.pid,port:r,configPath:t.configPath})}}
|
|
46
46
|
n.stdout?.on("data",(e=>c(e.toString()))),n.stderr?.on("data",(e=>c(e.toString()))),setTimeout((()=>{a||(a=!0,this.logger.warn("UnifiedLauncher: Timeout waiting for opencode serve port",{outputBuffer:i.slice(0,500)}),n.unref(),e({success:!0,pid:n.pid,configPath:t.configPath,error:"Could not determine port from opencode serve output"}))}),1e4),n.on("error",(t=>{a||(a=!0,this.logger.error("UnifiedLauncher: opencode serve failed to start",{error:t.message}),e({success:!1,error:"Failed to start opencode serve: "+t.message}))})),n.on("exit",(t=>{a||(a=!0,this.logger.error("UnifiedLauncher: opencode serve exited early",{code:t,output:i.slice(0,500)}),e({success:!1,error:"opencode serve exited with code "+t}))}))}))}}},1253(e,exports,t){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
47
47
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.TokenManager=void 0
|
|
48
|
-
const o=r(t(6928)),s=r(t(1943)),n=r(t(6982)),i=r(t(857)),a=t(
|
|
49
|
-
exports.TokenManager=class{constructor(e,t,r={}){this.projectId=null,this.encryptionKey=null,this.pendingAuthCheck=!1,this.tempAuthCode=null,this.CACHE_TTL_MS=6e4,this.TOKEN_CACHE={manualToken:null,encryptedToken:null,hasValidToken:null},this.initPromise=null,this.initialized=!1,this.logger=e.child({component:"token-manager"}),this.remoteServerUrl=r.remoteServerUrl||"https://api.alchemylabcode.com",this.allowInteractiveAuth=!1!==r.allowInteractiveAuth,void 0!==r.cacheTtlMs&&r.cacheTtlMs>0&&(this.CACHE_TTL_MS=r.cacheTtlMs),this.serverInstallDir=this.getModuleDir(),this.tokenDirPath=
|
|
50
|
-
return e.includes("node_modules")?o.default.join(t,".
|
|
48
|
+
const o=r(t(6928)),s=r(t(1943)),n=r(t(6982)),i=r(t(857)),a=t(9067),c=t(8824),l="alchemy-lab"
|
|
49
|
+
exports.TokenManager=class{constructor(e,t,r={}){this.projectId=null,this.encryptionKey=null,this.pendingAuthCheck=!1,this.tempAuthCode=null,this.CACHE_TTL_MS=6e4,this.TOKEN_CACHE={manualToken:null,encryptedToken:null,hasValidToken:null},this.initPromise=null,this.initialized=!1,this.logger=e.child({component:"token-manager"}),this.remoteServerUrl=r.remoteServerUrl||"https://api.alchemylabcode.com",this.allowInteractiveAuth=!1!==r.allowInteractiveAuth,void 0!==r.cacheTtlMs&&r.cacheTtlMs>0&&(this.CACHE_TTL_MS=r.cacheTtlMs),this.serverInstallDir=this.getModuleDir(),this.tokenDirPath=this.serverInstallDir,this.authFlow=new a.ExternalAuthFlow(e,this.remoteServerUrl),this.logger.info("TokenManager initialized",{workingDir:t,serverInstallDir:this.serverInstallDir,tokenDirPath:this.tokenDirPath,allowInteractiveAuth:this.allowInteractiveAuth,cacheTtlMs:this.CACHE_TTL_MS}),this.generateEncryptionKey()}getModuleDir(){const e=__dirname||process.cwd(),t=i.default.homedir()
|
|
50
|
+
return e.includes("node_modules")?o.default.join(t,".alchemy_lab","tokens"):o.default.join(t,".alchemy_lab","tokens-dev")}async initialize(){return this.initPromise||(this.initPromise=this.ensureTokenDirectoryExists().then((()=>{this.initialized=!0,this.logger.info("TokenManager initialization completed")})).catch((e=>{throw this.logger.error("TokenManager initialization failed",{error:e instanceof Error?e.stack:e+""}),Error("TokenManager initialization failed")}))),this.initPromise}getStableMachineId(){try{const e=t(857),r=o.default.join(this.serverInstallDir,".machine-id")
|
|
51
51
|
try{const e=t(9896).readFileSync(r,"utf8").trim()
|
|
52
52
|
if(e&&e.length>0)return e}catch{}const s=e.platform(),i=e.arch(),a=e.homedir(),c=e.cpus(),l=`${s}-${i}-${c&&c.length>0?c[0].model:"unknown"}-${a}`,d=n.default.createHash("sha256").update(l).digest("hex")
|
|
53
|
-
try{t(9896).writeFileSync(r,d,"utf8"),this.logger.debug("Created new machine ID file",{path:r})}catch(e){this.logger.warn("Could not persist machine ID, will regenerate on restart",{error:e instanceof Error?e.message:e+""})}return d}catch(e){return this.logger.error("Error getting stable machine ID",{error:(0,
|
|
54
|
-
this.encryptionKey=n.default.createHash("sha256").update(o).digest(),this.logger.debug("Generated encryption key using stable machine ID")}catch(e){this.logger.error("Error generating encryption key",{error:(0,
|
|
55
|
-
return n.default.createHash("sha256").update(o).digest()}catch(e){return this.logger.error("Error generating legacy encryption key",{error:(0,
|
|
53
|
+
try{t(9896).writeFileSync(r,d,"utf8"),this.logger.debug("Created new machine ID file",{path:r})}catch(e){this.logger.warn("Could not persist machine ID, will regenerate on restart",{error:e instanceof Error?e.message:e+""})}return d}catch(e){return this.logger.error("Error getting stable machine ID",{error:(0,c.getErrorMessage)(e)}),n.default.randomBytes(32).toString("hex")}}generateEncryptionKey(){try{const e=t(857).platform(),r=this.getStableMachineId(),o=`${l}-${e}-${r}`
|
|
54
|
+
this.encryptionKey=n.default.createHash("sha256").update(o).digest(),this.logger.debug("Generated encryption key using stable machine ID")}catch(e){this.logger.error("Error generating encryption key",{error:(0,c.getErrorMessage)(e)}),this.encryptionKey=Buffer.from(l.repeat(4),"utf8")}}generateLegacyEncryptionKey(){try{const e=t(857).platform(),r=t(857).hostname(),o=`${l}-${e}-${r}`
|
|
55
|
+
return n.default.createHash("sha256").update(o).digest()}catch(e){return this.logger.error("Error generating legacy encryption key",{error:(0,c.getErrorMessage)(e)}),Buffer.from(l.repeat(4),"utf8")}}async ensureTokenDirectoryExists(){try{await s.default.mkdir(this.tokenDirPath,{recursive:!0}),this.logger.debug("Token directory ensured exists",{path:this.tokenDirPath})}catch(e){throw this.logger.error("Error creating token directory",{path:this.tokenDirPath,error:(0,c.getErrorMessage)(e)}),Error("Error creating token directory: "+this.tokenDirPath)}}setProjectId(e){this.projectId=e,this.logger.info("Set project ID for token manager",{projectId:e})}getAccountKey(){return"default-user-"+n.default.createHash("sha256").update(this.remoteServerUrl).digest("hex").substring(0,8)}getTokenFilePath(e){const t=e.replace(/[^a-zA-Z0-9]/g,"_")
|
|
56
56
|
return o.default.join(this.tokenDirPath,t+".token")}invalidateCache(){this.logger.debug("Invalidating all token caches"),this.TOKEN_CACHE.manualToken=null,this.TOKEN_CACHE.encryptedToken=null,this.TOKEN_CACHE.hasValidToken=null}async checkForManualToken(){const e=Date.now(),t=o.default.join(this.tokenDirPath,"manual-token.json")
|
|
57
57
|
if(this.TOKEN_CACHE.manualToken&&e<this.TOKEN_CACHE.manualToken.expiresAt){const t=this.TOKEN_CACHE.manualToken
|
|
58
58
|
return this.logger.debug("Using cached manual token check result",{ageSecs:Math.round((e-t.timestamp)/1e3),exists:t.exists,hasToken:null!==t.value}),t.exists&&t.value?t.value:null}this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Checking for manual token file",{tokenFilePath:t})
|
|
59
59
|
try{let r=!1,o=null
|
|
60
60
|
try{await s.default.stat(t),r=!0,this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Manual token file exists")}catch{this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Manual token file does not exist")}if(r)try{const r=await s.default.readFile(t,"utf8"),n=JSON.parse(r)
|
|
61
|
-
new Date(n.expiresAt)>new Date?(o=n,this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Valid manual token found",{username:o.username,expiresAt:o.expiresAt})):this.logger.debug("Manual token has expired",{expiresAt:n.expiresAt})}catch(e){this.logger.error("Error parsing manual token file",{error:e instanceof Error?e.message:e+""})}return this.TOKEN_CACHE.manualToken={value:o,timestamp:e,expiresAt:e+this.CACHE_TTL_MS,lastCheck:e,exists:r},o}catch(e){return this.logger.error("Error checking for manual token",{error:(0,
|
|
61
|
+
new Date(n.expiresAt)>new Date?(o=n,this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Valid manual token found",{username:o.username,expiresAt:o.expiresAt})):this.logger.debug("Manual token has expired",{expiresAt:n.expiresAt})}catch(e){this.logger.error("Error parsing manual token file",{error:e instanceof Error?e.message:e+""})}return this.TOKEN_CACHE.manualToken={value:o,timestamp:e,expiresAt:e+this.CACHE_TTL_MS,lastCheck:e,exists:r},o}catch(e){return this.logger.error("Error checking for manual token",{error:(0,c.getErrorMessage)(e)}),null}}encrypt(e){if(!this.encryptionKey)throw Error("Encryption key not available")
|
|
62
62
|
const t=n.default.randomBytes(16),r=n.default.createCipheriv("aes-256-cbc",this.encryptionKey,t)
|
|
63
63
|
let o=r.update(e,"utf8","hex")
|
|
64
64
|
return o+=r.final("hex"),`${t.toString("hex")}:${o}`}decrypt(e){if(!this.encryptionKey)throw Error("Encryption key not available")
|
|
@@ -72,7 +72,7 @@ const s=Buffer.from(r,"hex"),i=n.default.createDecipheriv("aes-256-cbc",t,s)
|
|
|
72
72
|
let a=i.update(o,"hex","utf8")
|
|
73
73
|
return a+=i.final("utf8"),a}catch(e){throw e}}async storeToken(e){!this.initPromise&&this.initialized?await this.initPromise:this.initialized||await this.initialize()
|
|
74
74
|
try{const t=this.getAccountKey(),r=this.getTokenFilePath(t),o=JSON.stringify(e),n=this.encrypt(o)
|
|
75
|
-
await s.default.writeFile(r,n,"utf8"),this.logger.info("Token stored securely",{username:e.username,expiresAt:e.expiresAt}),this.invalidateCache()}catch(e){throw this.logger.error("Error storing token",{error:(0,
|
|
75
|
+
await s.default.writeFile(r,n,"utf8"),this.logger.info("Token stored securely",{username:e.username,expiresAt:e.expiresAt}),this.invalidateCache()}catch(e){throw this.logger.error("Error storing token",{error:(0,c.getErrorMessage)(e)}),Error("Failed to store token securely")}}async getToken(){!this.initPromise&&this.initialized?await this.initPromise:this.initialized||await this.initialize()
|
|
76
76
|
try{if(this.hasPendingAuth()&&this.tempAuthCode){const e=await this.checkAuthStatus(this.tempAuthCode)
|
|
77
77
|
if(e)return e}const e=await this.checkForManualToken()
|
|
78
78
|
if(e)return e
|
|
@@ -80,32 +80,32 @@ const t=Date.now()
|
|
|
80
80
|
if(this.TOKEN_CACHE.encryptedToken&&t<this.TOKEN_CACHE.encryptedToken.expiresAt){const e=this.TOKEN_CACHE.encryptedToken
|
|
81
81
|
return this.logger.debug("Using cached encrypted token",{ageSecs:Math.round((t-e.timestamp)/1e3),hasToken:null!==e.value}),e.value}const r=this.getAccountKey(),o=this.getTokenFilePath(r)
|
|
82
82
|
try{await s.default.stat(o)}catch{return this.logger.debug("No encrypted token file found"),this.TOKEN_CACHE.encryptedToken={value:null,timestamp:t,expiresAt:t+this.CACHE_TTL_MS,lastCheck:t,exists:!1},this.allowInteractiveAuth&&!this.hasPendingAuth()?(this.logger.debug("No stored token found, starting authentication flow"),this.startAuthenticationFlow().catch((e=>{this.logger.error("Failed to start authentication flow",{error:e instanceof Error?e.message:e+""})}))):this.logger.debug("Interactive authentication disabled or already pending"),null}try{const e=await s.default.readFile(o,"utf8"),t=this.decrypt(e),r=JSON.parse(t),n=new Date(r.expiresAt),i=new Date,a=n.getTime()-i.getTime(),c=a/36e5
|
|
83
|
-
return this.logger.info("Token expiration check details",{tokenExpiresAt:r.expiresAt,currentTime:i.toISOString(),isExpired:i>=n,timeUntilExpiryMs:a,timeUntilExpiryHours:Math.round(100*c)/100,username:r.username,systemTimezone:Intl.DateTimeFormat().resolvedOptions().timeZone}),n>i?(this.logger.info("Retrieved valid token from storage",{username:r.username,expiresAt:r.expiresAt}),this.TOKEN_CACHE.encryptedToken={value:r,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!0},r):(this.logger.info("Stored token has expired",{expiresAt:r.expiresAt,expiredByMs:i.getTime()-n.getTime(),expiredByHours:Math.round((i.getTime()-n.getTime())/36e5*100)/100}),this.TOKEN_CACHE.encryptedToken={value:null,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!1},null)}catch(e){this.logger.error("Error reading/decrypting token file",{error:(0,
|
|
83
|
+
return this.logger.info("Token expiration check details",{tokenExpiresAt:r.expiresAt,currentTime:i.toISOString(),isExpired:i>=n,timeUntilExpiryMs:a,timeUntilExpiryHours:Math.round(100*c)/100,username:r.username,systemTimezone:Intl.DateTimeFormat().resolvedOptions().timeZone}),n>i?(this.logger.info("Retrieved valid token from storage",{username:r.username,expiresAt:r.expiresAt}),this.TOKEN_CACHE.encryptedToken={value:r,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!0},r):(this.logger.info("Stored token has expired",{expiresAt:r.expiresAt,expiredByMs:i.getTime()-n.getTime(),expiredByHours:Math.round((i.getTime()-n.getTime())/36e5*100)/100}),this.TOKEN_CACHE.encryptedToken={value:null,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!1},null)}catch(e){this.logger.error("Error reading/decrypting token file",{error:(0,c.getErrorMessage)(e),path:o})
|
|
84
84
|
try{const e=o+".backup"
|
|
85
|
-
await s.default.rename(o,e),this.logger.info("Moved corrupted token file to backup",{from:o,to:e})}catch(e){this.logger.error("Error creating backup of corrupted token file",{error:e instanceof Error?e.message:e+""})}return this.TOKEN_CACHE.encryptedToken={value:null,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!1},null}}catch(e){return this.logger.error("Error retrieving token",{error:(0,
|
|
85
|
+
await s.default.rename(o,e),this.logger.info("Moved corrupted token file to backup",{from:o,to:e})}catch(e){this.logger.error("Error creating backup of corrupted token file",{error:e instanceof Error?e.message:e+""})}return this.TOKEN_CACHE.encryptedToken={value:null,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!1},null}}catch(e){return this.logger.error("Error retrieving token",{error:(0,c.getErrorMessage)(e)}),null}}async removeToken(){!this.initPromise&&this.initialized?await this.initPromise:this.initialized||await this.initialize()
|
|
86
86
|
try{const e=this.getAccountKey(),t=this.getTokenFilePath(e)
|
|
87
|
-
try{await s.default.stat(t)}catch{return this.logger.info("No token file to remove"),!1}return await s.default.unlink(t),this.logger.info("Token removed from secure storage"),this.invalidateCache(),!0}catch(e){return this.logger.error("Error removing token",{error:(0,
|
|
87
|
+
try{await s.default.stat(t)}catch{return this.logger.info("No token file to remove"),!1}return await s.default.unlink(t),this.logger.info("Token removed from secure storage"),this.invalidateCache(),!0}catch(e){return this.logger.error("Error removing token",{error:(0,c.getErrorMessage)(e)}),!1}}async hasValidToken(){const e=Date.now()
|
|
88
88
|
if(this.TOKEN_CACHE.hasValidToken&&e<this.TOKEN_CACHE.hasValidToken.expiresAt){const t=this.TOKEN_CACHE.hasValidToken
|
|
89
89
|
return this.logger.debug("Using cached hasValidToken result",{ageSecs:Math.round((e-t.timestamp)/1e3),hasToken:t.value}),t.value}this.logger.debug("Checking if valid token exists (cache miss or expired)")
|
|
90
90
|
const t=null!==await this.getToken()
|
|
91
91
|
return this.TOKEN_CACHE.hasValidToken={value:t,timestamp:e,expiresAt:e+this.CACHE_TTL_MS,lastCheck:e,exists:t},t}getTokenDirectoryPath(){return this.tokenDirPath}getManualTokenFilePath(){return o.default.join(this.tokenDirPath,"manual-token.json")}logTokenLocationInstructions(){const e=this.getManualTokenFilePath()
|
|
92
92
|
console.log("========================================"),console.log("To use MCP server, you need to authenticate."),console.log("Token file location: "+e),console.log("You can either:"),console.log('\n1. Use the authenticate tool (recommended)\n2. Manually place a token file at the above location\n3. Set up automatic authentication flow\n\nFor manual token placement, create a JSON file with:\n{\n "token": "your_token_here",\n "userId": "your_user_id",\n "username": "your_username", \n "role": "user",\n "expiresAt": "2025-12-31T23:59:59.000Z"\n}\n'),console.log("For more information, see the authentication documentation."),console.log("========================================")}async startAuthenticationFlow(){this.pendingAuthCheck=!0
|
|
93
93
|
try{const{tempCode:e,authUrl:t}=await this.authFlow.startAuthFlow()
|
|
94
|
-
return this.tempAuthCode=e,this.logger.info("Started external authentication flow",{tempCode:e,authUrl:t}),this.authFlow.startPolling(e,(async t=>{this.logger.info("Authentication flow completed",{tempCode:e.substring(0,4)+"***",username:t.username}),await this.storeToken(t),this.pendingAuthCheck=!1,this.tempAuthCode=null,this.invalidateCache(),console.log("Authentication successful!"),console.log(`Welcome, ${t.username}!`),console.log("You can now use all MCP tools and features."),console.log("========================================")}),(()=>{this.logger.info("Authentication flow timed out",{tempCode:e.substring(0,4)+"***"}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow timed out."),console.log("Please try again or manually place a token file."),console.log("Token file location:"),console.log(""+this.getManualTokenFilePath()),console.log("========================================")}),(t=>{this.logger.error("Authentication flow error",{tempCode:e.substring(0,4)+"***",error:t}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow error."),console.log("Error: "+t),console.log("Please try again or manually place a token file."),console.log(""+this.getManualTokenFilePath()),console.log("========================================")})),{tempCode:e,authUrl:t}}catch(e){throw this.logger.error("Error starting authentication flow",{error:(0,
|
|
94
|
+
return this.tempAuthCode=e,this.logger.info("Started external authentication flow",{tempCode:e,authUrl:t}),this.authFlow.startPolling(e,(async t=>{this.logger.info("Authentication flow completed",{tempCode:e.substring(0,4)+"***",username:t.username}),await this.storeToken(t),this.pendingAuthCheck=!1,this.tempAuthCode=null,this.invalidateCache(),console.log("Authentication successful!"),console.log(`Welcome, ${t.username}!`),console.log("You can now use all MCP tools and features."),console.log("========================================")}),(()=>{this.logger.info("Authentication flow timed out",{tempCode:e.substring(0,4)+"***"}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow timed out."),console.log("Please try again or manually place a token file."),console.log("Token file location:"),console.log(""+this.getManualTokenFilePath()),console.log("========================================")}),(t=>{this.logger.error("Authentication flow error",{tempCode:e.substring(0,4)+"***",error:t}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow error."),console.log("Error: "+t),console.log("Please try again or manually place a token file."),console.log(""+this.getManualTokenFilePath()),console.log("========================================")})),{tempCode:e,authUrl:t}}catch(e){throw this.logger.error("Error starting authentication flow",{error:(0,c.getErrorMessage)(e)}),this.pendingAuthCheck=!1,this.tempAuthCode=null,e}}hasPendingAuth(){return this.pendingAuthCheck||!!this.tempAuthCode}getAuthStatus(){return this.hasPendingAuth()?{hasToken:!1,isPendingAuth:!0,tempCode:this.tempAuthCode,authUrl:this.authFlow.getAuthUrl(this.tempAuthCode)}:{hasToken:!1,isPendingAuth:!1}}async checkAuthStatus(e){if(!e)return null
|
|
95
95
|
try{const t=this.authFlow.getPollingStatus()
|
|
96
|
-
if(t.status===
|
|
97
|
-
if(t.status===
|
|
96
|
+
if(t.status===a.PollingStatus.AUTHENTICATED&&t.tokenInfo)return this.logger.info("Authentication polling has token available",{tempCode:e.substring(0,4)+"***",username:t.tokenInfo.username}),t.tokenInfo
|
|
97
|
+
if(t.status===a.PollingStatus.ERROR||t.status===a.PollingStatus.TIMEOUT)return this.pendingAuthCheck=!1,this.tempAuthCode=null,null
|
|
98
98
|
const r=this.authFlow.getPollingUrl(e),o=await fetch(r)
|
|
99
99
|
if(o.ok){const e=await o.json()
|
|
100
100
|
if(e.authenticated&&e.token){const t=this.authFlow.parseTokenResponse(e)
|
|
101
|
-
if(t)return await this.storeToken(t),this.pendingAuthCheck=!1,this.tempAuthCode=null,this.invalidateCache(),t}}return null}catch(e){return this.logger.error("Error checking authentication status",{error:(0,
|
|
101
|
+
if(t)return await this.storeToken(t),this.pendingAuthCheck=!1,this.tempAuthCode=null,this.invalidateCache(),t}}return null}catch(e){return this.logger.error("Error checking authentication status",{error:(0,c.getErrorMessage)(e)}),this.pendingAuthCheck=!1,this.tempAuthCode=null,null}}async validateTokenWithRemoteServer(){this.logger.debug("Validating stored token with remote server")
|
|
102
102
|
try{const e=await this.getToken()
|
|
103
103
|
if(!e)return this.logger.debug("No token available for validation"),!1
|
|
104
104
|
const t=this.remoteServerUrl+"/auth/validate"
|
|
105
105
|
this.logger.debug("Making token validation request",{validationUrl:t})
|
|
106
106
|
const r=await fetch(t,{method:"GET",headers:{Authorization:"Bearer "+e.token,"Content-Type":"application/json"},signal:AbortSignal.timeout(1e4)})
|
|
107
107
|
if(r.ok){const e=await r.json()
|
|
108
|
-
return this.logger.info("Token validation successful",{status:e.status||"unknown",username:e.username||"unknown"}),!0}return this.logger.info("Token validation failed - token may be invalid or expired",{status:r.status,statusText:r.statusText}),this.invalidateCache(),!1}catch(e){return e instanceof Error&&"AbortError"===e.name?(this.logger.info("Token validation request timed out - assuming offline or server unavailable"),!0):(this.logger.info("Token validation request failed",{error:(0,
|
|
108
|
+
return this.logger.info("Token validation successful",{status:e.status||"unknown",username:e.username||"unknown"}),!0}return this.logger.info("Token validation failed - token may be invalid or expired",{status:r.status,statusText:r.statusText}),this.invalidateCache(),!1}catch(e){return e instanceof Error&&"AbortError"===e.name?(this.logger.info("Token validation request timed out - assuming offline or server unavailable"),!0):(this.logger.info("Token validation request failed",{error:(0,c.getErrorMessage)(e)}),!0)}}async hasValidAndWorkingToken(){return!!await this.hasValidToken()&&await this.validateTokenWithRemoteServer()}async isGoogleOAuthAvailable(){return await this.authFlow.isGoogleOAuthAvailable()}async startGoogleOAuthFlow(){return await this.authFlow.startGoogleOAuthFlow()}async startGoogleOAuthPolling(e,t,r,o,s){return await this.authFlow.startGoogleOAuthPolling(e,t,r,o,s)}}},1943(module){module.exports=require("fs/promises")},2096(e,exports,t){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
109
109
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.ProjectConfigService=void 0
|
|
110
110
|
const o=r(t(1943)),s=r(t(6928)),n=r(t(857)),i=t(4139),a=t(8824)
|
|
111
111
|
exports.ProjectConfigService=class{constructor(e){this.logger=e.child({component:"project-config"})}async readProjectId(e){try{const t=s.default.join(e,i.ALCHEMY_DIR_NAME,"project.json"),r=await o.default.readFile(t,"utf-8"),n=JSON.parse(r)
|
|
@@ -124,10 +124,10 @@ try{const e=await o.default.readFile(i,"utf-8")
|
|
|
124
124
|
c=JSON.parse(e)}catch(e){}c[e]={preset:t,disabledTools:r||[],allowedAlchemyTools:n||[],updatedAt:(new Date).toISOString()},await o.default.writeFile(i,JSON.stringify(c,null,2),"utf-8"),this.logger.info("User tool preferences saved",{clientType:e,prefsFile:i})}}},2366(e,exports,t){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
125
125
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
|
|
126
126
|
const o=r(t(7252)),s=r(t(8577)),n=t(4192),i=t(1152),a=r(t(6928)),c=r(t(9896)),l=r(t(857)),d=t(5607),u=t(8984),g=t(8524),h=t(8824),p=t(2096),f=t(7210),m=t(5317)
|
|
127
|
-
class y{updateAuthToken(e){this.options.authToken=e,this.apiClient.authToken=e,this.configurer.authToken=e}constructor(e,t){this.directReportDedup=new Map,this.settingsSyncCounter=0,this.isPolling=!1,this.isProcessingAction=!1,this.openCodeUrlCache=null,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.cachedProjectConfig=null,this.logger=e,this.options=t,this.port=t.port||3456,this.startTime=new Date
|
|
127
|
+
class y{updateAuthToken(e){this.options.authToken=e,this.apiClient.authToken=e,this.configurer.authToken=e}constructor(e,t){this.directReportDedup=new Map,this.settingsSyncCounter=0,this.isPolling=!1,this.isProcessingAction=!1,this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.logger=e,this.options=t,this.port=t.port||3456,this.startTime=new Date
|
|
128
128
|
const r=(0,i.createMetricsCollector)(!1)
|
|
129
129
|
this.apiClient=new n.BaseApiClient(t.remoteServerUrl,e,r,t.authToken),this.configurer=new u.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.cliBridge=new g.CLIBridgeWebSocket(e),(0,g.setCLIBridge)(this.cliBridge),this.hookExecutor=new f.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,o.default)()
|
|
130
|
-
return e.use((0,s.default)()),e.use(o.default.json({limit:"
|
|
130
|
+
return e.use((0,s.default)()),e.use(o.default.json({limit:"10mb"})),e.get("/health",this.handleHealth.bind(this)),e.get("/status",this.handleStatus.bind(this)),e.get("/opencode",this.handleOpenCodeConfig.bind(this)),e.post("/sync-agents",this.handleSyncAgents.bind(this)),e.post("/api/generate-journal",this.handleGenerateJournal.bind(this)),e.get("/api/event-hooks/by-event/:eventType",this.handleEventHooksProxy.bind(this)),e.post("/api/hook-triggers",this.handleHookTriggersProxy.bind(this)),e.get("/api/hook-iterations/:sessionId/:hookId",this.handleGetHookIteration.bind(this)),e.post("/api/hook-iterations/:sessionId/:hookId/increment",this.handleIncrementHookIteration.bind(this)),e.get("/api/project-settings",((e,t)=>{t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null}})})),e.post("/api/sync-settings",(async(e,t)=>{await this.syncProjectSettings(),t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null}})})),e.get("/api/agents/by-name/:agentName",this.handleGetAgentByName.bind(this)),e.get("/api/sessions/:sessionId/agent-type",this.handleGetSessionAgentType.bind(this)),e.get("/api/sessions/:sessionId/parent",this.handleGetSessionParent.bind(this)),e.get("/api/sessions/:sessionId/ancestors",(async(e,t)=>{try{const{sessionId:r}=e.params,o=this.projectId
|
|
131
131
|
if(!o)return void t.status(400).json({success:!1,error:"No project"})
|
|
132
132
|
const s=await this.apiClient.get(`/api/projects/${o}/cli-sessions/external/opencode/${encodeURIComponent(r)}/ancestors`),n=(s?.data||s||[]).map((e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title})))
|
|
133
133
|
t.json({success:!0,ancestors:n})}catch(e){t.status(500).json({success:!1,error:(0,h.getErrorMessage)(e)})}})),e.post("/api/hook-event",(async(e,t)=>{try{const{eventType:r,sessionId:o,variables:s,timing:n}=e.body
|
|
@@ -136,7 +136,7 @@ const i=await this.hookExecutor.processEvent({eventType:r,sessionId:o||"",variab
|
|
|
136
136
|
t.json({success:!0,data:i})}catch(e){this.logger.error("Hook event processing failed",{error:(0,h.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,h.getErrorMessage)(e)})}})),e.post("/api/direct-report",this.handleDirectReport.bind(this)),e.post("/api/cli-bridge/command",this.handleCLIBridgeCommand.bind(this)),e.get("/api/cli-bridge/status",this.handleCLIBridgeStatus.bind(this)),e.use("/api/opencode",((e,t)=>this.handleOpenCodeProxy(e,t))),e}handleHealth(e,t){t.json({status:"healthy",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),timestamp:(new Date).toISOString()})}handleStatus(e,t){t.json({status:"running",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),startTime:this.startTime.toISOString(),projectId:this.projectId,serverId:this.serverId,isPolling:this.isPolling,port:this.port,timestamp:(new Date).toISOString()})}handleOpenCodeConfig(e,t){const r=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
137
137
|
try{if(!c.default.existsSync(r))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
|
|
138
138
|
const e=c.default.readFileSync(r,"utf-8"),o=JSON.parse(e)
|
|
139
|
-
t.json({port:o.port,pid:o.pid,startedAt:o.startedAt,baseUrl:"http://localhost:"+o.port,directory:this.options.projectPath,proxyEnabled
|
|
139
|
+
t.json({port:o.port,pid:o.pid,startedAt:o.startedAt,baseUrl:"http://localhost:"+o.port,directory:this.options.projectPath,proxyEnabled:!0})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,h.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,h.getErrorMessage)(e)})}}async handleSyncAgents(e,t){try{const e=this.readProjectConfig()
|
|
140
140
|
if(!e?.id)return void t.status(400).json({success:!1,error:"No project configuration found"})
|
|
141
141
|
this.logger.info("Syncing agents from remote",{projectId:e.id})
|
|
142
142
|
const r=await this.configurer.fetchAgentsFromRemote(e.id)
|
|
@@ -163,7 +163,7 @@ o.stdout?.on("data",(s=>{t+=s.toString()
|
|
|
163
163
|
const n=t.match(/listening on http:\/\/[^:]+:(\d+)/)
|
|
164
164
|
n&&(clearTimeout(r),o.unref(),e({port:parseInt(n[1],10),pid:o.pid}))})),o.on("error",(()=>{clearTimeout(r)
|
|
165
165
|
try{o.kill("SIGTERM")}catch{}e(null)}))})),n=await s
|
|
166
|
-
n&&(this.trackChildPid(n.pid),c.default.writeFileSync(e,JSON.stringify({port:n.port,pid:n.pid,startedAt:(new Date).toISOString()},null,2)),this.logger.info("Restarted OpenCode",{port:n.port,pid:n.pid}),l=!0)}}catch(e){this.logger.warn("Could not restart OpenCode",{error:(0,h.getErrorMessage)(e)})}t.json({success:!0,message:`Synced ${i} agent(s) to local files${l?" - OpenCode reloading":""}`,count:i,restarted:l})}catch(e){this.logger.error("Failed to sync agents",{error:(0,h.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,h.getErrorMessage)(e)})}}async handleGenerateJournal(e,t){try{const{requestId:r}=e.body
|
|
166
|
+
n&&(this.trackChildPid(n.pid),c.default.writeFileSync(e,JSON.stringify({port:n.port,pid:n.pid,startedAt:(new Date).toISOString()},null,2)),this.logger.info("Restarted OpenCode",{port:n.port,pid:n.pid}),this.setOpenCodePort(n.port),l=!0)}}catch(e){this.logger.warn("Could not restart OpenCode",{error:(0,h.getErrorMessage)(e)})}t.json({success:!0,message:`Synced ${i} agent(s) to local files${l?" - OpenCode reloading":""}`,count:i,restarted:l})}catch(e){this.logger.error("Failed to sync agents",{error:(0,h.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,h.getErrorMessage)(e)})}}async handleGenerateJournal(e,t){try{const{requestId:r}=e.body
|
|
167
167
|
if(!r)return void t.status(400).json({success:!1,error:"requestId is required"})
|
|
168
168
|
if(!/^req\d+$/.test(r))return void t.status(400).json({success:!1,error:"Invalid requestId format"})
|
|
169
169
|
this.logger.info("Generating journal for request",{requestId:r})
|
|
@@ -210,20 +210,20 @@ if(o?.id)try{const e=await this.apiClient.get(`/api/projects/${o.id}/cli-session
|
|
|
210
210
|
if(this.logger.info("Agent type DB lookup",{sessionId:r,dbAgentType:s?.agentType||null,dbId:s?.id||s?.localId||null}),s?.agentType)return void t.json({success:!0,agentType:s.agentType,source:"db"})}catch(e){this.logger.info("Agent type DB lookup failed",{sessionId:r,error:(0,h.getErrorMessage)(e)})}if(this.cliBridge.isUIConnected())try{const e=await this.cliBridge.sendCommand("get-session-status",{sessionId:r},5e3)
|
|
211
211
|
if(this.logger.info("Agent type bridge lookup",{sessionId:r,bridgeAgentType:e?.data?.agentType||null,bridgeSuccess:e?.success}),e.success&&e.data?.agentType)return void t.json({success:!0,agentType:e.data.agentType,source:"bridge"})}catch(e){this.logger.info("Agent type bridge lookup failed",{sessionId:r,error:(0,h.getErrorMessage)(e)})}this.logger.info("Agent type not found",{sessionId:r}),t.json({success:!0,agentType:null,source:"unknown"})}catch(e){this.logger.debug("Failed to get session agent type",{error:(0,h.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,h.getErrorMessage)(e)})}}async handleGetSessionParent(e,t){try{const{sessionId:r}=e.params,o=this.readProjectConfig()
|
|
212
212
|
if(o?.id)try{const e=await this.apiClient.get(`/api/projects/${o.id}/cli-sessions/resolve/${encodeURIComponent(r)}`),s=e?.data?.parent?.externalSessionId||""
|
|
213
|
-
return void t.json({success:!0,parentSessionId:s})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:r,error:(0,h.getErrorMessage)(e)})}t.json({success:!0,parentSessionId:""})}catch(e){this.logger.debug("Failed to get parent session",{error:(0,h.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,h.getErrorMessage)(e)})}}async handleOpenCodeProxy(e,t){try{
|
|
214
|
-
if(!
|
|
215
|
-
const r
|
|
216
|
-
|
|
217
|
-
if(
|
|
218
|
-
if(!o.ok||!o.body)return void t.status(o.status).json({error:"SSE upstream error"})
|
|
213
|
+
return void t.json({success:!0,parentSessionId:s})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:r,error:(0,h.getErrorMessage)(e)})}t.json({success:!0,parentSessionId:""})}catch(e){this.logger.debug("Failed to get parent session",{error:(0,h.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,h.getErrorMessage)(e)})}}async handleOpenCodeProxy(e,t){try{const r=this.getOpenCodeUrl()
|
|
214
|
+
if(!r)return void t.status(502).json({error:"OpenCode not running"})
|
|
215
|
+
const o=`${r}/${e.path.replace(/^\//,"")}${e.originalUrl.includes("?")?e.originalUrl.substring(e.originalUrl.indexOf("?")):""}`,s=e.headers.accept?.includes("text/event-stream")
|
|
216
|
+
if(s){const r=new AbortController,s=()=>{this.proxyAbortControllers.delete(r)},n=(async()=>{try{const e=await fetch(o,{headers:{Accept:"text/event-stream"},signal:r.signal})
|
|
217
|
+
if(!e.ok||!e.body)return s(),void t.status(e.status).json({error:"SSE upstream error"})
|
|
219
218
|
t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"})
|
|
220
|
-
const
|
|
221
|
-
|
|
219
|
+
const n=e.body.getReader()
|
|
220
|
+
try{for(;;){const{done:e,value:r}=await n.read()
|
|
222
221
|
if(e)break
|
|
223
|
-
t.write(r)}}catch{}t.end()}
|
|
224
|
-
"
|
|
225
|
-
|
|
226
|
-
|
|
222
|
+
t.write(r)}}catch{}t.end()}catch{t.headersSent||t.status(502).json({error:"SSE upstream error"})}finally{s()}})()
|
|
223
|
+
return this.proxyAbortControllers.set(r,n),void e.on("close",(()=>{try{r.abort()}catch{}}))}const n={"Content-Type":e.headers["content-type"]||"application/json"},i={method:e.method,headers:n}
|
|
224
|
+
"GET"!==e.method&&"HEAD"!==e.method&&e.body&&(i.body=JSON.stringify(e.body))
|
|
225
|
+
const a=await fetch(o,i),c=a.headers.get("content-type")||"",l=c.includes("application/json")?await a.json():await a.text()
|
|
226
|
+
t.status(a.status),c&&t.setHeader("Content-Type",c),"string"==typeof l?t.send(l):t.json(l)}catch(r){this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,h.getErrorMessage)(r),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,h.getErrorMessage)(r)})}}async handleGetAgentByName(e,t){try{const r=this.readProjectConfig()
|
|
227
227
|
if(!r?.id)return void t.status(400).json({success:!1,error:"No project configured"})
|
|
228
228
|
const{agentName:o}=e.params,s=await this.apiClient.get(`/api/projects/${r.id}/agents`),n=(s?.data||s||[]).find((e=>e.name===o))
|
|
229
229
|
if(!n)return void t.status(404).json({success:!1,error:`Agent '${o}' not found`})
|
|
@@ -231,7 +231,10 @@ t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by n
|
|
|
231
231
|
const e=a.default.join(this.options.projectPath,".alchemy_lab","project.json")
|
|
232
232
|
try{if(!c.default.existsSync(e))return this.cachedProjectConfig={config:null,timestamp:Date.now()},null
|
|
233
233
|
const t=c.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
234
|
-
return this.cachedProjectConfig={config:r,timestamp:Date.now()},r}catch(e){return this.logger.error("Failed to read project config",{error:(0,h.getErrorMessage)(e)}),this.cachedProjectConfig={config:null,timestamp:Date.now()},null}}
|
|
234
|
+
return this.cachedProjectConfig={config:r,timestamp:Date.now()},r}catch(e){return this.logger.error("Failed to read project config",{error:(0,h.getErrorMessage)(e)}),this.cachedProjectConfig={config:null,timestamp:Date.now()},null}}setOpenCodePort(e){this.openCodePort=e
|
|
235
|
+
const t="http://127.0.0.1:"+e
|
|
236
|
+
this.openCodeUrlCache={url:t,timestamp:Date.now()},this.logger.info("OpenCode port set directly",{port:e})}getOpenCodeUrl(){if(this.openCodePort)return"http://127.0.0.1:"+this.openCodePort
|
|
237
|
+
if(this.openCodeUrlCache&&3e4>Date.now()-this.openCodeUrlCache.timestamp)return this.openCodeUrlCache.url
|
|
235
238
|
const e=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
236
239
|
try{if(!c.default.existsSync(e))return null
|
|
237
240
|
const t=c.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
@@ -292,16 +295,18 @@ if(!t)return
|
|
|
292
295
|
this.logger.debug("SSE hook: session_idle",{sessionId:t}),await this.hookExecutor.processEvent({eventType:"session_idle",sessionId:t,variables:{sessionId:t}})}else if("session.created"===t){const t=e?.properties?.info?.id||e?.info?.id||""
|
|
293
296
|
if(!t)return
|
|
294
297
|
setTimeout((async()=>{this.logger.debug("SSE hook: session_created (delayed)",{sessionId:t}),await this.hookExecutor.processEvent({eventType:"session_created",sessionId:t,variables:{sessionId:t}})}),500)}}async flushSSEEvents(){if(0===this.sseEventBuffer.length||!this.projectId)return
|
|
298
|
+
if(!this.hasRemoteEventConsumers)return void(this.sseEventBuffer.length>500&&this.sseEventBuffer.splice(0,this.sseEventBuffer.length-500))
|
|
295
299
|
const e=this.sseEventBuffer.splice(0)
|
|
296
|
-
try{await this.apiClient.post(`/api/projects/${this.projectId}/opencode-events`,{events:e})}catch(t){this.logger.debug("SSE event flush failed",{error:(0,h.getErrorMessage)(t),count:e.length}),this.sseEventBuffer.unshift(...e),this.sseEventBuffer.length>500&&this.sseEventBuffer.splice(0,this.sseEventBuffer.length-500)}}startPolling(){this.projectId&&(this.isPolling=!0,this.logger.info("Starting action polling",{projectId:this.projectId}),this.
|
|
297
|
-
if(
|
|
300
|
+
try{await this.apiClient.post(`/api/projects/${this.projectId}/opencode-events`,{events:e})}catch(t){this.logger.debug("SSE event flush failed",{error:(0,h.getErrorMessage)(t),count:e.length}),this.sseEventBuffer.unshift(...e),this.sseEventBuffer.length>500&&this.sseEventBuffer.splice(0,this.sseEventBuffer.length-500)}}startPolling(){this.projectId&&(this.isPolling=!0,this.pollAbortController=new AbortController,this.logger.info("Starting action long-polling",{projectId:this.projectId}),this.longPollLoop())}async longPollLoop(){for(;this.isPolling;)try{await this.pollOnce()}catch(e){if(!this.isPolling)return
|
|
301
|
+
this.logger.debug("Long-poll error, retrying in 5s",{error:(0,h.getErrorMessage)(e)}),await new Promise((e=>setTimeout(e,5e3)))}}async pollOnce(){if(this.isPolling&&this.projectId)try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/actions/pending?timeout=30000`)
|
|
302
|
+
if(e.success&&e.data&&void 0!==e.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=e.data.hasRemoteEventConsumers),e.success&&e.data?.settings){const{defaultModel:t,autoJournal:r}=e.data.settings
|
|
298
303
|
if(void 0!==t&&(this.cachedDefaultModel=t,this.hookExecutor.setDefaultModel(t)),void 0!==r&&r!==this.cachedAutoJournal){this.logger.info("autoJournal setting changed",{oldValue:this.cachedAutoJournal,newValue:r})
|
|
299
304
|
const e=new p.ProjectConfigService(this.logger)
|
|
300
305
|
await e.saveMultiAgentConfig(this.options.projectPath,void 0,r),this.cachedAutoJournal=r}}if(e.success&&e.data?.actions?.length>0){const t=[],r=[]
|
|
301
306
|
for(const o of e.data.actions)"opencode-proxy"===o.type||"run-tests"===o.type?t.push(o):r.push(o)
|
|
302
307
|
for(const e of t)this.processAction(e).catch((t=>this.logger.debug("Proxy action failed",{actionId:e.id,error:(0,h.getErrorMessage)(t)})))
|
|
303
308
|
if(r.length>0&&!this.isProcessingAction){this.isProcessingAction=!0
|
|
304
|
-
try{for(const e of r)await this.processAction(e)}finally{this.isProcessingAction=!1}}}}catch(e){
|
|
309
|
+
try{for(const e of r)await this.processAction(e)}finally{this.isProcessingAction=!1}}}}catch(e){throw e}}async syncProjectSettings(){try{const e=await this.apiClient.get("/api/projects/"+this.projectId)
|
|
305
310
|
if(e.success&&e.data?.metadata){const{autoJournal:t,defaultModel:r}=e.data.metadata
|
|
306
311
|
if(void 0!==r&&(this.cachedDefaultModel=r,this.hookExecutor.setDefaultModel(r)),void 0!==t&&t!==this.cachedAutoJournal){this.logger.info("autoJournal setting changed, syncing to local config",{oldValue:this.cachedAutoJournal,newValue:t})
|
|
307
312
|
const e=new p.ProjectConfigService(this.logger)
|
|
@@ -329,10 +334,11 @@ const e=encodeURIComponent(t.requestId),[o,s]=await Promise.all([this.apiClient.
|
|
|
329
334
|
return{success:!0,data:{...o.data,tasks:s.data||[]}}}default:return{success:!1,error:"Unknown request action: "+e}}}catch(e){return{success:!1,error:(0,h.getErrorMessage)(e)}}}handleCLIBridgeStatus(e,t){t.json({uiConnected:this.cliBridge.isUIConnected()})}async start(){this.logger.info("Starting AlchemyActionServer",{projectPath:this.options.projectPath,port:this.port})
|
|
330
335
|
for(let e=0;10>e;e++)try{await new Promise(((e,t)=>{try{this.server=this.app.listen(this.port,"127.0.0.1",(()=>{this.logger.info("HTTP server started",{port:this.port}),this.cliBridge.attach(this.server),e()})),this.server.on("error",(e=>{e.code,t(e)}))}catch(e){t(e)}}))
|
|
331
336
|
break}catch(t){if("EADDRINUSE"===t?.code&&9>e){this.logger.info(`Port ${this.port} in use, trying ${this.port+1}`),this.port++
|
|
332
|
-
continue}throw Error(`Port ${this.port} is already in use (tried 10 ports)`)}try{await this.register(),this.startHeartbeat()}catch(e){this.logger.warn("Remote registration failed, continuing in local-only mode",{error:(0,h.getErrorMessage)(e)})}this.startPolling(),this.
|
|
337
|
+
continue}throw Error(`Port ${this.port} is already in use (tried 10 ports)`)}try{await this.register(),this.startHeartbeat()}catch(e){this.logger.warn("Remote registration failed, continuing in local-only mode",{error:(0,h.getErrorMessage)(e)})}this.startPolling(),this.projectId&&this.startSSEEventForwarder()}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,h.getErrorMessage)(t)})}}this.trackedChildPids.clear()
|
|
333
338
|
try{const e=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
334
339
|
if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
335
|
-
if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,h.getErrorMessage)(e)})}}}}catch{}}async stop(){
|
|
340
|
+
if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,h.getErrorMessage)(e)})}}}}catch{}}async stop(){this.logger.info("Stopping AlchemyActionServer...")
|
|
341
|
+
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}if(this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.killTrackedProcesses(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval),this.heartbeatInterval&&clearInterval(this.heartbeatInterval),this.serverId)try{await this.apiClient.post(`/api/servers/${this.serverId}/disconnect`)}catch{}this.cliBridge.close(),await new Promise((e=>{if(this.server){const t=setTimeout((()=>{this.logger.warn("Graceful shutdown timed out after 10s, forcing close"),e()}),1e4)
|
|
336
342
|
this.server.close((()=>{clearTimeout(t),e()}))}else e()})),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=y,y.PROJECT_CONFIG_TTL=3e4},2782(e,exports,t){function r(){return"true"===process.env.ENABLE_DEBUG_TOOLS?.toLowerCase()}function o(e={}){const t={...function(e){const t={}
|
|
337
343
|
if(!r())return t.level=(0,l.isDevBuild)()?c.LogLevel.INFO:c.LogLevel.ERROR,t.logToFile=!1,t
|
|
338
344
|
if("true"===process.env.DEBUG_LOGGING?.toLowerCase()){t.level=c.LogLevel.DEBUG,t.logToFile=!0
|
|
@@ -392,7 +398,7 @@ r>0&&(t=e.substring(0,r+5))}else{const r=e.lastIndexOf("/dist-dev/")
|
|
|
392
398
|
if(r>0)t=e.substring(0,r+9)
|
|
393
399
|
else{const r=e.lastIndexOf("/dist/")
|
|
394
400
|
r>0&&(t=e.substring(0,r+5))}}if(!t){const e=process.cwd()
|
|
395
|
-
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"
|
|
396
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}}
|
|
397
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"}
|
|
398
404
|
const s=o(t(6928))
|
|
@@ -529,24 +535,24 @@ let E
|
|
|
529
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))
|
|
530
536
|
else{const e=await y.get(`/api/projects/${v}/test-processes/active`)
|
|
531
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"}
|
|
532
|
-
const
|
|
533
|
-
if((0,o.existsSync)(
|
|
534
|
-
await e.raw(["reset","--hard",I]),m.info("Reset test worktree to latest branch HEAD",{worktreePath:
|
|
535
|
-
try{await j.raw(["worktree","remove","--force",
|
|
538
|
+
const b=(0,s.join)(w,c,l),k=(0,s.join)(b,u),j=(0,n.simpleGit)(w)
|
|
539
|
+
if((0,o.existsSync)(k))try{const e=(0,n.simpleGit)(k)
|
|
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
|
+
try{await j.raw(["worktree","remove","--force",k])}catch{}}if(!(0,o.existsSync)(k)){(0,o.mkdirSync)(b,{recursive:!0})
|
|
536
542
|
try{if(!(await j.branch()).all.includes(I))return{success:!1,message:`Branch ${I} not found`,error:"Branch not found"}
|
|
537
|
-
await j.raw(["worktree","add","--detach",
|
|
538
|
-
m.info("Test worktree already exists, using as-is",{worktreePath:
|
|
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
|
+
m.info("Test worktree already exists, using as-is",{worktreePath:k})}}const{execSync:T}=await Promise.resolve().then(t.t.bind(t,5317,23))
|
|
539
545
|
for(const e of E){const t=e.config?.setup||[]
|
|
540
546
|
if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length})
|
|
541
|
-
for(const r of t)try{m.info("Setup command",{command:r,worktreePath:
|
|
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=[]
|
|
542
548
|
for(const process of E){let t
|
|
543
549
|
const o=e.testRunIds?.[S.length]
|
|
544
|
-
try{o?(await y.patch(`/api/projects/${v}/test-runs/${o}`,{worktreePath:
|
|
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)})
|
|
545
551
|
continue}const s=r(p,process,0,v,f),n=f?"tester":"auto-tester"
|
|
546
|
-
try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:s,cwd:
|
|
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})
|
|
547
553
|
if(!e.success)throw Error(e.error||"Spawn failed")
|
|
548
|
-
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:
|
|
549
|
-
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:
|
|
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:E.length,worktreePath:k}}}catch(e){const t=(0,i.getErrorMessage)(e)
|
|
550
556
|
return m.error("run-tests action failed",{requestId:u,error:t}),{success:!1,message:"Failed to run tests: "+t,error:t}}}
|
|
551
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
|
|
552
558
|
if(!s)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
@@ -702,7 +708,7 @@ return` ${o.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${s}`}: ${r}`})).join("\n")
|
|
|
702
708
|
let i=`---\ndescription: "${e.description||e.name}"\nmode: "${"utility"===e.mode?"primary":e.mode||"primary"}"\nmodel: "${r}"\n`
|
|
703
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"
|
|
704
710
|
for(const[t,r]of Object.entries(e.opencodePermission))if("object"==typeof r&&null!==r){i+=` ${t}:\n`
|
|
705
|
-
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")
|
|
706
712
|
this.logger.info("Configuring OpenCode",{isDevMode:l,serverName:d,scriptPath:u,projectPath:s})
|
|
707
713
|
const g=c.join(s,"opencode.json")
|
|
708
714
|
let h=null,p=null,f=null,m=null,y=!1,v=!1
|
|
@@ -711,13 +717,13 @@ if(!i){try{const e=await r.readFile(w,"utf-8"),t=JSON.parse(e)
|
|
|
711
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)
|
|
712
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"
|
|
713
719
|
i?.openCodeModel?I.model=i.openCodeModel:p?I.model=p:m&&(I.model=m)
|
|
714
|
-
const
|
|
715
|
-
await r.mkdir(
|
|
716
|
-
const
|
|
720
|
+
const b=c.join(s,".opencode","agent")
|
|
721
|
+
await r.mkdir(b,{recursive:!0})
|
|
722
|
+
const k=E||"anthropic/claude-opus-4-5-20251101"
|
|
717
723
|
let j,T=null,S=null
|
|
718
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})
|
|
719
725
|
for(const e of T){if(!e.enabled)continue
|
|
720
|
-
const t=this.generateAgentMarkdown(e,d,
|
|
726
|
+
const t=this.generateAgentMarkdown(e,d,k,j),o=c.join(b,e.name+".md")
|
|
721
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")
|
|
722
728
|
else this.logger.warn("No projectId provided - agents must be configured via database")
|
|
723
729
|
const P={$schema:"https://opencode.ai/config.json",model:E,mcp:{[d]:{type:"local",command:[n,u,s],enabled:!0}}}
|
|
@@ -728,7 +734,9 @@ for(const[r,s]of Object.entries(e.tools||{})){const e=s,n=r.replace(/-/g,"_")
|
|
|
728
734
|
t[o.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${d}_${n}`]=e}const r=S?.metadata?.bashToolAgents||{}
|
|
729
735
|
e.name in r&&(t.bash=!!r[e.name]),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name)&&void 0===t.task&&(t.task=!1)
|
|
730
736
|
let s=""
|
|
731
|
-
j&&(s+=j.trim()+"\n\n"),e.prompt&&(s+=e.prompt),e.promptAddition&&(s+="\n\n"+e.promptAddition),P.agent[e.name]={tools:t,...s?{prompt:s}:{},...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}
|
|
737
|
+
j&&(s+=j.trim()+"\n\n"),e.prompt&&(s+=e.prompt),e.promptAddition&&(s+="\n\n"+e.promptAddition),P.agent[e.name]={tools:t,...s?{prompt:s}:{},...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}P.agent.general={...P.agent.general,disable:!0},P.agent.explore={...P.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[d+"_search"]:!0,[d+"_read_files"]:!0,[d+"_view_asset"]:!0,[d+"_query_history"]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
|
|
738
|
+
const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${j?j.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`
|
|
739
|
+
await r.writeFile(c.join(b,"explore.md"),e,"utf8"),this.logger.info("Created explore agent override with research prompt"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(P.agent).length})}const A=[]
|
|
732
740
|
if(A.push("file://"+c.join(s,".opencode","plugin","inject-session.js")),A.push("file://"+c.join(s,".opencode","plugin","event-hooks.js")),A.push("file://"+c.join(s,".opencode","plugin","anthropic-auth.js")),A.push("file://"+c.join(s,".opencode","plugin","system-logger.js")),A.length>0&&(P.plugin=A),await r.writeFile(g,JSON.stringify(P,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:g,plugins:A}),v&&!i)try{let e={}
|
|
733
741
|
try{const t=await r.readFile(w,"utf-8")
|
|
734
742
|
e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=C,await r.writeFile(w,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:w}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPlugins:r}=await t.e(939).then(t.t.bind(t,9939,23)),o=await r(s,l??!1,this.logger,e)
|
|
@@ -736,10 +744,10 @@ o.synced.length>0&&this.logger.info("Synced plugins during OpenCode config",{syn
|
|
|
736
744
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.ExternalAuthFlow=exports.PollingStatus=void 0
|
|
737
745
|
const o=r(t(6982)),s=t(8824)
|
|
738
746
|
var n
|
|
739
|
-
!function(e){e.NOT_STARTED="not_started",e.POLLING="polling",e.AUTHENTICATED="authenticated",e.TIMEOUT="timeout",e.ERROR="error"}(n||(exports.PollingStatus=n={})),exports.ExternalAuthFlow=class{constructor(e,t,r=5e3,o=9e5){this.pollingStatus=n.NOT_STARTED,this.pollingStartTime=0,this.pollingIntervalId=null,this.tokenResult=null,this.pollingError=null,this.logger=e.child({component:"auth-flow"}),this.remoteServerUrl=t,this.pollingIntervalMs=r,this.maxPollingTimeMs=o}getAuthUrl(e){let
|
|
740
|
-
|
|
741
|
-
const
|
|
742
|
-
return this.logger.info("Generated auth URL",{url:
|
|
747
|
+
!function(e){e.NOT_STARTED="not_started",e.POLLING="polling",e.AUTHENTICATED="authenticated",e.TIMEOUT="timeout",e.ERROR="error"}(n||(exports.PollingStatus=n={})),exports.ExternalAuthFlow=class{constructor(e,t,r=5e3,o=9e5){this.pollingStatus=n.NOT_STARTED,this.pollingStartTime=0,this.pollingIntervalId=null,this.tokenResult=null,this.pollingError=null,this.logger=e.child({component:"auth-flow"}),this.remoteServerUrl=t,this.pollingIntervalMs=r,this.maxPollingTimeMs=o}getAuthUrl(e){let r=this.remoteServerUrl
|
|
748
|
+
r=r.includes("api.alchemylabcode.com")?r.replace("api.alchemylabcode.com","alchemylabcode.com"):r.match(/localhost:\d+/)?r.replace(/localhost:\d+/,"localhost:3000"):r.replace(/\/\/api\./g,"//")
|
|
749
|
+
const o=t(6928).basename(process.cwd()),s=`${r}/login?mcp_code=${e}&project=${encodeURIComponent(o)}`
|
|
750
|
+
return this.logger.info("Generated auth URL",{url:s,frontendUrl:r,backendUrl:this.remoteServerUrl}),s}getPollingUrl(e){const t=`${this.remoteServerUrl}/auth/check-auth?code=${e}`
|
|
743
751
|
return this.logger.info("Generated polling URL",{url:t}),t}async requestTemporaryCode(){const e=this.remoteServerUrl+"/auth/generate-code"
|
|
744
752
|
this.logger.info("Requesting temporary code from remote server",{url:e})
|
|
745
753
|
try{const t=await fetch(e)
|
|
@@ -786,11 +794,13 @@ if(!s.default.existsSync(t))return null
|
|
|
786
794
|
try{return"http://localhost:"+JSON.parse(s.default.readFileSync(t,"utf-8")).port}catch{return null}}(t.projectPath)
|
|
787
795
|
if(!l)return{success:!1,message:"OpenCode not running (no opencode.json found)"}
|
|
788
796
|
const d=`${l}${i.startsWith("/")?i:"/"+i}`
|
|
789
|
-
try{const e={"Content-Type":"application/json",...c},
|
|
790
|
-
"GET"!==r.toUpperCase()&&"HEAD"!==r.toUpperCase()&&void 0!==a
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
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)}}}
|
|
794
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
|
|
795
805
|
if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
796
806
|
s.info("Starting approve-request action",{requestId:o})
|
|
@@ -814,20 +824,20 @@ for(var a=0;a<s.length;a++)o[s[a]]=1})(require("./"+e.u(t)))}
|
|
|
814
824
|
var i={};(()=>{function t(){const e=[(0,g.join)(__dirname,"..","package.json"),(0,g.join)(__dirname,"..","..","package.json")]
|
|
815
825
|
for(const t of e)try{const e=JSON.parse((0,d.readFileSync)(t,"utf-8"))
|
|
816
826
|
if(e.version)return e.version}catch{}return"unknown"}function r(){const e=t()
|
|
817
|
-
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 --
|
|
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")
|
|
818
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)
|
|
819
829
|
setTimeout((()=>{clearInterval(o),r(!1)}),t)}))}async function a(t){if(process.env.REMOTE_SERVER_URL)return process.env.REMOTE_SERVER_URL
|
|
820
830
|
try{const{createConfigManager:r}=await e.e(60).then(e.t.bind(e,9060,23)),o=r(t)
|
|
821
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}})})
|
|
822
832
|
if(i.ok){const e=await i.json()
|
|
823
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})
|
|
824
|
-
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)
|
|
825
|
-
let
|
|
826
|
-
const b=i.indexOf("--port");-1!==b&&i[b+1]&&(
|
|
827
|
-
const
|
|
828
|
-
if(0===
|
|
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)
|
|
835
|
+
let p
|
|
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)))
|
|
837
|
+
const k=i.filter(((e,t)=>"--port"!==e&&(-1===b||t!==b+1)))
|
|
838
|
+
if(0===k.length)return void await async function(r={}){const i=process.cwd(),l=(0,g.join)(i,E.ALCHEMY_DIR_NAME)
|
|
829
839
|
await(0,u.mkdir)(l,{recursive:!0})
|
|
830
|
-
const p=(0,g.join)(l,"server.log"),{createLogger:
|
|
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})
|
|
831
841
|
await T.initialize()
|
|
832
842
|
let S=await T.getToken()
|
|
833
843
|
if(!S?.token){console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
|
|
@@ -838,12 +848,12 @@ console.log("\u{1f4c1} Initializing AlchemyLab project..."),console.log("")
|
|
|
838
848
|
const n=(0,g.join)(e,E.ALCHEMY_DIR_NAME)
|
|
839
849
|
let i
|
|
840
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")
|
|
841
|
-
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,
|
|
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)
|
|
842
852
|
const A=(0,g.join)(i,E.ALCHEMY_DIR_NAME,"project.json")
|
|
843
853
|
let M
|
|
844
854
|
try{const e=JSON.parse((0,d.readFileSync)(A,"utf-8"))
|
|
845
|
-
if(M=e.id||void 0,!1===e.registeredRemotely&&P){const t=await c(j,P,i,
|
|
846
|
-
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{
|
|
855
|
+
if(M=e.id||void 0,!1===e.registeredRemotely&&P){const t=await c(j,P,i,k,"cli-init-retry")
|
|
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"
|
|
847
857
|
try{const e=(0,g.join)(t,E.ALCHEMY_DIR_NAME,"config.json")
|
|
848
858
|
if((0,d.existsSync)(e)){const t=JSON.parse((0,d.readFileSync)(e,"utf-8"))
|
|
849
859
|
t.opencodeVersion&&(r=t.opencodeVersion)}}catch{}console.log(""),console.log("\u{1f4e6} OpenCode not found in PATH. Installing...")
|
|
@@ -856,30 +866,30 @@ const o=(0,g.join)(process.env.HOME||"/usr/local/bin",".local","bin")
|
|
|
856
866
|
await(0,u.mkdir)(o,{recursive:!0})
|
|
857
867
|
const s=(0,g.join)(o,"opencode")
|
|
858
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)
|
|
859
|
-
const
|
|
860
|
-
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"+
|
|
861
|
-
const
|
|
862
|
-
try{await x.start()
|
|
869
|
+
const _=t()
|
|
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)
|
|
871
|
+
const O=r.port||await(0,I.findAvailablePort)(3456),x=new y.AlchemyActionServer(k,{projectPath:i,remoteServerUrl:j,port:O,authToken:P})
|
|
872
|
+
try{await x.start()
|
|
863
873
|
let t=process.env.FRONTEND_URL
|
|
864
874
|
if(!t)if(j.includes("api.alchemylabcode.com"))t="https://alchemylabcode.com"
|
|
865
875
|
else{try{const e=await fetch(j+"/health")
|
|
866
876
|
if(e.ok){const r=await e.json()
|
|
867
877
|
t=r?.data?.frontendUrl}}catch{}t||(t=j.replace(/:\d+/,":3000"))}console.log(""),console.log(" Action server .... port "+x.getPort())
|
|
868
|
-
try{const e=(0,v.getInstallContext)(),t=new m.MCPClientConfigurer(
|
|
869
|
-
if(o.success&&o.port){o.pid&&x.trackChildPid(o.pid),console.log(" OpenCode serve ... port "+o.port)
|
|
870
|
-
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)),
|
|
871
|
-
console.log(" OpenCode serve ... started")}}catch(e){
|
|
872
|
-
let
|
|
873
|
-
const
|
|
874
|
-
process.on("SIGTERM",
|
|
875
|
-
const{InteractiveShell:
|
|
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})
|
|
879
|
+
if(o.success&&o.port){o.pid&&x.trackChildPid(o.pid),x.setOpenCodePort(o.port),console.log(" OpenCode serve ... port "+o.port)
|
|
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")
|
|
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("")
|
|
882
|
+
let r=!1,o=null
|
|
883
|
+
const a=async()=>{r||(r=!0,o&&clearInterval(o),console.log(""),console.log("Shutting down..."),await x.stop(),process.exit(0))}
|
|
884
|
+
process.on("SIGTERM",a),process.on("SIGINT",a)
|
|
885
|
+
const{InteractiveShell:c}=await e.e(994).then(e.t.bind(e,5994,23)),l=new c({projectPath:i,logger:k,remoteServerUrl:j,authToken:P,projectId:M})
|
|
886
|
+
o=setInterval((async()=>{try{if(await T.hasValidToken())return
|
|
877
887
|
console.log(""),console.log("\u26a0\ufe0f Auth token expired. Opening browser to re-authenticate...")
|
|
878
888
|
const e=await T.startAuthenticationFlow()
|
|
879
889
|
if(console.log("\u{1f310} "+e.authUrl),s(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(T)){const e=await T.getToken()
|
|
880
|
-
e?.token&&(P=e.token,x.updateAuthToken(P)),console.log("\u2705 Re-authenticated successfully.")}else console.log("\u274c Re-authentication timed out. Some features may not work until you restart.")}catch(e){
|
|
881
|
-
const
|
|
882
|
-
switch("--help"!==
|
|
890
|
+
e?.token&&(P=e.token,x.updateAuthToken(P)),console.log("\u2705 Re-authenticated successfully.")}else console.log("\u274c Re-authentication timed out. Some features may not work until you restart.")}catch(e){k.warn("Token watchdog check failed",{error:(0,C.getErrorMessage)(e)})}}),3e5),await l.start(a)}catch(e){k.error("Failed to start server",{error:(0,C.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,C.getErrorMessage)(e)),process.exit(1)}}({port:p})
|
|
891
|
+
const j=k[0].toLowerCase()
|
|
892
|
+
switch("--help"!==j&&"-h"!==j||(r(),process.exit(0)),"--version"!==j&&"-v"!==j||(console.log("alchemy v"+t()),process.exit(0)),j){case"serve":await async function(e={}){const r=process.cwd()
|
|
883
893
|
o(r)||(console.log("\u{1f4c1} Current directory is not an AlchemyLab project."),console.log(""),console.log("\u{1f4a1} Run `alchemy` to initialize this project."),console.log(""),process.exit(1))
|
|
884
894
|
const s=(0,l.createEnvLogger)({serviceName:"alchemy-server",console:!0,baseDir:r}),n=t()
|
|
885
895
|
console.log(`\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551 AlchemyLab Server v${n.padEnd(10)} \u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n`),console.log("\u{1f4c1} Project: "+r)
|
|
@@ -887,13 +897,13 @@ const i=await a(s),c=new f.TokenManager(s,r,{remoteServerUrl:i,allowInteractiveA
|
|
|
887
897
|
await c.initialize()
|
|
888
898
|
const d=await c.getToken(),u=d?.token
|
|
889
899
|
u||console.log("\u26a0\ufe0f No auth token. Some features may be limited. Run `alchemy` to authenticate.")
|
|
890
|
-
const g=e.port||await(0,I.findAvailablePort)(3456),h=new y.AlchemyActionServer(s,{projectPath:r,remoteServerUrl:i,port:g,authToken:u
|
|
891
|
-
try{await h.start(),console.log(""),console.log("\u2705 Action server running!"),
|
|
892
|
-
let
|
|
893
|
-
const
|
|
894
|
-
process.on("SIGTERM",
|
|
900
|
+
const g=e.port||await(0,I.findAvailablePort)(3456),h=new y.AlchemyActionServer(s,{projectPath:r,remoteServerUrl:i,port:g,authToken:u})
|
|
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("")
|
|
902
|
+
let e=!1
|
|
903
|
+
const t=async()=>{e||(e=!0,console.log(""),console.log("Shutting down action server..."),await h.stop(),process.exit(0))}
|
|
904
|
+
process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){s.error("Failed to start server",{error:(0,C.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,C.getErrorMessage)(e)),process.exit(1)}}({port:p})
|
|
895
905
|
break
|
|
896
906
|
case"logout":await async function(){const e=process.cwd(),t=(0,l.createEnvLogger)({serviceName:"alchemy-cli",console:!1,baseDir:e}),r=await a(t),o=new f.TokenManager(t,e,{remoteServerUrl:r,allowInteractiveAuth:!1})
|
|
897
907
|
await o.initialize(),await o.hasValidToken()?await o.removeToken()?console.log("Logged out successfully."):(console.error("Failed to remove auth token."),process.exit(1)):console.log("Not currently logged in.")}()
|
|
898
908
|
break
|
|
899
|
-
default:console.error("\u274c Unknown command: "+
|
|
909
|
+
default:console.error("\u274c Unknown command: "+j),console.log(""),r(),process.exit(1)}})().catch((e=>{console.error("\u{1f4a5} Unexpected error:",e),process.exit(1)}))})(),module.exports=i})()
|