@agenthippo-ai/agenthippo 1.110.28 → 1.110.30
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/out/analytics/service.js +1 -1
- package/out/auth/provider.js +1 -1
- package/out/auth/signInFlow.js +1 -1
- package/out/cli/index.js +11 -11
- package/package.json +2 -2
package/out/analytics/service.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*! Copyright (c) 2026 AgentHippo.ai. All rights reserved. Proprietary; see LICENSE. */
|
|
2
|
-
"use strict";var __createBinding=this&&this.__createBinding||(Object.create?(function(l,t,e,i){i===void 0&&(i=e);var s=Object.getOwnPropertyDescriptor(t,e);(!s||("get"in s?!t.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return t[e]}}),Object.defineProperty(l,i,s)}):(function(l,t,e,i){i===void 0&&(i=e),l[i]=t[e]})),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?(function(l,t){Object.defineProperty(l,"default",{enumerable:!0,value:t})}):function(l,t){l.default=t}),__importStar=this&&this.__importStar||(function(){var l=function(t){return l=Object.getOwnPropertyNames||function(e){var i=[];for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&(i[i.length]=s);return i},l(t)};return function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var i=l(t),s=0;s<i.length;s++)i[s]!=="default"&&__createBinding(e,t,i[s]);return __setModuleDefault(e,t),e}})();Object.defineProperty(exports,"__esModule",{value:!0}),exports.AnalyticsService=void 0;const vscode=__importStar(require("vscode")),path=__importStar(require("path")),fsSync=__importStar(require("fs")),fs_1=require("fs"),child_process_1=require("child_process"),branding_1=require("../branding"),indexingService_1=require("./dashboard/indexingService"),performanceMonitor_1=require("./dashboard/performanceMonitor"),lifecycle_1=require("./lifecycle"),index_1=require("../utils/index"),windowsCommandLine_1=require("../utils/windowsCommandLine");let cachedWindowsPwshPath;function getWindowsPwshOrPowerShell(){return process.platform!=="win32"?"powershell.exe":(cachedWindowsPwshPath===void 0&&(cachedWindowsPwshPath=resolvePwshExecutable()),cachedWindowsPwshPath??"powershell.exe")}function resolvePwshExecutable(){const l=[],t=(process.env.PATH??"").split(path.delimiter).filter(Boolean);for(const a of t)l.push(path.join(a,"pwsh.exe"));const e=process.env.ProgramFiles??"C:\\Program Files",i=process.env["ProgramFiles(x86)"]??"C:\\Program Files (x86)",s=process.env.LOCALAPPDATA;for(const a of["7","8"])l.push(path.join(e,"PowerShell",a,"pwsh.exe")),l.push(path.join(i,"PowerShell",a,"pwsh.exe"));s&&l.push(path.join(s,"Microsoft","PowerShell","pwsh.exe"));for(const a of l)try{if(fsSync.statSync(a).isFile())return a}catch{}return null}class AnalyticsService{statusBarItem;outputChannel;extensionContext;_status="stopped";_lastStackStatus={otelRunning:!1,litellmRunning:!1,litellmWithOtel:!1};indexProgress={status:"idle",percent:0};indexingService;performanceMonitor;disposables=[];lifecycleInProgress=!1;get status(){return this._status}healthCheckTimer=null;static HEALTH_CHECK_INTERVAL=3e4;constructor(t,e){this.extensionContext=t,this.outputChannel=e,this.performanceMonitor=new performanceMonitor_1.PerformanceMonitor(e),this.statusBarItem=vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right,99),this.statusBarItem.command="agentide.analytics.toggle",this.registerCommands(),this.initializeWithLazyHeartbeat()}async initializeWithLazyHeartbeat(){await this.isAnalyticsConfigured()?(this.updateStatusBar("checking"),await this.checkStatus(),this.startHeartbeat()):(this.updateStatusBar("stopped"),this.statusBarItem.show(),await this.detectStandaloneLiteLLM()),await this.initializeIndexingService()}async initializeIndexingService(){try{const t=this.getAnalyticsDir(),e=path.join(t,"traces");this.indexingService=new indexingService_1.IndexingService(e,this.outputChannel,this.performanceMonitor),this.disposables.push(this.indexingService.onProgressChange(i=>{this.indexProgress=i,this.updateStatusBar(this._status)})),await this.indexingService.initialize()}catch(t){this.outputChannel.appendLine(`[Analytics] Failed to initialize indexing service: ${t}`)}}getIndexingService(){return this.indexingService}async detectStandaloneLiteLLM(){try{(await this.getLiteLLMStatus()).running&&this.outputChannel.appendLine("[Analytics] Detected standalone LiteLLM running")}catch{}}async isAnalyticsConfigured(){const t=this.getAnalyticsDir(),e=path.join(t,"config","otel-collector.yaml");try{return await fs_1.promises.access(e),!0}catch{return!1}}startHeartbeat(){this.healthCheckTimer||(this.healthCheckTimer=setInterval(()=>{this.checkStatus()},AnalyticsService.HEALTH_CHECK_INTERVAL))}registerCommands(){this.disposables.push(vscode.commands.registerCommand("agentide.analytics.toggle",()=>this.toggle()),vscode.commands.registerCommand("agentide.analytics.restart",()=>this.restart()),vscode.commands.registerCommand("agentide.analytics.start",()=>this.start()),vscode.commands.registerCommand("agentide.analytics.stop",()=>this.stop()),vscode.commands.registerCommand("agentide.analytics.status",()=>this.showStatus()),vscode.commands.registerCommand("agentide.analytics.query",()=>this.openQueryTerminal()),vscode.commands.registerCommand("agentide.analytics.openLiteLLMConfig",()=>this.openLiteLLMConfig()),vscode.commands.registerCommand("agentide.analytics.isRunning",()=>this.checkStatus()))}getAnalyticsDir(){return index_1.DIRS.analytics()}getOtelPidFile(){return index_1.PID_FILES.otelcol()}getLiteLLMPidFile(){return index_1.PID_FILES.litellm()}async checkStatus(){try{const t=await this.getStackStatus();return this._lastStackStatus=t,t.otelRunning&&t.litellmWithOtel?(this.updateStatusBar("running"),!0):this.lifecycleInProgress?(this.updateStatusBar("checking"),!1):t.otelRunning||t.litellmRunning?(this.updateStatusBar("partial"),!1):(this.updateStatusBar("stopped"),!1)}catch{return this.updateStatusBar("error"),!1}}async getStackStatus(){const t=await this.isOtelRunning(),e=await this.getLiteLLMStatus();return{otelRunning:t,litellmRunning:e.running,litellmWithOtel:e.withOtel}}async isOtelRunning(){return await(0,index_1.checkPidFileWithCleanup)(this.getOtelPidFile())!==null}async getLiteLLMStatus(){const t=await(0,index_1.checkPidFileWithCleanup)(this.getLiteLLMPidFile());if(!t)return{running:!1,withOtel:!1};const e=t.metadata??[],i=e.some(a=>a.trim().toLowerCase()==="otel"),s=e.find(a=>a.startsWith("url="));return s&&await this.maybeUpdateLiteLLMBaseUrlValue(s.slice(4).trim()),{running:!0,withOtel:i}}async maybeUpdateLiteLLMBaseUrlValue(t){const e=vscode.workspace.getConfiguration("agentide.providers.litellm");if(e.get("baseUrl")?.trim())return;const i=t.trim();i&&(await e.update("baseUrl",i,vscode.ConfigurationTarget.Global),this.outputChannel.appendLine(`[Analytics] Auto-detected LiteLLM base URL: ${i}`))}async waitForStackReady(t=3e4,e=1e3){const i=Date.now()+t;let s=await this.getStackStatus();for(;Date.now()<i;){if(s=await this.getStackStatus(),this._lastStackStatus=s,s.otelRunning&&s.litellmWithOtel)return s;await new Promise(a=>setTimeout(a,e))}return s}async waitForStackStopped(t=15e3,e=500){const i=Date.now()+t;let s=await this.getStackStatus();for(;Date.now()<i;){if(s=await this.getStackStatus(),this._lastStackStatus=s,!s.otelRunning&&!s.litellmRunning)return s;await new Promise(a=>setTimeout(a,e))}return s}async monitorStartupReadiness(t){const e=await this.waitForStackReady();this._lastStackStatus=e;const i=this.deriveStatusFrom(e);if(this.lifecycleInProgress=!1,this.updateStatusBar(i),i==="running"){this.log(`Analytics stack ready after ${t}.`),vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight ${t==="restart"?"restarted":"started"}.`);return}this.log(`Analytics stack not fully ready after ${t} (otel=${e.otelRunning}, litellm=${e.litellmRunning}, litellmWithOtel=${e.litellmWithOtel})`),await vscode.window.showWarningMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight ${t==="restart"?"restart":"start"} is not fully ready yet.`,"Show Status")==="Show Status"&&await this.showStatus()}async monitorStopReadiness(){const t=await this.waitForStackStopped();this._lastStackStatus=t;const e=this.deriveStatusFrom(t);if(this.lifecycleInProgress=!1,this.updateStatusBar(e),e==="stopped"){this.log("Analytics stack stopped."),vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight stopped.`);return}this.log(`Analytics stack not fully stopped (otel=${t.otelRunning}, litellm=${t.litellmRunning}, litellmWithOtel=${t.litellmWithOtel})`),await vscode.window.showWarningMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight stop is not fully complete yet.`,"Show Status")==="Show Status"&&await this.showStatus()}async toggle(){const t=this._status==="checking"?this.deriveStatusFrom(this._lastStackStatus):this._status;t==="running"?await this.showStatusWithRestartOption():t==="partial"?await this.showPartialStatusOption():await this.showStoppedStatusOption(t)}queueBackgroundStatusRefresh(t){this.lifecycleInProgress||setTimeout(()=>{this.lifecycleInProgress||this.checkStatus().catch(e=>{this.log(`Background status refresh failed after ${t}: ${e instanceof Error?e.message:String(e)}`)})},100)}deriveStatusFrom(t){return(0,lifecycle_1.deriveAnalyticsStatus)(t)}async showPartialStatusOption(){const t=this._lastStackStatus,e=(0,lifecycle_1.getPartialSpotlightMessage)(t),i=vscode.window.showWarningMessage(`${branding_1.AGENTIDE_BRAND_NAME}: ${e}`,"Install","Restart All","Show Status");switch(this.queueBackgroundStatusRefresh("opening partial menu"),await i){case"Install":await vscode.commands.executeCommand("agentide.setupAnalytics");break;case"Restart All":await this.restart();break;case"Show Status":await this.showStatus();break}}async showStatusWithRestartOption(){const t=vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME} Spotlight is running.`,"See Spotlight","Restart All","Stop","Indexing","Show Status");switch(this.queueBackgroundStatusRefresh("opening running menu"),await t){case"See Spotlight":await vscode.commands.executeCommand("agentide.analytics.openDashboard");break;case"Restart All":await this.restart();break;case"Stop":await this.stop();break;case"Indexing":await this.triggerIndexing();break;case"Show Status":await this.showStatus();break}}async restart(){if(this.log("Restarting analytics stack..."),this.lifecycleInProgress=!0,this.updateStatusBar("checking"),!await this.isAnalyticsConfigured()){await this.start();return}const t=process.platform==="win32",e=await this.findScript(t?"start-analytics.ps1":"start-analytics.sh");if(!e){this.lifecycleInProgress=!1,this.updateStatusBar("error"),vscode.window.showErrorMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight scripts not found. Run Install first.`);return}(await this.createSpotlightLifecycleTerminal("Spotlight Restart","restart",e)).show(),this.updateStatusBar("checking"),this.startHeartbeat(),this.monitorStartupReadiness("restart")}async setupWindowsSpotlight(t={}){if(process.platform!=="win32")return{success:!1,error:"Windows-only setup flow invoked on a non-Windows platform."};const e=await this.findScript("setup-analytics.ps1");if(!e){const i=`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight install script not found.`;return vscode.window.showErrorMessage(i),{success:!1,error:i}}this.lifecycleInProgress=!0,this.updateStatusBar("checking"),this.outputChannel.show(!0),this.log(`${t.reinstall?"Reinstalling":"Installing/repairing"} Spotlight on Windows...`),vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: ${t.reinstall?"Reinstalling":"Installing"} Spotlight...`);try{if(t.reinstall&&(this.log("Stopping LiteLLM before reinstall..."),await this.stopLiteLLMDirectly()),!await this.runWindowsSetupScript(e))throw new Error("setup-analytics.ps1 failed");if((await this.getLiteLLMStatus()).running&&(this.log("Restarting LiteLLM to apply Spotlight config..."),await this.stopLiteLLMDirectly()),this.log("Starting LiteLLM proxy via IDE-managed launcher..."),!await this.startLiteLLMDirectly())throw new Error("LiteLLM launcher did not start");const r=this.getAnalyticsDir();return this.log(`LiteLLM logs: ${path.join(r,"litellm.log")}`),this.log(`LiteLLM errors: ${path.join(r,"litellm.error.log")}`),this.startHeartbeat(),this.monitorStartupReadiness(t.reinstall?"restart":"start"),{success:!0}}catch(i){this.lifecycleInProgress=!1,this.updateStatusBar("error");const s=i instanceof Error?i.message:String(i);return this.log(`Windows Spotlight install failed: ${s}`),vscode.window.showErrorMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight install failed. ${s}`),{success:!1,error:s}}}async showStoppedStatusOption(t){const e=t==="error"?`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight status is unavailable.`:`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight is stopped.`,i=vscode.window.showInformationMessage(e,"Start","Install","See Spotlight","Show Status");switch(this.queueBackgroundStatusRefresh("opening stopped menu"),await i){case"Start":await this.start();break;case"Install":await vscode.commands.executeCommand("agentide.setupAnalytics");break;case"See Spotlight":await vscode.commands.executeCommand("agentide.analytics.openDashboard");break;case"Show Status":await this.showStatus();break}}async runWindowsSetupScript(t){const e=(0,index_1.getAgentideHome)(),i=["-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-File",t,"-Yes"];return this.log(`Running ${path.basename(t)} -Yes...`),new Promise(s=>{let a=!1;const r=n=>{a||(a=!0,s(n))},o=(0,child_process_1.spawn)(getWindowsPwshOrPowerShell(),i,{cwd:path.dirname(t),env:{...process.env,AGENT_HOME_DIR:e},windowsHide:!0});o.stdout?.on("data",n=>{this.outputChannel.append(n.toString())}),o.stderr?.on("data",n=>{this.outputChannel.append(n.toString())}),o.once("error",n=>{this.log(`Windows setup script failed to spawn: ${n.message}`),r(!1)}),o.once("exit",n=>{n===0?(this.log("Windows Spotlight install script completed."),r(!0)):(this.log(`Windows Spotlight install script exited with code ${n??"unknown"}.`),r(!1))})})}async start(){if(await this.checkStatus()){vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight is already running.`);return}const e=process.platform==="win32",i=await this.findScript(e?"start-analytics.ps1":"start-analytics.sh");if(!i){this.lifecycleInProgress=!1,vscode.window.showErrorMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight scripts not found. Run Install first.`);return}this.lifecycleInProgress=!0,this.updateStatusBar("checking"),(await this.createSpotlightLifecycleTerminal("Spotlight Start","start",i)).show(),this.log("Starting Spotlight (OTEL Collector + LiteLLM)..."),vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Starting Spotlight...`),this.updateStatusBar("checking"),this.startHeartbeat(),this.monitorStartupReadiness("start")}async stop(){this.lifecycleInProgress=!0,this.updateStatusBar("checking");const t=process.platform==="win32",e=await this.findScript(t?"start-analytics.ps1":"start-analytics.sh");if(!e){await this.stopDirectly(),this.lifecycleInProgress=!1;return}(await this.createSpotlightLifecycleTerminal("Spotlight Stop","stop",e)).show(),this.log("Stopping Spotlight (OTEL Collector + LiteLLM)..."),this.updateStatusBar("checking"),this.monitorStopReadiness()}async stopDirectly(){try{const t=this.getOtelPidFile(),e=await fs_1.promises.readFile(t,"utf8"),i=parseInt(e.trim(),10);isNaN(i)||(process.kill(i,"SIGTERM"),await fs_1.promises.unlink(t).catch(()=>{}),this.log(`Stopped OTEL Collector (PID: ${i})`)),await this.stopLiteLLMDirectly(),this.updateStatusBar("stopped")}catch(t){this.log(`Failed to stop directly: ${t}`),this.updateStatusBar("error")}}async createSpotlightLifecycleTerminal(t,e,i){const s=(0,index_1.getAgentideHome)(),a=path.join(s,"analytics");fsSync.mkdirSync(a,{recursive:!0});const r=await this.findLiteLLMScript(),o=path.join(s,"litellm-otel.yaml"),n=path.join(a,"litellm.log"),c=path.join(a,"litellm.error.log");return process.platform==="win32"?vscode.window.createTerminal({name:t,cwd:path.dirname(i),env:{AGENT_HOME_DIR:s},shellPath:getWindowsPwshOrPowerShell(),shellArgs:["-NoProfile","-NoExit","-ExecutionPolicy","Bypass","-Command",this.buildWindowsSpotlightLifecycleCommand({action:e,startScript:i,litellmScript:r,agentHomeDir:s,configPath:o,stdoutPath:n,stderrPath:c})]}):vscode.window.createTerminal({name:t,cwd:path.dirname(i),env:{AGENT_HOME_DIR:s},shellPath:"/bin/bash",shellArgs:["-c",this.buildUnixSpotlightLifecycleCommand({action:e,startScript:i,litellmScript:r,agentHomeDir:s,configPath:o,stdoutPath:n,stderrPath:c})]})}buildWindowsSpotlightLifecycleCommand(t){const e=s=>`'${this.escapePowerShellSingleQuotedString(s)}'`,i=["$ErrorActionPreference = 'Continue'",`$env:AGENT_HOME_DIR = ${e(t.agentHomeDir)}`,`Write-Host ${e(this.getLifecycleTerminalIntro(t.action))}`];return(t.action==="stop"||t.action==="restart")&&i.push('Write-Host ""','Write-Host "Stopping LiteLLM proxy..."',...this.buildWindowsLiteLLMStopCommands(t.litellmScript,e)),t.action==="stop"?i.push('Write-Host ""','Write-Host "Stopping OTEL Collector..."',`& ${e(t.startScript)} -Stop`):t.action==="restart"?i.push('Write-Host ""','Write-Host "Restarting OTEL Collector..."',`& ${e(t.startScript)} -Stop`,"Start-Sleep 1",`& ${e(t.startScript)} -ForceDaemon`):i.push('Write-Host ""','Write-Host "Starting OTEL Collector..."',`& ${e(t.startScript)} -ForceDaemon`),(t.action==="start"||t.action==="restart")&&i.push('Write-Host ""',...this.buildWindowsLiteLLMStartCommands(t,e)),i.push('Write-Host ""','Write-Host "You can close this window."'),i.join(`\r
|
|
2
|
+
"use strict";var __createBinding=this&&this.__createBinding||(Object.create?(function(l,t,e,i){i===void 0&&(i=e);var s=Object.getOwnPropertyDescriptor(t,e);(!s||("get"in s?!t.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return t[e]}}),Object.defineProperty(l,i,s)}):(function(l,t,e,i){i===void 0&&(i=e),l[i]=t[e]})),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?(function(l,t){Object.defineProperty(l,"default",{enumerable:!0,value:t})}):function(l,t){l.default=t}),__importStar=this&&this.__importStar||(function(){var l=function(t){return l=Object.getOwnPropertyNames||function(e){var i=[];for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&(i[i.length]=s);return i},l(t)};return function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var i=l(t),s=0;s<i.length;s++)i[s]!=="default"&&__createBinding(e,t,i[s]);return __setModuleDefault(e,t),e}})();Object.defineProperty(exports,"__esModule",{value:!0}),exports.AnalyticsService=void 0;const vscode=__importStar(require("vscode")),path=__importStar(require("path")),fsSync=__importStar(require("fs")),fs_1=require("fs"),child_process_1=require("child_process"),branding_1=require("../branding"),indexingService_1=require("./dashboard/indexingService"),performanceMonitor_1=require("./dashboard/performanceMonitor"),lifecycle_1=require("./lifecycle"),index_1=require("../utils/index"),windowsCommandLine_1=require("../utils/windowsCommandLine");let cachedWindowsPwshPath;function getWindowsPwshOrPowerShell(){return process.platform!=="win32"?"powershell.exe":(cachedWindowsPwshPath===void 0&&(cachedWindowsPwshPath=resolvePwshExecutable()),cachedWindowsPwshPath??"powershell.exe")}function resolvePwshExecutable(){const l=[],t=(process.env.PATH??"").split(path.delimiter).filter(Boolean);for(const a of t)l.push(path.join(a,"pwsh.exe"));const e=process.env.ProgramFiles??"C:\\Program Files",i=process.env["ProgramFiles(x86)"]??"C:\\Program Files (x86)",s=process.env.LOCALAPPDATA;for(const a of["7","8"])l.push(path.join(e,"PowerShell",a,"pwsh.exe")),l.push(path.join(i,"PowerShell",a,"pwsh.exe"));s&&l.push(path.join(s,"Microsoft","PowerShell","pwsh.exe"));for(const a of l)try{if(fsSync.statSync(a).isFile())return a}catch{}return null}class AnalyticsService{statusBarItem;outputChannel;extensionContext;_status="stopped";_lastStackStatus={otelRunning:!1,litellmRunning:!1,litellmWithOtel:!1};indexProgress={status:"idle",percent:0};indexingService;performanceMonitor;disposables=[];lifecycleInProgress=!1;get status(){return this._status}healthCheckTimer=null;static HEALTH_CHECK_INTERVAL=3e4;constructor(t,e){this.extensionContext=t,this.outputChannel=e,this.performanceMonitor=new performanceMonitor_1.PerformanceMonitor(e),this.statusBarItem=vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right,99),this.statusBarItem.command="agentide.analytics.toggle",this.registerCommands(),this.initializeWithLazyHeartbeat()}async initializeWithLazyHeartbeat(){await this.isAnalyticsConfigured()?(this.updateStatusBar("checking"),await this.checkStatus(),this.startHeartbeat()):(this.updateStatusBar("stopped"),this.statusBarItem.show(),await this.detectStandaloneLiteLLM()),await this.initializeIndexingService()}async initializeIndexingService(){try{const t=this.getAnalyticsDir(),e=path.join(t,"traces");this.indexingService=new indexingService_1.IndexingService(e,this.outputChannel,this.performanceMonitor),this.disposables.push(this.indexingService.onProgressChange(i=>{this.indexProgress=i,this.updateStatusBar(this._status)})),await this.indexingService.initialize()}catch(t){this.outputChannel.appendLine(`[Analytics] Failed to initialize indexing service: ${t}`)}}getIndexingService(){return this.indexingService}async detectStandaloneLiteLLM(){try{(await this.getLiteLLMStatus()).running&&this.outputChannel.appendLine("[Analytics] Detected standalone LiteLLM running")}catch{}}async isAnalyticsConfigured(){const t=this.getAnalyticsDir(),e=path.join(t,"config","otel-collector.yaml");try{return await fs_1.promises.access(e),!0}catch{return!1}}startHeartbeat(){this.healthCheckTimer||(this.healthCheckTimer=setInterval(()=>{this.checkStatus()},AnalyticsService.HEALTH_CHECK_INTERVAL))}registerCommands(){this.disposables.push(vscode.commands.registerCommand("agentide.analytics.toggle",()=>this.toggle()),vscode.commands.registerCommand("agentide.analytics.restart",()=>this.restart()),vscode.commands.registerCommand("agentide.analytics.start",()=>this.start()),vscode.commands.registerCommand("agentide.analytics.stop",()=>this.stop()),vscode.commands.registerCommand("agentide.analytics.status",()=>this.showStatus()),vscode.commands.registerCommand("agentide.analytics.query",()=>this.openQueryTerminal()),vscode.commands.registerCommand("agentide.analytics.openLiteLLMConfig",()=>this.openLiteLLMConfig()),vscode.commands.registerCommand("agentide.analytics.isRunning",()=>this.checkStatus()))}getAnalyticsDir(){return index_1.DIRS.analytics()}getOtelPidFile(){return index_1.PID_FILES.otelcol()}getLiteLLMPidFile(){return index_1.PID_FILES.litellm()}async checkStatus(){try{const t=await this.getStackStatus();return this._lastStackStatus=t,t.otelRunning&&t.litellmWithOtel?(this.updateStatusBar("running"),!0):this.lifecycleInProgress?(this.updateStatusBar("checking"),!1):t.otelRunning||t.litellmRunning?(this.updateStatusBar("partial"),!1):(this.updateStatusBar("stopped"),!1)}catch{return this.updateStatusBar("error"),!1}}async getStackStatus(){const t=await this.isOtelRunning(),e=await this.getLiteLLMStatus();return{otelRunning:t,litellmRunning:e.running,litellmWithOtel:e.withOtel}}async isOtelRunning(){return await(0,index_1.checkPidFileWithCleanup)(this.getOtelPidFile())!==null}async getLiteLLMStatus(){const t=await(0,index_1.checkPidFileWithCleanup)(this.getLiteLLMPidFile());if(!t)return{running:!1,withOtel:!1};const e=t.metadata??[],i=e.some(a=>a.trim().toLowerCase()==="otel"),s=e.find(a=>a.startsWith("url="));return s&&await this.maybeUpdateLiteLLMBaseUrlValue(s.slice(4).trim()),{running:!0,withOtel:i}}async maybeUpdateLiteLLMBaseUrlValue(t){const e=vscode.workspace.getConfiguration("agentide.providers.litellm");if(e.get("baseUrl")?.trim())return;const i=t.trim();i&&(await e.update("baseUrl",i,vscode.ConfigurationTarget.Global),this.outputChannel.appendLine(`[Analytics] Auto-detected LiteLLM base URL: ${i}`))}async waitForStackReady(t=3e4,e=1e3){const i=Date.now()+t;let s=await this.getStackStatus();for(;Date.now()<i;){if(s=await this.getStackStatus(),this._lastStackStatus=s,s.otelRunning&&s.litellmWithOtel)return s;await new Promise(a=>setTimeout(a,e))}return s}async waitForStackStopped(t=15e3,e=500){const i=Date.now()+t;let s=await this.getStackStatus();for(;Date.now()<i;){if(s=await this.getStackStatus(),this._lastStackStatus=s,!s.otelRunning&&!s.litellmRunning)return s;await new Promise(a=>setTimeout(a,e))}return s}async monitorStartupReadiness(t){const e=await this.waitForStackReady();this._lastStackStatus=e;const i=this.deriveStatusFrom(e);if(this.lifecycleInProgress=!1,this.updateStatusBar(i),i==="running"){this.log(`Analytics stack ready after ${t}.`),vscode.commands.executeCommand("agentide.models.refresh").then(void 0,a=>{this.log(`Model registry refresh after ${t} failed: ${String(a)}`)}),vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight ${t==="restart"?"restarted":"started"}.`);return}this.log(`Analytics stack not fully ready after ${t} (otel=${e.otelRunning}, litellm=${e.litellmRunning}, litellmWithOtel=${e.litellmWithOtel})`),await vscode.window.showWarningMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight ${t==="restart"?"restart":"start"} is not fully ready yet.`,"Show Status")==="Show Status"&&await this.showStatus()}async monitorStopReadiness(){const t=await this.waitForStackStopped();this._lastStackStatus=t;const e=this.deriveStatusFrom(t);if(this.lifecycleInProgress=!1,this.updateStatusBar(e),e==="stopped"){this.log("Analytics stack stopped."),vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight stopped.`);return}this.log(`Analytics stack not fully stopped (otel=${t.otelRunning}, litellm=${t.litellmRunning}, litellmWithOtel=${t.litellmWithOtel})`),await vscode.window.showWarningMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight stop is not fully complete yet.`,"Show Status")==="Show Status"&&await this.showStatus()}async toggle(){const t=this._status==="checking"?this.deriveStatusFrom(this._lastStackStatus):this._status;t==="running"?await this.showStatusWithRestartOption():t==="partial"?await this.showPartialStatusOption():await this.showStoppedStatusOption(t)}queueBackgroundStatusRefresh(t){this.lifecycleInProgress||setTimeout(()=>{this.lifecycleInProgress||this.checkStatus().catch(e=>{this.log(`Background status refresh failed after ${t}: ${e instanceof Error?e.message:String(e)}`)})},100)}deriveStatusFrom(t){return(0,lifecycle_1.deriveAnalyticsStatus)(t)}async showPartialStatusOption(){const t=this._lastStackStatus,e=(0,lifecycle_1.getPartialSpotlightMessage)(t),i=vscode.window.showWarningMessage(`${branding_1.AGENTIDE_BRAND_NAME}: ${e}`,"Install","Restart All","Show Status");switch(this.queueBackgroundStatusRefresh("opening partial menu"),await i){case"Install":await vscode.commands.executeCommand("agentide.setupAnalytics");break;case"Restart All":await this.restart();break;case"Show Status":await this.showStatus();break}}async showStatusWithRestartOption(){const t=vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME} Spotlight is running.`,"See Spotlight","Restart All","Stop","Indexing","Show Status");switch(this.queueBackgroundStatusRefresh("opening running menu"),await t){case"See Spotlight":await vscode.commands.executeCommand("agentide.analytics.openDashboard");break;case"Restart All":await this.restart();break;case"Stop":await this.stop();break;case"Indexing":await this.triggerIndexing();break;case"Show Status":await this.showStatus();break}}async restart(){if(this.log("Restarting analytics stack..."),this.lifecycleInProgress=!0,this.updateStatusBar("checking"),!await this.isAnalyticsConfigured()){await this.start();return}const t=process.platform==="win32",e=await this.findScript(t?"start-analytics.ps1":"start-analytics.sh");if(!e){this.lifecycleInProgress=!1,this.updateStatusBar("error"),vscode.window.showErrorMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight scripts not found. Run Install first.`);return}(await this.createSpotlightLifecycleTerminal("Spotlight Restart","restart",e)).show(),this.updateStatusBar("checking"),this.startHeartbeat(),this.monitorStartupReadiness("restart")}async setupWindowsSpotlight(t={}){if(process.platform!=="win32")return{success:!1,error:"Windows-only setup flow invoked on a non-Windows platform."};const e=await this.findScript("setup-analytics.ps1");if(!e){const i=`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight install script not found.`;return vscode.window.showErrorMessage(i),{success:!1,error:i}}this.lifecycleInProgress=!0,this.updateStatusBar("checking"),this.outputChannel.show(!0),this.log(`${t.reinstall?"Reinstalling":"Installing/repairing"} Spotlight on Windows...`),vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: ${t.reinstall?"Reinstalling":"Installing"} Spotlight...`);try{if(t.reinstall&&(this.log("Stopping LiteLLM before reinstall..."),await this.stopLiteLLMDirectly()),!await this.runWindowsSetupScript(e))throw new Error("setup-analytics.ps1 failed");if((await this.getLiteLLMStatus()).running&&(this.log("Restarting LiteLLM to apply Spotlight config..."),await this.stopLiteLLMDirectly()),this.log("Starting LiteLLM proxy via IDE-managed launcher..."),!await this.startLiteLLMDirectly())throw new Error("LiteLLM launcher did not start");const r=this.getAnalyticsDir();return this.log(`LiteLLM logs: ${path.join(r,"litellm.log")}`),this.log(`LiteLLM errors: ${path.join(r,"litellm.error.log")}`),this.startHeartbeat(),this.monitorStartupReadiness(t.reinstall?"restart":"start"),{success:!0}}catch(i){this.lifecycleInProgress=!1,this.updateStatusBar("error");const s=i instanceof Error?i.message:String(i);return this.log(`Windows Spotlight install failed: ${s}`),vscode.window.showErrorMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight install failed. ${s}`),{success:!1,error:s}}}async showStoppedStatusOption(t){const e=t==="error"?`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight status is unavailable.`:`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight is stopped.`,i=vscode.window.showInformationMessage(e,"Start","Install","See Spotlight","Show Status");switch(this.queueBackgroundStatusRefresh("opening stopped menu"),await i){case"Start":await this.start();break;case"Install":await vscode.commands.executeCommand("agentide.setupAnalytics");break;case"See Spotlight":await vscode.commands.executeCommand("agentide.analytics.openDashboard");break;case"Show Status":await this.showStatus();break}}async runWindowsSetupScript(t){const e=(0,index_1.getAgentideHome)(),i=["-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-File",t,"-Yes"];return this.log(`Running ${path.basename(t)} -Yes...`),new Promise(s=>{let a=!1;const r=n=>{a||(a=!0,s(n))},o=(0,child_process_1.spawn)(getWindowsPwshOrPowerShell(),i,{cwd:path.dirname(t),env:{...process.env,AGENT_HOME_DIR:e},windowsHide:!0});o.stdout?.on("data",n=>{this.outputChannel.append(n.toString())}),o.stderr?.on("data",n=>{this.outputChannel.append(n.toString())}),o.once("error",n=>{this.log(`Windows setup script failed to spawn: ${n.message}`),r(!1)}),o.once("exit",n=>{n===0?(this.log("Windows Spotlight install script completed."),r(!0)):(this.log(`Windows Spotlight install script exited with code ${n??"unknown"}.`),r(!1))})})}async start(){if(await this.checkStatus()){vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight is already running.`);return}const e=process.platform==="win32",i=await this.findScript(e?"start-analytics.ps1":"start-analytics.sh");if(!i){this.lifecycleInProgress=!1,vscode.window.showErrorMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Spotlight scripts not found. Run Install first.`);return}this.lifecycleInProgress=!0,this.updateStatusBar("checking"),(await this.createSpotlightLifecycleTerminal("Spotlight Start","start",i)).show(),this.log("Starting Spotlight (OTEL Collector + LiteLLM)..."),vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Starting Spotlight...`),this.updateStatusBar("checking"),this.startHeartbeat(),this.monitorStartupReadiness("start")}async stop(){this.lifecycleInProgress=!0,this.updateStatusBar("checking");const t=process.platform==="win32",e=await this.findScript(t?"start-analytics.ps1":"start-analytics.sh");if(!e){await this.stopDirectly(),this.lifecycleInProgress=!1;return}(await this.createSpotlightLifecycleTerminal("Spotlight Stop","stop",e)).show(),this.log("Stopping Spotlight (OTEL Collector + LiteLLM)..."),this.updateStatusBar("checking"),this.monitorStopReadiness()}async stopDirectly(){try{const t=this.getOtelPidFile(),e=await fs_1.promises.readFile(t,"utf8"),i=parseInt(e.trim(),10);isNaN(i)||(process.kill(i,"SIGTERM"),await fs_1.promises.unlink(t).catch(()=>{}),this.log(`Stopped OTEL Collector (PID: ${i})`)),await this.stopLiteLLMDirectly(),this.updateStatusBar("stopped")}catch(t){this.log(`Failed to stop directly: ${t}`),this.updateStatusBar("error")}}async createSpotlightLifecycleTerminal(t,e,i){const s=(0,index_1.getAgentideHome)(),a=path.join(s,"analytics");fsSync.mkdirSync(a,{recursive:!0});const r=await this.findLiteLLMScript(),o=path.join(s,"litellm-otel.yaml"),n=path.join(a,"litellm.log"),c=path.join(a,"litellm.error.log");return process.platform==="win32"?vscode.window.createTerminal({name:t,cwd:path.dirname(i),env:{AGENT_HOME_DIR:s},shellPath:getWindowsPwshOrPowerShell(),shellArgs:["-NoProfile","-NoExit","-ExecutionPolicy","Bypass","-Command",this.buildWindowsSpotlightLifecycleCommand({action:e,startScript:i,litellmScript:r,agentHomeDir:s,configPath:o,stdoutPath:n,stderrPath:c})]}):vscode.window.createTerminal({name:t,cwd:path.dirname(i),env:{AGENT_HOME_DIR:s},shellPath:"/bin/bash",shellArgs:["-c",this.buildUnixSpotlightLifecycleCommand({action:e,startScript:i,litellmScript:r,agentHomeDir:s,configPath:o,stdoutPath:n,stderrPath:c})]})}buildWindowsSpotlightLifecycleCommand(t){const e=s=>`'${this.escapePowerShellSingleQuotedString(s)}'`,i=["$ErrorActionPreference = 'Continue'",`$env:AGENT_HOME_DIR = ${e(t.agentHomeDir)}`,`Write-Host ${e(this.getLifecycleTerminalIntro(t.action))}`];return(t.action==="stop"||t.action==="restart")&&i.push('Write-Host ""','Write-Host "Stopping LiteLLM proxy..."',...this.buildWindowsLiteLLMStopCommands(t.litellmScript,e)),t.action==="stop"?i.push('Write-Host ""','Write-Host "Stopping OTEL Collector..."',`& ${e(t.startScript)} -Stop`):t.action==="restart"?i.push('Write-Host ""','Write-Host "Restarting OTEL Collector..."',`& ${e(t.startScript)} -Stop`,"Start-Sleep 1",`& ${e(t.startScript)} -ForceDaemon`):i.push('Write-Host ""','Write-Host "Starting OTEL Collector..."',`& ${e(t.startScript)} -ForceDaemon`),(t.action==="start"||t.action==="restart")&&i.push('Write-Host ""',...this.buildWindowsLiteLLMStartCommands(t,e)),i.push('Write-Host ""','Write-Host "You can close this window."'),i.join(`\r
|
|
3
3
|
`)}buildWindowsLiteLLMStopCommands(t,e){return t?[`& ${e(t)} -Stop`]:['Write-Host "[WARN] LiteLLM launcher script not found. Skipping LiteLLM stop." -ForegroundColor Yellow']}buildWindowsLiteLLMStartCommands(t,e){if(!t.litellmScript)return['Write-Host "[WARN] LiteLLM launcher script not found. Skipping LiteLLM start." -ForegroundColor Yellow'];const i=(0,windowsCommandLine_1.buildWindowsArgumentLine)(["-NoProfile","-NoLogo","-ExecutionPolicy","Bypass","-File",t.litellmScript,"-ConfigPath",t.configPath]);return['Write-Host "Starting LiteLLM proxy..."',`if (-not (Test-Path ${e(t.configPath)})) {`,` Write-Host ${e(`[INFO] LiteLLM config missing: ${t.configPath} \u2014 launcher will re-create it from the bundled template.`)}`,"}",`$env:LITELLM_CONFIG_PATH = ${e(t.configPath)}`,"try {",` $proc = Start-Process -FilePath ${e(getWindowsPwshOrPowerShell())} -WindowStyle Hidden -WorkingDirectory ${e(path.dirname(t.litellmScript))} -ArgumentList ${e(i)} -RedirectStandardOutput ${e(t.stdoutPath)} -RedirectStandardError ${e(t.stderrPath)} -PassThru -ErrorAction Stop`,' Write-Host "[OK] LiteLLM launcher started (PID: $($proc.Id))"',` Write-Host ${e(` Logs: ${t.stdoutPath}`)}`,` Write-Host ${e(` Errors: ${t.stderrPath}`)}`,"} catch {",' Write-Host "[ERROR] Failed to start LiteLLM launcher." -ForegroundColor Red'," Write-Host $_.Exception.Message -ForegroundColor Red","}"]}buildUnixSpotlightLifecycleCommand(t){const e=s=>this.quoteBash(s),i=["set +e",`export AGENT_HOME_DIR=${e(t.agentHomeDir)}`,`mkdir -p ${e(path.dirname(t.stdoutPath))}`,`echo ${e(this.getLifecycleTerminalIntro(t.action))}`];return(t.action==="stop"||t.action==="restart")&&i.push('echo ""','echo "Stopping LiteLLM proxy..."',...this.buildUnixLiteLLMStopCommands(t.litellmScript,e)),t.action==="stop"?i.push('echo ""','echo "Stopping OTEL Collector..."',`${e(t.startScript)} --stop`):t.action==="restart"?i.push('echo ""','echo "Restarting OTEL Collector..."',`${e(t.startScript)} --stop`,"sleep 1",`${e(t.startScript)} --force-daemon`):i.push('echo ""','echo "Starting OTEL Collector..."',`${e(t.startScript)} --force-daemon`),(t.action==="start"||t.action==="restart")&&i.push('echo ""',...this.buildUnixLiteLLMStartCommands(t,e)),i.push('echo ""','echo "Press any key to close..."',"read -n 1"),i.join(`
|
|
4
4
|
`)}buildUnixLiteLLMStopCommands(t,e){return t?[`${e(t)} --stop`]:['echo "[WARN] LiteLLM launcher script not found. Skipping LiteLLM stop." >&2']}buildUnixLiteLLMStartCommands(t,e){return t.litellmScript?['echo "Starting LiteLLM proxy..."',`if [ ! -f ${e(t.configPath)} ]; then`,` echo ${e(`[INFO] LiteLLM config missing: ${t.configPath} \u2014 launcher will re-create it from the bundled template.`)}`,"fi",`export LITELLM_CONFIG_PATH=${e(t.configPath)}`,`nohup ${e(t.litellmScript)} --config ${e(t.configPath)} >> ${e(t.stdoutPath)} 2>> ${e(t.stderrPath)} &`,'echo "[OK] LiteLLM launcher started (PID: $!)"',`echo ${e(` Logs: ${t.stdoutPath}`)}`,`echo ${e(` Errors: ${t.stderrPath}`)}`]:['echo "[WARN] LiteLLM launcher script not found. Skipping LiteLLM start." >&2']}getLifecycleTerminalIntro(t){switch(t){case"start":return"Starting Spotlight (OTEL Collector + LiteLLM)...";case"stop":return"Stopping Spotlight (LiteLLM + OTEL Collector)...";case"restart":return"Restarting Spotlight (LiteLLM + OTEL Collector)..."}}quoteBash(t){return`'${t.replace(/'/g,"'\\''")}'`}async stopLiteLLMDirectly(){const e=process.platform==="win32"?["-Stop"]:["--stop"];return this.runLauncher(e,{detached:!1})}async startLiteLLMDirectly(){const t=path.join((0,index_1.getAgentideHome)(),"litellm-otel.yaml"),i=process.platform==="win32"?["-ConfigPath",t]:["--config",t];return this.runLauncher(i,{detached:!0,configPath:t})}async runLauncher(t,e){const i=await this.findLiteLLMScript();if(!i)return!1;const s=process.platform==="win32",a=s?getWindowsPwshOrPowerShell():"/bin/bash",r=s?["-NoProfile","-ExecutionPolicy","Bypass","-File",i,...t]:[i,...t],o={...process.env,AGENT_HOME_DIR:(0,index_1.getAgentideHome)()};if(o.AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE||(o.AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE="1"),e.configPath&&(o.LITELLM_CONFIG_PATH=e.configPath),s&&e.detached)return this.startLiteLLMViaHiddenWindowsProcess(i,t,o);if(e.detached){const n=path.join((0,index_1.getAgentideHome)(),"analytics");fsSync.mkdirSync(n,{recursive:!0});const c=fsSync.openSync(path.join(n,"litellm.log"),"a"),h=fsSync.openSync(path.join(n,"litellm.error.log"),"a");try{const d=(0,child_process_1.spawn)(a,r,{detached:!0,stdio:["ignore",c,h],windowsHide:!0,env:o,cwd:path.dirname(i)});return d.unref(),this.log(`LiteLLM launcher started (PID: ${d.pid})`),!0}catch(d){return this.log(`Failed to start LiteLLM: ${d}`),!1}finally{fsSync.closeSync(c),fsSync.closeSync(h)}}return new Promise(n=>{const c=(0,child_process_1.spawn)(a,r,{windowsHide:!0,env:o,cwd:path.dirname(i)});c.once("error",h=>{this.log(`LiteLLM launcher failed to spawn: ${h}`),n(!1)}),c.once("exit",h=>n(h===0))})}async startLiteLLMViaHiddenWindowsProcess(t,e,i){const s=path.join((0,index_1.getAgentideHome)(),"analytics");fsSync.mkdirSync(s,{recursive:!0});const a=path.join(s,"litellm.log"),r=path.join(s,"litellm.error.log"),o=(0,windowsCommandLine_1.buildWindowsArgumentLine)(["-NoProfile","-NoLogo","-ExecutionPolicy","Bypass","-File",t,...e]),n=[`$env:AGENT_HOME_DIR = '${this.escapePowerShellSingleQuotedString(i.AGENT_HOME_DIR??(0,index_1.getAgentideHome)())}'`,i.LITELLM_CONFIG_PATH?`$env:LITELLM_CONFIG_PATH = '${this.escapePowerShellSingleQuotedString(i.LITELLM_CONFIG_PATH)}'`:"",`$proc = Start-Process -FilePath '${this.escapePowerShellSingleQuotedString(getWindowsPwshOrPowerShell())}' -WindowStyle Hidden -WorkingDirectory '${this.escapePowerShellSingleQuotedString(path.dirname(t))}' -ArgumentList '${this.escapePowerShellSingleQuotedString(o)}' -RedirectStandardOutput '${this.escapePowerShellSingleQuotedString(a)}' -RedirectStandardError '${this.escapePowerShellSingleQuotedString(r)}' -PassThru`,"Write-Output $proc.Id"].filter(Boolean).join("; ");return new Promise(c=>{let h="",d="";const g=(0,child_process_1.spawn)(getWindowsPwshOrPowerShell(),["-NoProfile","-ExecutionPolicy","Bypass","-Command",n],{windowsHide:!0,env:i,cwd:path.dirname(t)});g.stdout.on("data",u=>{h+=u.toString()}),g.stderr.on("data",u=>{d+=u.toString()}),g.once("error",u=>{this.log(`LiteLLM launcher failed to spawn hidden Windows process: ${u}`),c(!1)}),g.once("exit",u=>{if(u!==0){const m=d.trim()||h.trim();this.log(`LiteLLM hidden Windows launcher exited with code ${u}${m?`: ${m}`:""}`),c(!1);return}const p=parseInt(h.trim(),10);Number.isNaN(p)||this.log(`LiteLLM hidden Windows process started (PID: ${p})`),c(!0)})})}escapePowerShellSingleQuotedString(t){return t.replace(/'/g,"''")}async findLiteLLMScript(){const t=this.extensionContext.extensionPath,i=process.platform==="win32"?"agentide-litellm.ps1":"agentide-litellm.sh",s=[path.join(t,"out","scripts",i),path.join(t,"scripts",i),path.join(t,"..","..","scripts",i)];for(const a of s)try{return await fs_1.promises.access(a),a}catch{}return null}async triggerIndexing(){if(!this.indexingService){vscode.window.showWarningMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Indexing service is not available.`);return}if(this.indexingService.isIndexing()){vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Indexing is already in progress.`);return}this.indexingService.scheduleIndexing(!0),vscode.window.showInformationMessage(`${branding_1.AGENTIDE_BRAND_NAME}: Triggered indexing.`)}async showStatus(){const t=await this.getStackStatus(),e=vscode.workspace.getConfiguration("agentide.providers.litellm").get("baseUrl")?.trim(),i=t.otelRunning?"\u{1F7E2} 127.0.0.1:4318":"\u26AB Not running";let s;t.litellmRunning&&e?s=`\u{1F7E2} ${e}`:t.litellmRunning?s="\u{1F7E2} Set URL in Settings or Stop External LiteLLM":s="\u26AB Not running";const a=[`LiteLLM: ${s}`,`OTEL: ${i}`].join(`
|
|
5
5
|
|
package/out/auth/provider.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! Copyright (c) 2026 AgentHippo.ai. All rights reserved. Proprietary; see LICENSE. */
|
|
2
|
-
"use strict";var __createBinding=this&&this.__createBinding||(Object.create?(function(s,t,e,n){n===void 0&&(n=e);var o=Object.getOwnPropertyDescriptor(t,e);(!o||("get"in o?!t.__esModule:o.writable||o.configurable))&&(o={enumerable:!0,get:function(){return t[e]}}),Object.defineProperty(s,n,o)}):(function(s,t,e,n){n===void 0&&(n=e),s[n]=t[e]})),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?(function(s,t){Object.defineProperty(s,"default",{enumerable:!0,value:t})}):function(s,t){s.default=t}),__importStar=this&&this.__importStar||(function(){var s=function(t){return s=Object.getOwnPropertyNames||function(e){var n=[];for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&(n[n.length]=o);return n},s(t)};return function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var n=s(t),o=0;o<n.length;o++)n[o]!=="default"&&__createBinding(e,t,n[o]);return __setModuleDefault(e,t),e}})();Object.defineProperty(exports,"__esModule",{value:!0}),exports.AgentHippoAuthProvider=exports.getAgentHippoAccessToken=void 0,exports.registerAgentHippoAuth=registerAgentHippoAuth,exports.refreshBootstrapIfSignedIn=refreshBootstrapIfSignedIn,exports.startAgentHippoTokenRefreshTimer=startAgentHippoTokenRefreshTimer;const vscode=__importStar(require("vscode")),authBaseUrl_js_1=require("./authBaseUrl.js"),bootstrapApply_js_1=require("./bootstrapApply.js"),types_js_1=require("./types.js"),signInFlow_js_1=require("./signInFlow.js"),sessionStore_js_1=require("./sessionStore.js");var sessionStore_js_2=require("./sessionStore.js");Object.defineProperty(exports,"getAgentHippoAccessToken",{enumerable:!0,get:function(){return sessionStore_js_2.getAgentHippoAccessToken}});
|
|
2
|
+
"use strict";var __createBinding=this&&this.__createBinding||(Object.create?(function(s,t,e,n){n===void 0&&(n=e);var o=Object.getOwnPropertyDescriptor(t,e);(!o||("get"in o?!t.__esModule:o.writable||o.configurable))&&(o={enumerable:!0,get:function(){return t[e]}}),Object.defineProperty(s,n,o)}):(function(s,t,e,n){n===void 0&&(n=e),s[n]=t[e]})),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?(function(s,t){Object.defineProperty(s,"default",{enumerable:!0,value:t})}):function(s,t){s.default=t}),__importStar=this&&this.__importStar||(function(){var s=function(t){return s=Object.getOwnPropertyNames||function(e){var n=[];for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&(n[n.length]=o);return n},s(t)};return function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var n=s(t),o=0;o<n.length;o++)n[o]!=="default"&&__createBinding(e,t,n[o]);return __setModuleDefault(e,t),e}})();Object.defineProperty(exports,"__esModule",{value:!0}),exports.AgentHippoAuthProvider=exports.getAgentHippoAccessToken=void 0,exports.registerAgentHippoAuth=registerAgentHippoAuth,exports.refreshBootstrapIfSignedIn=refreshBootstrapIfSignedIn,exports.startAgentHippoTokenRefreshTimer=startAgentHippoTokenRefreshTimer;const vscode=__importStar(require("vscode")),authBaseUrl_js_1=require("./authBaseUrl.js"),bootstrapApply_js_1=require("./bootstrapApply.js"),types_js_1=require("./types.js"),signInFlow_js_1=require("./signInFlow.js"),sessionStore_js_1=require("./sessionStore.js");var sessionStore_js_2=require("./sessionStore.js");Object.defineProperty(exports,"getAgentHippoAccessToken",{enumerable:!0,get:function(){return sessionStore_js_2.getAgentHippoAccessToken}});async function syncAgentHippoModelGatewayToken(s){try{return await vscode.commands.executeCommand("agentide.auth.applyBootstrapKeys",{provider:"agenthippo",apiKey:s}),!0}catch{return!1}}async function clearAgentHippoModelGatewayToken(){try{await vscode.commands.executeCommand("agentide.auth.applyBootstrapKeys",{provider:"agenthippo",apiKey:""})}catch{}}class AgentHippoAuthProvider{context;_onDidChangeSessions=new vscode.EventEmitter;onDidChangeSessions=this._onDidChangeSessions.event;constructor(t){this.context=t}dispose(){}async getSessions(t){const e=await(0,sessionStore_js_1.getStoredAuthSession)(this.context.secrets);return e?t?.length&&!t.every(n=>e.scopes.includes(n))?[]:[(0,sessionStore_js_1.storedSessionToVsCode)(e)]:[]}async createSession(t){const e=await(0,signInFlow_js_1.runAgentHippoSignInFlow)(this.context,{scopes:t});if(!e)throw new Error("Sign-in cancelled");return await syncAgentHippoModelGatewayToken(e.accessToken),this._onDidChangeSessions.fire({added:[e],removed:[],changed:[]}),e}async removeSession(t){const e=await(0,sessionStore_js_1.getStoredAuthSession)(this.context.secrets),n=e?(0,sessionStore_js_1.storedSessionToVsCode)(e):void 0;if(e){const r=(0,authBaseUrl_js_1.resolveAuthBrokerBaseUrl)(vscode.workspace.getConfiguration("agentide"),i=>console.warn(i));try{await fetch(`${r}/api/ide/signout`,{method:"POST",headers:{Authorization:`Bearer ${e.accessToken}`}})}catch{}}await(0,sessionStore_js_1.clearAuthSession)(this.context.secrets),clearAgentHippoModelGatewayToken(),await(0,bootstrapApply_js_1.revertTeamManagedBootstrap)({getConfiguration:r=>vscode.workspace.getConfiguration(r),ConfigurationTarget:vscode.ConfigurationTarget,setParticipantApiKey:async r=>{await vscode.commands.executeCommand("agentide.setParticipantApiKey",r),await vscode.commands.executeCommand("agentide.auth.applyBootstrapKeys",r)},globalStateUpdate:async(r,i)=>{await this.context.globalState.update(r,i)},globalStateGet:r=>this.context.globalState.get(r)});const o=n?[n]:[];this._onDidChangeSessions.fire({added:[],removed:o,changed:[]})}}exports.AgentHippoAuthProvider=AgentHippoAuthProvider;function registerAgentHippoAuth(s){const t=new AgentHippoAuthProvider(s);s.subscriptions.push(vscode.authentication.registerAuthenticationProvider(types_js_1.AUTH_PROVIDER_ID,"AgentHippo",t,{supportsMultipleAccounts:!1}),vscode.commands.registerCommand("agenthippo.signIn",async()=>{try{await vscode.authentication.getSession(types_js_1.AUTH_PROVIDER_ID,[...types_js_1.AUTH_SCOPES],{forceNewSession:!0})}catch(e){const n=e instanceof Error?e.message:String(e);n.toLowerCase().includes("cancel")||vscode.window.showErrorMessage(`AgentHippo sign-in failed: ${n}`)}}),vscode.commands.registerCommand("agenthippo.signOut",async()=>{const e=await(0,sessionStore_js_1.getStoredAuthSession)(s.secrets);e&&await t.removeSession(e.accountId)}),vscode.commands.registerCommand("agentide.auth.refreshToken",async()=>{const e=await(0,sessionStore_js_1.getAgentHippoAccessToken)(s);return e?syncAgentHippoModelGatewayToken(e.accessToken):!1}))}async function refreshBootstrapIfSignedIn(s){const t=await(0,sessionStore_js_1.getAgentHippoAccessToken)(s);if(t){try{await(0,sessionStore_js_1.applyIdeBootstrapForContext)(s,t.accessToken)}catch{}syncAgentHippoModelGatewayToken(t.accessToken)}}const TOKEN_REFRESH_INTERVAL_MS=6e4;function startAgentHippoTokenRefreshTimer(s){let t="",e=!1;const n=setInterval(()=>{e||(async()=>{e=!0;try{const o=await(0,sessionStore_js_1.getAgentHippoAccessToken)(s);if(!o)return;o.accessToken!==t&&await syncAgentHippoModelGatewayToken(o.accessToken)&&(t=o.accessToken)}finally{e=!1}})()},TOKEN_REFRESH_INTERVAL_MS);return{dispose:()=>clearInterval(n)}}
|
package/out/auth/signInFlow.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! Copyright (c) 2026 AgentHippo.ai. All rights reserved. Proprietary; see LICENSE. */
|
|
2
|
-
"use strict";var __createBinding=this&&this.__createBinding||(Object.create?(function(t,e,n,i){i===void 0&&(i=n);var r=Object.getOwnPropertyDescriptor(e,n);(!r||("get"in r?!e.__esModule:r.writable||r.configurable))&&(r={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,i,r)}):(function(t,e,n,i){i===void 0&&(i=n),t[i]=e[n]})),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?(function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}):function(t,e){t.default=e}),__importStar=this&&this.__importStar||(function(){var t=function(e){return t=Object.getOwnPropertyNames||function(n){var i=[];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(i[i.length]=r);return i},t(e)};return function(e){if(e&&e.__esModule)return e;var n={};if(e!=null)for(var i=t(e),r=0;r<i.length;r++)i[r]!=="default"&&__createBinding(n,e,i[r]);return __setModuleDefault(n,e),n}})();Object.defineProperty(exports,"__esModule",{value:!0}),exports.runAgentHippoSignInFlow=runAgentHippoSignInFlow,exports.ensureAgentHippoSignedIn=ensureAgentHippoSignedIn;const vscode=__importStar(require("vscode")),brokerClient_js_1=require("./brokerClient.js"),authBaseUrl_js_1=require("./authBaseUrl.js"),pkce_js_1=require("./pkce.js"),callbackUriParse_js_1=require("./callbackUriParse.js"),types_js_1=require("./types.js"),sessionStore_js_1=require("./sessionStore.js"),loopbackAuthServer_js_1=require("./loopbackAuthServer.js"),SIGN_IN_TIMEOUT_MS=300*1e3;async function promptForRedirectUrl(t,e){const n=await vscode.window.showInputBox({title:"AgentHippo sign-in",prompt:t,placeHolder:e??"http://127.0.0.1:.../callback?code=...&state=...",ignoreFocusOut:!0});if(n?.trim())return n.trim()}function parseCodeFromRedirect(t,e){return(0,callbackUriParse_js_1.parseAuthCallbackFromLoopbackUrl)(t,e)??(0,callbackUriParse_js_1.parseAuthCallbackFromText)(t,e)}function extractRedirectUriFromText(t){try{const e=new URL(t.trim());return e.search="",e.toString()}catch{return}}async function runAgentHippoSignInFlow(t,e){const n=(0,authBaseUrl_js_1.resolveAuthBrokerBaseUrl)(vscode.workspace.getConfiguration("agentide"),
|
|
2
|
+
"use strict";var __createBinding=this&&this.__createBinding||(Object.create?(function(t,e,n,i){i===void 0&&(i=n);var r=Object.getOwnPropertyDescriptor(e,n);(!r||("get"in r?!e.__esModule:r.writable||r.configurable))&&(r={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,i,r)}):(function(t,e,n,i){i===void 0&&(i=n),t[i]=e[n]})),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?(function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}):function(t,e){t.default=e}),__importStar=this&&this.__importStar||(function(){var t=function(e){return t=Object.getOwnPropertyNames||function(n){var i=[];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(i[i.length]=r);return i},t(e)};return function(e){if(e&&e.__esModule)return e;var n={};if(e!=null)for(var i=t(e),r=0;r<i.length;r++)i[r]!=="default"&&__createBinding(n,e,i[r]);return __setModuleDefault(n,e),n}})();Object.defineProperty(exports,"__esModule",{value:!0}),exports.runAgentHippoSignInFlow=runAgentHippoSignInFlow,exports.ensureAgentHippoSignedIn=ensureAgentHippoSignedIn;const vscode=__importStar(require("vscode")),brokerClient_js_1=require("./brokerClient.js"),authBaseUrl_js_1=require("./authBaseUrl.js"),pkce_js_1=require("./pkce.js"),callbackUriParse_js_1=require("./callbackUriParse.js"),types_js_1=require("./types.js"),sessionStore_js_1=require("./sessionStore.js"),loopbackAuthServer_js_1=require("./loopbackAuthServer.js"),SIGN_IN_TIMEOUT_MS=300*1e3;async function promptForRedirectUrl(t,e){const n=await vscode.window.showInputBox({title:"AgentHippo sign-in",prompt:t,placeHolder:e??"http://127.0.0.1:.../callback?code=...&state=...",ignoreFocusOut:!0});if(n?.trim())return n.trim()}function parseCodeFromRedirect(t,e){return(0,callbackUriParse_js_1.parseAuthCallbackFromLoopbackUrl)(t,e)??(0,callbackUriParse_js_1.parseAuthCallbackFromText)(t,e)}function extractRedirectUriFromText(t){try{const e=new URL(t.trim());return e.search="",e.toString()}catch{return}}async function runAgentHippoSignInFlow(t,e){const n=(0,authBaseUrl_js_1.resolveAuthBrokerBaseUrl)(vscode.workspace.getConfiguration("agentide"),s=>console.warn(s)),i=e?.scopes??[...types_js_1.AUTH_SCOPES],r=new loopbackAuthServer_js_1.AgentHippoLoopbackAuthServer,f=(0,pkce_js_1.generateCodeVerifier)(),h=(0,pkce_js_1.codeChallengeFromVerifier)(f),d=(0,pkce_js_1.generateCodeVerifier)();let u="",l=!1;const g=async s=>{const a=await(0,brokerClient_js_1.exchangeAuthorizationCode)(n,s,u,f),o=await(0,sessionStore_js_1.applyIdeBootstrapForContext)(t,a.access_token);return(0,sessionStore_js_1.persistAuthSession)(t.secrets,a,o,i)};try{await r.start(),u=r.getRedirectUri();const s=(0,brokerClient_js_1.buildAuthorizeUrl)(n,u,h,d);vscode.window.showInformationMessage("A browser window is opening to sign in to AgentHippo.","Copy URL","Open in browser").then(o=>{o==="Copy URL"?vscode.env.clipboard.writeText(s):o==="Open in browser"&&vscode.env.openExternal(vscode.Uri.parse(s))});const a=await vscode.window.withProgress({location:vscode.ProgressLocation.Notification,title:"Sign in to AgentHippo",cancellable:!0},async(o,c)=>{c.onCancellationRequested(()=>{l=!0}),o.report({message:"Opening browser\u2026"}),await Promise.resolve(vscode.env.openExternal(vscode.Uri.parse(s))).catch(()=>!1)?o.report({message:"Waiting for sign-in to complete in your browser\u2026"}):(vscode.env.clipboard.writeText(s),o.report({message:"Could not open browser \u2014 URL copied to clipboard."}));let p;try{p=await r.waitForAuthorizationCode(d,SIGN_IN_TIMEOUT_MS,()=>l||c.isCancellationRequested)}catch{if(l||c.isCancellationRequested)return;const w=await promptForRedirectUrl("Paste the redirect URL from your browser after sign-in.","http://127.0.0.1:.../callback?code=...&state=...");if(w&&(p=parseCodeFromRedirect(w,d)),!p&&(c.isCancellationRequested||l))return;if(!p)throw new Error("Sign-in timed out")}return o.report({message:"Completing sign-in\u2026"}),g(p)});if(a)return vscode.window.showInformationMessage(`Signed in to AgentHippo as ${a.account.label}`),a;if(l)return;vscode.window.showWarningMessage("AgentHippo sign-in did not complete.","Try again").then(o=>{o==="Try again"&&vscode.commands.executeCommand("agenthippo.signIn")});return}catch{const s=await promptForRedirectUrl("Sign-in did not finish automatically. Paste the full redirect URL from your browser.");if(s){const a=parseCodeFromRedirect(s,d),o=extractRedirectUriFromText(s)??u;if(a&&o){u=o;const c=await g(a);return c&&vscode.window.showInformationMessage(`Signed in to AgentHippo as ${c?.account.label??""}`),c}}return}finally{await r.stop()}}async function ensureAgentHippoSignedIn(t,e){const n=await(0,sessionStore_js_1.getAgentHippoAccessToken)(t);if(n?.accessToken)return n;if(!e?.silent)return await runAgentHippoSignInFlow(t),(0,sessionStore_js_1.getAgentHippoAccessToken)(t)}
|
package/out/cli/index.js
CHANGED
|
@@ -417,7 +417,7 @@ else {
|
|
|
417
417
|
${xe}`,ot.message=he)}return ot}onMaintenance(e){let n=Date.now();for(let[r,s]of this.activeSessions.entries())n-s.lastUsedAt>dP&&(this.activeSessions.delete(r),e.info(`[AgentIDE] Closed idle custom engine session (key=${r}, idleMs=${n-s.lastUsedAt})`));for(let{engine:r,manifest:s}of this.instances.values())try{r.onMaintenance?.(e)}catch(i){e.warn(`[AgentIDE] Custom engine "${s.id}" onMaintenance error: ${i}`)}this.workerPool.onMaintenance()}dispose(){this.clearInstances(),this.workerPool.disposeAll(),this.activeSessions.clear(),this.mtimeCache.clear()}};En.CustomEngine=Zu});var pi=T(ue=>{"use strict";var yP=ue&&ue.__createBinding||(Object.create?(function(t,e,n,r){r===void 0&&(r=n);var s=Object.getOwnPropertyDescriptor(e,n);(!s||("get"in s?!e.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,s)}):(function(t,e,n,r){r===void 0&&(r=n),t[r]=e[n]})),wP=ue&&ue.__setModuleDefault||(Object.create?(function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}):function(t,e){t.default=e}),AP=ue&&ue.__importStar||(function(){var t=function(e){return t=Object.getOwnPropertyNames||function(n){var r=[];for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(r[r.length]=s);return r},t(e)};return function(e){if(e&&e.__esModule)return e;var n={};if(e!=null)for(var r=t(e),s=0;s<r.length;s++)r[s]!=="default"&&yP(n,e,r[s]);return wP(n,e),n}})();Object.defineProperty(ue,"__esModule",{value:!0});ue.resolveModelAlias=ue.getSessionDir=ue.createSessionDir=ue.computeSkillsSignature=ue.syncPackToWorkspace=ue.initSessionStoreWithStore=ue.CLIPermissionHandler=ue.AutoApprovePermissionHandler=ue.DirectorySessionStore=ue.FileSessionStore=ue.MemorySessionStore=ue.StaticConfigProvider=ue.NoOpEditTracker=ue.NoOpLogger=ue.ConsoleLogger=void 0;ue.createHeadlessRunner=nd;ue.createEvalRunner=$P;ue.createCLIRunner=DP;var Tm=AP(require("path")),SP=tm(),TP=im(),bP=si(),Lt=Ca();Object.defineProperty(ue,"ConsoleLogger",{enumerable:!0,get:function(){return Lt.ConsoleLogger}});Object.defineProperty(ue,"NoOpLogger",{enumerable:!0,get:function(){return Lt.NoOpLogger}});Object.defineProperty(ue,"NoOpEditTracker",{enumerable:!0,get:function(){return Lt.NoOpEditTracker}});Object.defineProperty(ue,"StaticConfigProvider",{enumerable:!0,get:function(){return Lt.StaticConfigProvider}});Object.defineProperty(ue,"MemorySessionStore",{enumerable:!0,get:function(){return Lt.MemorySessionStore}});Object.defineProperty(ue,"FileSessionStore",{enumerable:!0,get:function(){return Lt.FileSessionStore}});Object.defineProperty(ue,"DirectorySessionStore",{enumerable:!0,get:function(){return Lt.DirectorySessionStore}});Object.defineProperty(ue,"AutoApprovePermissionHandler",{enumerable:!0,get:function(){return Lt.AutoApprovePermissionHandler}});Object.defineProperty(ue,"CLIPermissionHandler",{enumerable:!0,get:function(){return Lt.CLIPermissionHandler}});var bm=ri(),vP=Wu(),vm=xi(),OP=Er(),IP=is(),PP=ti(),NP="AGENTHIPPO_TURN_NONCE";function RP(t,e){let n=t?.trim();if(n)return n;let r=e?.trim();if(r)return r;let s=process.env.AGENTHIPPO_ANALYTICS_USER_ID?.trim()||process.env.AGENTIDE_ANALYTICS_USER_ID?.trim();if(s)return s;let i=process.env.LOGNAME?.trim()||process.env.USER?.trim()||process.env.LNAME?.trim()||process.env.USERNAME?.trim();return i||"anonymous"}function nd(t){if(t.freshState){let E=new Lt.MemorySessionStore;(0,bm.initSessionStoreWithStore)(E)}else(0,bm.initSessionStoreWithStore)(new Lt.DirectorySessionStore(Tm.join(t.storagePath,"provider-sessions")));let e=!!(t.litellmApiKey||t.openaiApiKey||t.anthropicApiKey),n={storagePath:t.storagePath,codexHome:void 0,claudeConfigDir:void 0,litellmApiKey:t.litellmApiKey??(e?void 0:t.apiKey),litellmBaseUrl:t.baseUrl,openaiApiKey:t.openaiApiKey,anthropicApiKey:t.anthropicApiKey,claudeCodeExecutablePath:t.claudeCodeExecutablePath,codexExecutablePath:t.codexExecutablePath},r=t.logger??new Lt.ConsoleLogger("[AgentIDE]"),s=t.editTracker??new Lt.NoOpEditTracker,i=new Lt.StaticConfigProvider({},t.customProviders??{},t.engineSettings??{}),o=t.permissionHandler??new Lt.AutoApprovePermissionHandler,a={logger:r,editTracker:s,configProvider:i,permissionHandler:o,cliEnvPatch:t.cliEnvPatch},c=t.packId??"default",l=E=>(0,vP.computeSkillsSignature)(E,c),d=()=>t.bundledPromptsDir,u,f,p,g=()=>(u||(u=new TP.ClaudeEngine(n,async(E,w)=>l(E)??`headless-${c}`,d)),u),m=()=>(f||(f=new SP.CodexEngine(n,l,d)),f),h=async()=>{if(!p){let{CustomEngine:E}=await Promise.resolve().then(()=>bn(td()));p=new E(n,d)}return p},y=new vm.SessionEventWriter(t.analyticsSessionsDir??Tm.join((0,PP.getAgentideHome)(),"analytics","sessions"));return{async run(E){let w={success:!1,text:"",thinking:"",toolCalls:[]},v=E.emitter??{async thinking(L){w.thinking+=L},async text(L){w.text+=L},async toolStart(L,ee,q){w.toolCalls.push({name:L,input:ee})},async toolEnd(L,ee,q,ge){let ce=w.toolCalls.find(fe=>fe.name===L&&!fe.result);ce&&(ce.result=q,ce.isError=ge)},async progress(L){r.debug(`[Progress] ${L}`)},async question(L){return r.debug(`[Question] ${L.prompt}`),{selected:[],freeText:null,declined:!0}},async error(L){w.error=L},async done(){w.error||(w.success=!0)}},I=(0,bP.generateRequestId)(),b=E.sessionId??`headless-${Date.now()}`,A=t.provenance?.(),S=(0,vm.wrapEmitterWithObservability)(v,y,b,I,(0,IP.currentCredential)()?.userId,A&&{policy_version:A.policyVersion,pack_digest:A.packDigest,engine_version:A.engineVersion,tier:A.tier,instance_id:A.instanceId}),O={modelId:E.modelId,engineId:E.engineId,workspaceRoot:E.workspaceRoot,additionalDirectories:E.additionalDirectories,sessionId:b,analyticsUserId:RP(E.analyticsUserId,t.analyticsUserId),apiKey:E.modelToken??(e?void 0:t.apiKey),baseUrl:t.baseUrl,useLiteLLM:t.useLiteLLM,agentPackId:c,agentPackVersion:t.packVersion,policyVersion:A?.policyVersion,packDigest:A?.packDigest,engineVersion:A?.engineVersion,tier:A?.tier,instanceId:A?.instanceId,permissions:E.permissions,engineHomeUserKey:E.engineHomeUserKey,requestId:I};await S.emitTurnStart();let M=null;if(t.dataPlane&&!E.turnEnv?.[NP])try{M=await t.dataPlane.beginTurn({sessionKey:b})}catch(L){return w.error=`data-plane turn registration failed: ${L instanceof Error?L.message:String(L)}`,await S.error(w.error),await S.emitTurnEnd(!1),w}let N={...a,cliEnvPatch:{...a.cliEnvPatch??{},...(0,OP.buildSessionEnvPatch)({workspaceRoot:E.workspaceRoot,chatSessionId:b,gatewaySessionKey:E.gatewaySessionKey}),...E.turnEnv??{},...M?.env??{}}};try{let L=E.engineId.trim().toLowerCase();L==="claude"?await g().stream(E.message,O,S,N,E.signal):L==="codex"?await m().stream(E.message,O,S,N,E.signal):await(await h()).stream(E.message,O,S,N,E.signal)}catch(L){w.error=L instanceof Error?L.message:String(L),w.success=!1,await S.error(w.error)}finally{try{await M?.release()}catch{}}return await S.emitTurnEnd(w.success),w},dispose(){u?.dispose(),f?.dispose(),p?.dispose()}}}var CP=ri();Object.defineProperty(ue,"initSessionStoreWithStore",{enumerable:!0,get:function(){return CP.initSessionStoreWithStore}});var Tc=Wu();Object.defineProperty(ue,"syncPackToWorkspace",{enumerable:!0,get:function(){return Tc.syncPackToWorkspace}});Object.defineProperty(ue,"computeSkillsSignature",{enumerable:!0,get:function(){return Tc.computeSkillsSignature}});Object.defineProperty(ue,"createSessionDir",{enumerable:!0,get:function(){return Tc.createSessionDir}});Object.defineProperty(ue,"getSessionDir",{enumerable:!0,get:function(){return Tc.getSessionDir}});var kP=$a();Object.defineProperty(ue,"resolveModelAlias",{enumerable:!0,get:function(){return kP.resolveModelAlias}});function $P(t,e,n){return nd({storagePath:t,apiKey:e,baseUrl:n,freshState:!0,logger:new Lt.NoOpLogger})}function DP(t,e,n){return nd({storagePath:t,apiKey:e,baseUrl:n,logger:new Lt.ConsoleLogger("[Agent]")})}});var gi=T(Vt=>{"use strict";var LP=Vt&&Vt.__createBinding||(Object.create?(function(t,e,n,r){r===void 0&&(r=n);var s=Object.getOwnPropertyDescriptor(e,n);(!s||("get"in s?!e.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,s)}):(function(t,e,n,r){r===void 0&&(r=n),t[r]=e[n]})),MP=Vt&&Vt.__setModuleDefault||(Object.create?(function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}):function(t,e){t.default=e}),bc=Vt&&Vt.__importStar||(function(){var t=function(e){return t=Object.getOwnPropertyNames||function(n){var r=[];for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(r[r.length]=s);return r},t(e)};return function(e){if(e&&e.__esModule)return e;var n={};if(e!=null)for(var r=t(e),s=0;s<r.length;s++)r[s]!=="default"&&LP(n,e,r[s]);return MP(n,e),n}})();Object.defineProperty(Vt,"__esModule",{value:!0});Vt.resolvePackPath=Im;Vt.listAvailablePacks=jP;Vt.loadAgentPack=xP;Vt.getDefaultConfig=VP;Vt.getEnvDefaultModel=WP;Vt.getApiConfig=Pm;Vt.hasCredentialForEngine=KP;Vt.getFileAccess=qP;var tn=bc(require("fs")),ft=bc(require("path")),GP=bc(require("os")),UP=bc(require("js-yaml")),vc=Je();function Go(t,e){return process.env[t]??(e?process.env[e]:void 0)}var HP=process.env.AGENTHIPPO_HOME||ft.join(GP.homedir(),vc.AGENTIDE_DIR_NAME),sd=ft.join(HP,"agents"),rd="agents";function Vi(t){if(!tn.existsSync(t))return;let e=ft.join(t,"current.txt");if(tn.existsSync(e)){let r=tn.readFileSync(e,"utf-8").trim(),s=ft.join(t,r);if(tn.existsSync(s))return s}let n=ft.join(t,"agent.yaml");if(tn.existsSync(n))return t;try{let s=tn.readdirSync(t,{withFileTypes:!0}).filter(i=>i.isDirectory()&&/^\d+\.\d+\.\d+/.test(i.name)).map(i=>i.name).sort((i,o)=>{let[a,c,l]=i.split(".").map(p=>parseInt(p,10)||0),[d,u,f]=o.split(".").map(p=>parseInt(p,10)||0);return d!==a?d-a:u!==c?u-c:f-l});for(let i of s){let o=ft.join(t,i),a=ft.join(o,"agent.yaml");if(tn.existsSync(a))return o}}catch{}}function Im(t,e){if(ft.isAbsolute(t)||t.startsWith("./")||t.startsWith("../")){let c=ft.isAbsolute(t)?t:ft.resolve(process.cwd(),t);if(tn.existsSync(c)){try{if(tn.statSync(c).isDirectory())return Vi(c)??c}catch{}return c}return}let n=ft.join(process.cwd(),t),r=Vi(n);if(r)return r;let s=ft.join(process.cwd(),rd,t),i=Vi(s);if(i)return i;if(e){let c=ft.join(e,vc.AGENTIDE_DIR_NAME,rd,t),l=Vi(c);if(l)return l}let o=ft.join(sd,t),a=Vi(o);if(a)return a}function Om(t,e){let n=[];if(!tn.existsSync(t))return n;let r=tn.readdirSync(t,{withFileTypes:!0});for(let s of r){if(!s.isDirectory()||s.name.startsWith("."))continue;let i=ft.join(t,s.name),o=Vi(i);if(!o)continue;let a;o!==i&&(a=ft.basename(o)),n.push({name:s.name,path:o,version:a,source:e})}return n}function jP(t){let e=[],n=new Set;if(t){let s=ft.join(t,vc.AGENTIDE_DIR_NAME,rd),i=Om(s,"workspace");for(let o of i)e.push(o),n.add(o.name)}let r=Om(sd,"user-library");for(let s of r)n.has(s.name)||e.push(s);return e}async function xP(t,e){let n=Im(t,e);if(!n){let u=ft.isAbsolute(t)||t.startsWith("./")||t.startsWith("../")?[ft.resolve(process.cwd(),t)]:[`${process.cwd()}/${t}`,`${process.cwd()}/agents/${t}`,e?`${e}/${vc.AGENTIDE_DIR_NAME}/agents/${t}`:null,`${sd}/${t}`].filter(Boolean);throw new Error(`Agent pack not found: ${t}
|
|
418
418
|
Searched: ${u.join(", ")}`)}let r=ft.join(n,"agent.yaml");if(!tn.existsSync(r))throw new Error(`Missing agent.yaml in pack: ${n}`);let s=tn.readFileSync(r,"utf-8"),i=UP.load(s);FP(i,r);let o=BP(i),a="",c=ft.join(n,"AGENTS.md");return tn.existsSync(c)&&(a=tn.readFileSync(c,"utf-8")),{manifest:o,packPath:n,systemPrompt:a,rules:[]}}function BP(t){let e=t.spec.engine?.trim()??"",n=e.toLowerCase();return e!==n?{...t,spec:{...t.spec,engine:n}}:t}function FP(t,e){let n=[];if(t.metadata?.name||n.push("Missing metadata.name"),t.metadata?.version||n.push("Missing metadata.version"),t.spec?.engine?.trim()||n.push("Missing spec.engine"),t.spec?.engine?.trim().toLowerCase()==="custom"&&n.push('Invalid spec.engine: use the concrete custom engine id (for example, openclaw), not "custom"'),t.spec?.cheapModel!==void 0&&typeof t.spec.cheapModel!="string"&&n.push("Invalid spec.cheapModel: must be a string when provided"),t.spec?.dataPlane!==void 0&&((typeof t.spec.dataPlane!="object"||!t.spec.dataPlane.connector?.trim())&&n.push("Invalid spec.dataPlane: requires a `connector` path (stdio MCP module run broker-side)"),t.spec.dataPlane?.provider!==void 0&&typeof t.spec.dataPlane.provider!="string"&&n.push("Invalid spec.dataPlane.provider: must be a string when provided")),n.length>0)throw new Error(`Invalid agent manifest at ${e}:
|
|
419
419
|
- ${n.join(`
|
|
420
|
-
- `)}`)}function VP(){return{engine:(Go("AGENTHIPPO_DEFAULT_ENGINE","AGENTIDE_DEFAULT_ENGINE")||"claude").trim().toLowerCase(),model:Go("AGENTHIPPO_DEFAULT_MODEL","AGENTIDE_DEFAULT_MODEL")||"claude-sonnet-4-5-20250929"}}function WP(){return Go("AGENTHIPPO_DEFAULT_MODEL","AGENTIDE_DEFAULT_MODEL")?.trim()||void 0}function Pm(){let t=Go("AGENTHIPPO_LITELLM_API_KEY","AGENTIDE_LITELLM_API_KEY"),e=Go("AGENTHIPPO_LITELLM_BASE_URL","AGENTIDE_LITELLM_BASE_URL"),n=process.env.ANTHROPIC_API_KEY||process.env.CLAUDE_API_KEY,r=process.env.OPENAI_API_KEY;return{apiKey:t||n||r,baseUrl:e,useLiteLLM:!!e,litellmApiKey:t,openaiApiKey:r,anthropicApiKey:n}}function KP(t,e=Pm()){if(e.useLiteLLM)return!0;let n=t.trim().toLowerCase();return n==="claude"?!!e.anthropicApiKey:n==="codex"?!!e.openaiApiKey:!0}function qP(t){return t?.manifest.spec.permissions?.fileAccess??"workspace-write"}});var Oc=T((fj,YP)=>{YP.exports={name:"agenthippo",displayName:"Agent Hippo",description:"Agent Hippo - The easiest way to build and test your AI agents",version:"1.110.28",agenthippo:{litellmImageTag:"0.2.16",hippoclawNpm:"@agenthippo-ai/hippoclaw@2026.7.28"},publisher:"agenthippo",private:!0,engines:{vscode:"^1.93.0"},enabledApiProposals:["chatParticipantAdditions","chatParticipantPrivate","languageModelThinkingPart"],activationEvents:["onStartupFinished","onCommand:agentide.customEngine.list","onCommand:agentide.engine.install","onCommand:agentide.engine.uninstall","onChatParticipant:agentide.claude","onChatParticipant:agentide.codex"],main:"./out/extension.js",contributes:{authentication:[{id:"agenthippo",label:"AgentHippo"}],chatParticipants:[{id:"agentide.claude",name:"claude",fullName:"Claude Agent",description:"AI coding agent powered by Anthropic's Claude Agent SDK",isSticky:!1},{id:"agentide.codex",name:"codex",fullName:"Codex Agent",description:"AI coding agent powered by OpenAI's Codex SDK",isSticky:!1}],mcpServerDefinitionProviders:[{id:"bundled-mcp-servers",label:"Agent Hippo Bundled MCP Servers"}],commands:[{command:"agentide.skills.syncRemote",title:"Sync Remote Skills",category:"Agent Hippo"},{command:"agentide.clearManagedResources",title:"Clear Managed Resources",category:"Agent Hippo"},{command:"agentide.showManagedResources",title:"Show Managed Resources",category:"Agent Hippo"},{command:"agentide.cleanupSessionLogs",title:"Cleanup Old Session Logs",category:"Agent Hippo"},{command:"agentide.pack.install",title:"Install Agent Pack",category:"Agent Hippo"},{command:"agentide.pack.list",title:"List Installed Packs",category:"Agent Hippo"},{command:"agentide.customEngine.list",title:"List Custom Engines",category:"Agent Hippo"},{command:"agentide.pack.uninstall",title:"Uninstall Agent Pack",category:"Agent Hippo"},{command:"agentide.pack.getActive",title:"Get Active Agent Pack",category:"Agent Hippo"},{command:"agentide.pack.create",title:"Create Agent Pack",category:"Agent Hippo"},{command:"agentide.pack.share",title:"Share Agent to User Library",category:"Agent Hippo"},{command:"agentide.pack.publish",title:"Publish Agent to Store",category:"Agent Hippo"},{command:"agentide.skill.share",title:"Save Skill to User Library",category:"Agent Hippo"},{command:"agentide.skill.publish",title:"Publish Skill to Store",category:"Agent Hippo"},{command:"agentide.mcp.share",title:"Save MCP Server to User Library",category:"Agent Hippo"},{command:"agentide.mcp.publish",title:"Publish MCP Server to Store",category:"Agent Hippo"},{command:"agenthippo.signIn",title:"Sign In to AgentHippo",category:"Agent Hippo"},{command:"agenthippo.signOut",title:"Sign Out of AgentHippo",category:"Agent Hippo"},{command:"agentide.store.find",title:"Install Packs, Skills, MCP Tools, Engines, Spotlight Views",category:"Agent Hippo"},{command:"agentide.engine.install",title:"Install Custom Engine",category:"Agent Hippo"},{command:"agentide.engine.uninstall",title:"Uninstall Custom Engine",category:"Agent Hippo"},{command:"agentide.engine.publish",title:"Publish Engine to Store",category:"Agent Hippo"},{command:"agentide.spotlight.publish",title:"Publish Spotlight View to Store (Full Bundle)",category:"Agent Hippo"},{command:"agentide.view.install",title:"Install Spotlight View",category:"Agent Hippo"},{command:"agentide.store.sync",title:"Sync Store Index",category:"Agent Hippo"},{command:"agentide.store.setup",title:"Setup Store Index Automation",category:"Agent Hippo"},{command:"agentide.pack.browseLibrary",title:"Browse User Library",category:"Agent Hippo"},{command:"agentide.pack.update",title:"Update Agent from User Library",category:"Agent Hippo"},{command:"agentide.pack.duplicate",title:"Duplicate Agent",category:"Agent Hippo"},{command:"agentide.pack.refresh",title:"Refresh Agent Packs",category:"Agent Hippo"},{command:"agentide.pack.resetDefault",title:"Reset Default Agent",category:"Agent Hippo"},{command:"agentide.gateway.connect",title:"Connect to Gateway",category:"Agent Hippo"},{command:"agentide.gateway.disconnect",title:"Disconnect from Gateway",category:"Agent Hippo"},{command:"agentide.gateway.toggle",title:"Toggle Gateway Connection",category:"Agent Hippo"},{command:"agentide.gateway.status",title:"Show Gateway Status",category:"Agent Hippo"},{command:"agentide.gateway.connectSession",title:"Connect Chat to Gateway",category:"Agent Hippo"},{command:"agentide.gateway.listSessions",title:"Show Connected Gateway Sessions",category:"Agent Hippo"},{command:"agentide.gateway.resetMappings",title:"Reset Agent Mappings (Gateway)",category:"Agent Hippo"},{command:"agentide.anywhere.toggle",title:"Agent Anywhere",category:"Agent Hippo"},{command:"agentide.anywhere.install",title:"Install Agent Anywhere (Gateway)",category:"Agent Hippo"},{command:"agentide.anywhere.uninstall",title:"Uninstall Agent Anywhere (OpenClaw CLI)",category:"Agent Hippo"},{command:"agentide.anywhere.start",title:"Start Agent Anywhere",category:"Agent Hippo"},{command:"agentide.anywhere.stop",title:"Stop Agent Anywhere",category:"Agent Hippo"},{command:"agentide.anywhere.status",title:"Agent Anywhere Status",category:"Agent Hippo"},{command:"agentide.anywhere.connect",title:"Connect Agent Anywhere",category:"Agent Hippo"},{command:"agentide.anywhere.disconnect",title:"Disconnect Agent Anywhere",category:"Agent Hippo"},{command:"agentide.anywhere.openConfig",title:"Open Agent Anywhere Config",category:"Agent Hippo"},{command:"agentide.anywhere.openWebUI",title:"Open Agent Anywhere Web UI",category:"Agent Hippo"},{command:"agentide.anywhere.detect",title:"Detect Agent Anywhere Gateway",category:"Agent Hippo"},{command:"agentide.anywhere.meetAgent",title:"Meet Agent Anywhere...",category:"Agent Hippo"},{command:"agentide.setupCli",title:"Setup AgentHippo CLI",category:"Agent Hippo"},{command:"agentide.cli.status",title:"AgentHippo CLI Status",category:"Agent Hippo"},{command:"agentide.installBundledTools",title:"Install Bundled Tools (rg, sg, tree-sitter)",category:"Agent Hippo"},{command:"agentide.setupAnalytics",title:"Install Spotlight (OTEL + DuckDB)",category:"Agent Hippo"},{command:"agentide.analytics.toggle",title:"Manage Spotlight",category:"Agent Hippo"},{command:"agentide.analytics.start",title:"Start Spotlight (OTEL + LiteLLM)",category:"Agent Hippo"},{command:"agentide.analytics.stop",title:"Stop Spotlight (OTEL + LiteLLM)",category:"Agent Hippo"},{command:"agentide.analytics.status",title:"Show Spotlight Status",category:"Agent Hippo"},{command:"agentide.analytics.query",title:"Query Spotlight (Cost, Sessions)",category:"Agent Hippo"},{command:"agentide.analytics.openDashboard",title:"Open Spotlight Dashboard",category:"Agent Hippo"},{command:"agentide.fleet.open",title:"Open Agent Team",category:"Agent Hippo"},{command:"agentide.analytics.clearArtifacts",title:"Clear Spotlight Artifacts",category:"Agent Hippo"},{command:"agentide.analytics.regenerateDefaults",title:"Regenerate Default Charts",category:"Agent Hippo"}],menus:{ChatModePicker:[{command:"agentide.anywhere.meetAgent",group:"navigation@30",when:"agentide.anywhere.enabled && !agentide.anywhere.hidden",icon:"$(broadcast)"}]},viewsContainers:{activitybar:[{id:"agentide-analytics",title:"Agent Hippo",icon:"$(graph)"},{id:"agentide-team",title:"Agent Team",icon:"$(organization)"}]},views:{"agentide-analytics":[{type:"webview",id:"agentide.analyticsView",name:"Spotlight"}],"agentide-team":[{type:"webview",id:"agentide.teamView",name:"Agent Team"}]},configuration:{title:"Agent Hippo",properties:{"agentide.permissions.fileAccess":{type:"string",scope:"application",enum:["read-only","workspace-write","full"],default:"full",markdownDescription:"Filesystem access scope (applies to all engines).\n- `read-only`: Read/search only (no file writes)\n- `workspace-write`: Read/write within workspace folders only\n- `full`: Full filesystem access (yolo, default)\n\n**Security**: Agent manifest permissions take precedence - user cannot override stricter pack permissions."},"agentide.claude.maxThinkingTokens":{type:"number",default:1024,description:"Claude extended thinking budget (tokens). Set to 0 to disable. Requires a compatible model; Anthropic requires >=1024 for extended thinking."},"agentide.claude.cheapModel":{type:"string",default:"",description:"Optional cheap Claude model for subagents/background routing (for example, claude-haiku-4-5-20251001). Main model selection still comes from the model picker."},"agentide.sessions.retentionDays":{type:"number",default:7,minimum:1,maximum:365,description:"Number of days to retain session logs before cleanup. Cleanup runs on startup (best-effort) and via the 'Agent Hippo: Cleanup Old Session Logs' command."},"agentide.fallbackDirectory":{type:"string",scope:"machine",default:".agent-hippo",description:"Directory name (relative to home) used as workspace when no folder is open. Default: ~/.agent-hippo"},"agentide.additionalEngineDir":{type:"string",scope:"machine",default:"",description:"Additional directories to scan for custom engines. Machine-scoped: engines discovered here are executed, so a workspace must not be able to point this at attacker-controlled code."},"agentide.auth.baseUrl":{type:"string",scope:"application",default:"https://auth.agenthippo.ai",markdownDescription:"AgentHippo auth broker base URL for team sign-in and bootstrap (`agenthippo.signIn`).\n\n**Application-scoped on purpose**: this endpoint receives your PKCE authorization code and your AgentHippo access token, and its `/api/ide/bootstrap` response is written into your model-routing settings. A workspace must never be able to redirect it. Must be `https://` (plain `http://` is accepted only for loopback dev hosts)."},"agentide.cli.path":{type:"string",scope:"machine",default:"",description:"Optional absolute path to the AgentHippo CLI binary. If unset, Agent Hippo auto-detects a bundled binary or falls back to PATH. Machine-scoped: this path is executed."},"agentide.cli.installSource":{type:"string",scope:"machine",default:"",description:"Optional setup source for AgentHippo CLI. Supports HTTPS URL to a prebuilt binary, or local file path. If empty, setup builds from the local repository. Machine-scoped: this source is downloaded and executed."},"agentide.storeRepo":{type:"string",scope:"application",default:"agenthippoai/agenthippo-store",markdownDescription:"GitHub repository for pack store (owner/repo format).\n\nDefault: `agenthippoai/agenthippo-store` ([github.com/agenthippoai/agenthippo-store](https://github.com/agenthippoai/agenthippo-store))\n\nSet to your own repo (e.g., `mycompany/our-packs`) to use a private or custom store."},"agentide.store.indexUrl":{type:"string",scope:"application",default:"https://agenthippoai.github.io/agenthippo-store",markdownDescription:`Base URL for the store index (Orama + metadata are loaded from paths under this URL).
|
|
420
|
+
- `)}`)}function VP(){return{engine:(Go("AGENTHIPPO_DEFAULT_ENGINE","AGENTIDE_DEFAULT_ENGINE")||"claude").trim().toLowerCase(),model:Go("AGENTHIPPO_DEFAULT_MODEL","AGENTIDE_DEFAULT_MODEL")||"claude-sonnet-4-5-20250929"}}function WP(){return Go("AGENTHIPPO_DEFAULT_MODEL","AGENTIDE_DEFAULT_MODEL")?.trim()||void 0}function Pm(){let t=Go("AGENTHIPPO_LITELLM_API_KEY","AGENTIDE_LITELLM_API_KEY"),e=Go("AGENTHIPPO_LITELLM_BASE_URL","AGENTIDE_LITELLM_BASE_URL"),n=process.env.ANTHROPIC_API_KEY||process.env.CLAUDE_API_KEY,r=process.env.OPENAI_API_KEY;return{apiKey:t||n||r,baseUrl:e,useLiteLLM:!!e,litellmApiKey:t,openaiApiKey:r,anthropicApiKey:n}}function KP(t,e=Pm()){if(e.useLiteLLM)return!0;let n=t.trim().toLowerCase();return n==="claude"?!!e.anthropicApiKey:n==="codex"?!!e.openaiApiKey:!0}function qP(t){return t?.manifest.spec.permissions?.fileAccess??"workspace-write"}});var Oc=T((fj,YP)=>{YP.exports={name:"agenthippo",displayName:"Agent Hippo",description:"Agent Hippo - The easiest way to build and test your AI agents",version:"1.110.30",agenthippo:{litellmImageTag:"0.2.16",hippoclawNpm:"@agenthippo-ai/hippoclaw@2026.7.28"},publisher:"agenthippo",private:!0,engines:{vscode:"^1.93.0"},enabledApiProposals:["chatParticipantAdditions","chatParticipantPrivate","languageModelThinkingPart"],activationEvents:["onStartupFinished","onCommand:agentide.customEngine.list","onCommand:agentide.engine.install","onCommand:agentide.engine.uninstall","onChatParticipant:agentide.claude","onChatParticipant:agentide.codex"],main:"./out/extension.js",contributes:{authentication:[{id:"agenthippo",label:"AgentHippo"}],chatParticipants:[{id:"agentide.claude",name:"claude",fullName:"Claude Agent",description:"AI coding agent powered by Anthropic's Claude Agent SDK",isSticky:!1},{id:"agentide.codex",name:"codex",fullName:"Codex Agent",description:"AI coding agent powered by OpenAI's Codex SDK",isSticky:!1}],mcpServerDefinitionProviders:[{id:"bundled-mcp-servers",label:"Agent Hippo Bundled MCP Servers"}],commands:[{command:"agentide.skills.syncRemote",title:"Sync Remote Skills",category:"Agent Hippo"},{command:"agentide.clearManagedResources",title:"Clear Managed Resources",category:"Agent Hippo"},{command:"agentide.showManagedResources",title:"Show Managed Resources",category:"Agent Hippo"},{command:"agentide.cleanupSessionLogs",title:"Cleanup Old Session Logs",category:"Agent Hippo"},{command:"agentide.pack.install",title:"Install Agent Pack",category:"Agent Hippo"},{command:"agentide.pack.list",title:"List Installed Packs",category:"Agent Hippo"},{command:"agentide.customEngine.list",title:"List Custom Engines",category:"Agent Hippo"},{command:"agentide.pack.uninstall",title:"Uninstall Agent Pack",category:"Agent Hippo"},{command:"agentide.pack.getActive",title:"Get Active Agent Pack",category:"Agent Hippo"},{command:"agentide.pack.create",title:"Create Agent Pack",category:"Agent Hippo"},{command:"agentide.pack.share",title:"Share Agent to User Library",category:"Agent Hippo"},{command:"agentide.pack.publish",title:"Publish Agent to Store",category:"Agent Hippo"},{command:"agentide.skill.share",title:"Save Skill to User Library",category:"Agent Hippo"},{command:"agentide.skill.publish",title:"Publish Skill to Store",category:"Agent Hippo"},{command:"agentide.mcp.share",title:"Save MCP Server to User Library",category:"Agent Hippo"},{command:"agentide.mcp.publish",title:"Publish MCP Server to Store",category:"Agent Hippo"},{command:"agenthippo.signIn",title:"Sign In to AgentHippo",category:"Agent Hippo"},{command:"agenthippo.signOut",title:"Sign Out of AgentHippo",category:"Agent Hippo"},{command:"agentide.store.find",title:"Install Packs, Skills, MCP Tools, Engines, Spotlight Views",category:"Agent Hippo"},{command:"agentide.engine.install",title:"Install Custom Engine",category:"Agent Hippo"},{command:"agentide.engine.uninstall",title:"Uninstall Custom Engine",category:"Agent Hippo"},{command:"agentide.engine.publish",title:"Publish Engine to Store",category:"Agent Hippo"},{command:"agentide.spotlight.publish",title:"Publish Spotlight View to Store (Full Bundle)",category:"Agent Hippo"},{command:"agentide.view.install",title:"Install Spotlight View",category:"Agent Hippo"},{command:"agentide.store.sync",title:"Sync Store Index",category:"Agent Hippo"},{command:"agentide.store.setup",title:"Setup Store Index Automation",category:"Agent Hippo"},{command:"agentide.pack.browseLibrary",title:"Browse User Library",category:"Agent Hippo"},{command:"agentide.pack.update",title:"Update Agent from User Library",category:"Agent Hippo"},{command:"agentide.pack.duplicate",title:"Duplicate Agent",category:"Agent Hippo"},{command:"agentide.pack.refresh",title:"Refresh Agent Packs",category:"Agent Hippo"},{command:"agentide.pack.resetDefault",title:"Reset Default Agent",category:"Agent Hippo"},{command:"agentide.gateway.connect",title:"Connect to Gateway",category:"Agent Hippo"},{command:"agentide.gateway.disconnect",title:"Disconnect from Gateway",category:"Agent Hippo"},{command:"agentide.gateway.toggle",title:"Toggle Gateway Connection",category:"Agent Hippo"},{command:"agentide.gateway.status",title:"Show Gateway Status",category:"Agent Hippo"},{command:"agentide.gateway.connectSession",title:"Connect Chat to Gateway",category:"Agent Hippo"},{command:"agentide.gateway.listSessions",title:"Show Connected Gateway Sessions",category:"Agent Hippo"},{command:"agentide.gateway.resetMappings",title:"Reset Agent Mappings (Gateway)",category:"Agent Hippo"},{command:"agentide.anywhere.toggle",title:"Agent Anywhere",category:"Agent Hippo"},{command:"agentide.anywhere.install",title:"Install Agent Anywhere (Gateway)",category:"Agent Hippo"},{command:"agentide.anywhere.uninstall",title:"Uninstall Agent Anywhere (OpenClaw CLI)",category:"Agent Hippo"},{command:"agentide.anywhere.start",title:"Start Agent Anywhere",category:"Agent Hippo"},{command:"agentide.anywhere.stop",title:"Stop Agent Anywhere",category:"Agent Hippo"},{command:"agentide.anywhere.status",title:"Agent Anywhere Status",category:"Agent Hippo"},{command:"agentide.anywhere.connect",title:"Connect Agent Anywhere",category:"Agent Hippo"},{command:"agentide.anywhere.disconnect",title:"Disconnect Agent Anywhere",category:"Agent Hippo"},{command:"agentide.anywhere.openConfig",title:"Open Agent Anywhere Config",category:"Agent Hippo"},{command:"agentide.anywhere.openWebUI",title:"Open Agent Anywhere Web UI",category:"Agent Hippo"},{command:"agentide.anywhere.detect",title:"Detect Agent Anywhere Gateway",category:"Agent Hippo"},{command:"agentide.anywhere.meetAgent",title:"Meet Agent Anywhere...",category:"Agent Hippo"},{command:"agentide.setupCli",title:"Setup AgentHippo CLI",category:"Agent Hippo"},{command:"agentide.cli.status",title:"AgentHippo CLI Status",category:"Agent Hippo"},{command:"agentide.installBundledTools",title:"Install Bundled Tools (rg, sg, tree-sitter)",category:"Agent Hippo"},{command:"agentide.setupAnalytics",title:"Install Spotlight (OTEL + DuckDB)",category:"Agent Hippo"},{command:"agentide.analytics.toggle",title:"Manage Spotlight",category:"Agent Hippo"},{command:"agentide.analytics.start",title:"Start Spotlight (OTEL + LiteLLM)",category:"Agent Hippo"},{command:"agentide.analytics.stop",title:"Stop Spotlight (OTEL + LiteLLM)",category:"Agent Hippo"},{command:"agentide.analytics.status",title:"Show Spotlight Status",category:"Agent Hippo"},{command:"agentide.analytics.query",title:"Query Spotlight (Cost, Sessions)",category:"Agent Hippo"},{command:"agentide.analytics.openDashboard",title:"Open Spotlight Dashboard",category:"Agent Hippo"},{command:"agentide.fleet.open",title:"Open Agent Team",category:"Agent Hippo"},{command:"agentide.analytics.clearArtifacts",title:"Clear Spotlight Artifacts",category:"Agent Hippo"},{command:"agentide.analytics.regenerateDefaults",title:"Regenerate Default Charts",category:"Agent Hippo"}],menus:{ChatModePicker:[{command:"agentide.anywhere.meetAgent",group:"navigation@30",when:"agentide.anywhere.enabled && !agentide.anywhere.hidden",icon:"$(broadcast)"}]},viewsContainers:{activitybar:[{id:"agentide-analytics",title:"Agent Hippo",icon:"$(graph)"},{id:"agentide-team",title:"Agent Team",icon:"$(organization)"}]},views:{"agentide-analytics":[{type:"webview",id:"agentide.analyticsView",name:"Spotlight"}],"agentide-team":[{type:"webview",id:"agentide.teamView",name:"Agent Team"}]},configuration:{title:"Agent Hippo",properties:{"agentide.permissions.fileAccess":{type:"string",scope:"application",enum:["read-only","workspace-write","full"],default:"full",markdownDescription:"Filesystem access scope (applies to all engines).\n- `read-only`: Read/search only (no file writes)\n- `workspace-write`: Read/write within workspace folders only\n- `full`: Full filesystem access (yolo, default)\n\n**Security**: Agent manifest permissions take precedence - user cannot override stricter pack permissions."},"agentide.claude.maxThinkingTokens":{type:"number",default:1024,description:"Claude extended thinking budget (tokens). Set to 0 to disable. Requires a compatible model; Anthropic requires >=1024 for extended thinking."},"agentide.claude.cheapModel":{type:"string",default:"",description:"Optional cheap Claude model for subagents/background routing (for example, claude-haiku-4-5-20251001). Main model selection still comes from the model picker."},"agentide.sessions.retentionDays":{type:"number",default:7,minimum:1,maximum:365,description:"Number of days to retain session logs before cleanup. Cleanup runs on startup (best-effort) and via the 'Agent Hippo: Cleanup Old Session Logs' command."},"agentide.fallbackDirectory":{type:"string",scope:"machine",default:".agent-hippo",description:"Directory name (relative to home) used as workspace when no folder is open. Default: ~/.agent-hippo"},"agentide.additionalEngineDir":{type:"string",scope:"machine",default:"",description:"Additional directories to scan for custom engines. Machine-scoped: engines discovered here are executed, so a workspace must not be able to point this at attacker-controlled code."},"agentide.auth.baseUrl":{type:"string",scope:"application",default:"https://auth.agenthippo.ai",markdownDescription:"AgentHippo auth broker base URL for team sign-in and bootstrap (`agenthippo.signIn`).\n\n**Application-scoped on purpose**: this endpoint receives your PKCE authorization code and your AgentHippo access token, and its `/api/ide/bootstrap` response is written into your model-routing settings. A workspace must never be able to redirect it. Must be `https://` (plain `http://` is accepted only for loopback dev hosts)."},"agentide.cli.path":{type:"string",scope:"machine",default:"",description:"Optional absolute path to the AgentHippo CLI binary. If unset, Agent Hippo auto-detects a bundled binary or falls back to PATH. Machine-scoped: this path is executed."},"agentide.cli.installSource":{type:"string",scope:"machine",default:"",description:"Optional setup source for AgentHippo CLI. Supports HTTPS URL to a prebuilt binary, or local file path. If empty, setup builds from the local repository. Machine-scoped: this source is downloaded and executed."},"agentide.storeRepo":{type:"string",scope:"application",default:"agenthippoai/agenthippo-store",markdownDescription:"GitHub repository for pack store (owner/repo format).\n\nDefault: `agenthippoai/agenthippo-store` ([github.com/agenthippoai/agenthippo-store](https://github.com/agenthippoai/agenthippo-store))\n\nSet to your own repo (e.g., `mycompany/our-packs`) to use a private or custom store."},"agentide.store.indexUrl":{type:"string",scope:"application",default:"https://agenthippoai.github.io/agenthippo-store",markdownDescription:`Base URL for the store index (Orama + metadata are loaded from paths under this URL).
|
|
421
421
|
|
|
422
422
|
Default: \`https://agenthippoai.github.io/agenthippo-store\`
|
|
423
423
|
|
|
@@ -451,7 +451,7 @@ When enabled, JSONL trace files are incrementally converted to an indexed DuckDB
|
|
|
451
451
|
|
|
452
452
|
**Application-scoped on purpose**: this host receives your Anthropic key and your prompts.`},"agentide.AgentHippoModelGateway.url":{type:"string",scope:"application",default:"",markdownDescription:"AgentHippo managed model gateway base URL (default `https://gateway.agenthippo.ai/v1`).\n\n**Application-scoped on purpose**: this host receives your AgentHippo sign-in JWT and your prompts."},"agentide.skills.remote.enabled":{type:"boolean",scope:"application",default:!1,description:"Sync skills from a remote skill registry into the default agent."},"agentide.skills.remote.registryUrl":{type:"string",scope:"application",default:"",markdownDescription:`Remote skill registry base URL.
|
|
453
453
|
|
|
454
|
-
**Application-scoped on purpose**: skills fetched from here become agent instructions and tool definitions on this machine, so this is an install source.`},"agentide.skills.remote.syncOnStartup":{type:"boolean",scope:"application",default:!0,description:"Sync remote skills on extension startup (requires agentide.skills.remote.enabled)."},"agentide.skills.remote.allowOverwriteLocal":{type:"boolean",scope:"application",default:!1,description:"Allow remote skills to overwrite locally-authored skill files of the same name."},"agentide.skills.remote.timeoutMs":{type:"number",scope:"application",default:8e3,minimum:500,maximum:12e4,description:"Timeout (milliseconds) for remote skill registry requests."}}}},capabilities:{untrustedWorkspaces:{supported:"limited",description:"Agent Hippo runs AI agents that read your code, spawn processes, and send prompts to remote model endpoints. In an untrusted workspace the security-relevant settings below are ignored in favour of your user/application values, and remote agent packs (`spec.remote.url`) will not receive your AgentHippo access token without an explicit per-host approval.",restrictedConfigurations:["agentide.auth.baseUrl","agentide.storeRepo","agentide.store.indexUrl","agentide.anywhere.installSource","agentide.anywhere.enabled","agentide.anywhere.autoStart","agentide.anywhere.autoConnect","agentide.anywhere.port","agentide.anywhere.hidden","agentide.cli.path","agentide.cli.installSource","agentide.additionalEngineDir","agentide.fallbackDirectory","agentide.permissions.fileAccess","agentide.gateway.url","agentide.gateway.token","agentide.gateway.autoConnect","agentide.gateway.autoRegister","agentide.gateway.agents","agentide.providers.litellm.baseUrl","agentide.providers.anthropic.baseUrl","agentide.AgentHippoModelGateway.url","agentide.skills.remote.enabled","agentide.skills.remote.registryUrl","agentide.skills.remote.syncOnStartup","agentide.skills.remote.allowOverwriteLocal","agentide.analytics.fleetSources","agentide.analytics.fleetScriptSources","agentide.analytics.defaultDashboardPrompt","agentide.analytics.duckdb.tempDirectory"]}},dependencies:{"@agenthippo-ai/evidence-ship":"0.2.4","@anthropic-ai/claude-agent-sdk":"0.3.
|
|
454
|
+
**Application-scoped on purpose**: skills fetched from here become agent instructions and tool definitions on this machine, so this is an install source.`},"agentide.skills.remote.syncOnStartup":{type:"boolean",scope:"application",default:!0,description:"Sync remote skills on extension startup (requires agentide.skills.remote.enabled)."},"agentide.skills.remote.allowOverwriteLocal":{type:"boolean",scope:"application",default:!1,description:"Allow remote skills to overwrite locally-authored skill files of the same name."},"agentide.skills.remote.timeoutMs":{type:"number",scope:"application",default:8e3,minimum:500,maximum:12e4,description:"Timeout (milliseconds) for remote skill registry requests."}}}},capabilities:{untrustedWorkspaces:{supported:"limited",description:"Agent Hippo runs AI agents that read your code, spawn processes, and send prompts to remote model endpoints. In an untrusted workspace the security-relevant settings below are ignored in favour of your user/application values, and remote agent packs (`spec.remote.url`) will not receive your AgentHippo access token without an explicit per-host approval.",restrictedConfigurations:["agentide.auth.baseUrl","agentide.storeRepo","agentide.store.indexUrl","agentide.anywhere.installSource","agentide.anywhere.enabled","agentide.anywhere.autoStart","agentide.anywhere.autoConnect","agentide.anywhere.port","agentide.anywhere.hidden","agentide.cli.path","agentide.cli.installSource","agentide.additionalEngineDir","agentide.fallbackDirectory","agentide.permissions.fileAccess","agentide.gateway.url","agentide.gateway.token","agentide.gateway.autoConnect","agentide.gateway.autoRegister","agentide.gateway.agents","agentide.providers.litellm.baseUrl","agentide.providers.anthropic.baseUrl","agentide.AgentHippoModelGateway.url","agentide.skills.remote.enabled","agentide.skills.remote.registryUrl","agentide.skills.remote.syncOnStartup","agentide.skills.remote.allowOverwriteLocal","agentide.analytics.fleetSources","agentide.analytics.fleetScriptSources","agentide.analytics.defaultDashboardPrompt","agentide.analytics.duckdb.tempDirectory"]}},dependencies:{"@agenthippo-ai/evidence-ship":"0.2.4","@anthropic-ai/claude-agent-sdk":"0.3.258","@anthropic-ai/sdk":"0.123.0","@cfworker/json-schema":"^4.1.1","@mariozechner/pi-ai":"0.54.0","@modelcontextprotocol/sdk":"^1.30.0","@openai/codex":"0.147.0","@openai/codex-sdk":"0.147.0","@orama/orama":"^3.1.6",commander:"^14.0.2",hono:"^4.11.5","js-yaml":"^4.1.1",zod:"^4.3.6"},devDependencies:{"@types/js-yaml":"^4.0.9","@types/mocha":"^10.0.10","@types/node":"^25.x"},scripts:{postinstall:"node ./scripts/vendor-tools.mjs --skip-if-present || true",compile:"node ../../node_modules/typescript/bin/tsc -p ./ && node ./scripts/generate-embedded-custom-engine-runner.mjs && node ./scripts/copy-compile-assets.mjs",watch:"node ../../node_modules/typescript/bin/tsc -watch -p ./",test:"node ../../node_modules/mocha/bin/mocha","test:serve-load":"bash ../../scripts/load/run-serve-load.sh","test:serve-load:full":"bash ../../scripts/load/run-serve-load.sh --full",cli:"node out/cli/index.js","build:tools":"node ./scripts/vendor-tools.mjs --clean","build:cli":"./scripts/build-cli.sh","build:cli:macos-arm64":"./scripts/build-cli.sh --target bun-darwin-arm64 --outfile dist/agenthippo-macos-arm64","build:cli:macos-x64":"./scripts/build-cli.sh --target bun-darwin-x64 --outfile dist/agenthippo-macos-x64","build:cli:linux-x64":"./scripts/build-cli.sh --target bun-linux-x64 --outfile dist/agenthippo-linux-x64","build:cli:linux-arm64":"./scripts/build-cli.sh --target bun-linux-arm64 --outfile dist/agenthippo-linux-arm64"}}});var hi=T(pt=>{"use strict";var XP=pt&&pt.__createBinding||(Object.create?(function(t,e,n,r){r===void 0&&(r=n);var s=Object.getOwnPropertyDescriptor(e,n);(!s||("get"in s?!e.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,s)}):(function(t,e,n,r){r===void 0&&(r=n),t[r]=e[n]})),JP=pt&&pt.__setModuleDefault||(Object.create?(function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}):function(t,e){t.default=e}),od=pt&&pt.__importStar||(function(){var t=function(e){return t=Object.getOwnPropertyNames||function(n){var r=[];for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(r[r.length]=s);return r},t(e)};return function(e){if(e&&e.__esModule)return e;var n={};if(e!=null)for(var r=t(e),s=0;s<r.length;s++)r[s]!=="default"&&XP(n,e,r[s]);return JP(n,e),n}})();Object.defineProperty(pt,"__esModule",{value:!0});pt.getDepsDir=Ki;pt.resolveFromOwnDeps=Cm;pt.getClaudeAgentSdkPlatformPackageName=tN;pt.resolveClaudeCodeExecutablePath=Nc;pt.resolveClaudeCodeFromOwnDeps=$m;pt.getUnsupportedClaudePlatformHint=rN;pt.areDepsInstalled=Dm;pt.installDeps=Lm;pt.isAutoInstallDisabled=Mm;pt.ensureDeps=sN;pt.getClaudeSdkCliPath=iN;pt.getCodexSdkBinaryPath=oN;pt.installedEngineVersion=cN;var nn=od(require("fs")),zP=od(require("os")),Ze=od(require("path")),Nm=require("child_process"),Gr=Rn(),Pc=Je();function QP(){let e=(()=>{try{return Oc()}catch{return}})()?.dependencies??{};return{"@anthropic-ai/claude-agent-sdk":e["@anthropic-ai/claude-agent-sdk"]??"0.3.227","@openai/codex":e["@openai/codex"]??"0.147.0","@openai/codex-sdk":e["@openai/codex-sdk"]??"0.147.0"}}var Rm=QP();function Ki(){let t=Ze.dirname(process.execPath),e=Ze.join(t,Pc.AGENTIDE_DEPS_DIR_NAME);if(nn.existsSync(e))return e;try{return nn.accessSync(t,nn.constants.W_OK),e}catch{return Ze.join(zP.homedir(),Pc.AGENTIDE_DIR_NAME,Pc.AGENTIDE_DEPS_DIR_NAME)}}function Cm(t,e){let n=__dirname;for(let r=0;r<8;r++){let s=Ze.join(n,"node_modules",...t.split("/"),...e.split("/"));try{if(nn.existsSync(s))return s}catch{}let i=Ze.dirname(n);if(i===n)break;n=i}}function Wi(t){try{return nn.existsSync(t)}catch{return!1}}function id(t,e){if(e==="x64")return t==="musl"?"/lib/ld-musl-x86_64.so.1":"/lib64/ld-linux-x86-64.so.2";if(e==="arm64")return t==="musl"?"/lib/ld-musl-aarch64.so.1":"/lib/ld-linux-aarch64.so.1"}function km(t,e){return t===process.platform&&e===process.arch}var Ic;function ZP(){return Ic||(Ic=(()=>{try{let s=process.report?.getReport?.();if((typeof s=="object"&&s?s.header:void 0)?.glibcVersionRuntime)return"glibc"}catch{}let t=id("glibc",process.arch),e=id("musl",process.arch),n=t?Wi(t):!1,r=e?Wi(e):!1;if(r&&!n)return"musl";if(n&&!r)return"glibc";if(Wi("/etc/alpine-release"))return"musl";try{if(/musl/i.test((0,Nm.execSync)("ldd --version 2>&1 || true",{encoding:"utf8"})))return"musl"}catch{}return"glibc"})(),Ic)}function eN(t,e){return km(t,e)?n=>{let r=id(n,e);return r?Wi(r):!1}:()=>!0}function ad(t={}){let e=t.platform??process.platform,n=t.arch??process.arch,r=n==="arm64"?"arm64":n==="x64"?"x64":void 0;if(!r)return[];if(e==="darwin")return[{name:`@anthropic-ai/claude-agent-sdk-darwin-${r}`}];if(e==="win32")return[{name:`@anthropic-ai/claude-agent-sdk-win32-${r}`}];if(e==="linux"){let s=t.libc??(km(e,n)?ZP():"glibc"),i=s==="musl"?"glibc":"musl",o=a=>({name:`@anthropic-ai/claude-agent-sdk-linux-${r}${a==="musl"?"-musl":""}`,libc:a});return[o(s),o(i)]}return[]}function tN(t={}){return ad(t)[0]?.name}function nN(t=process.platform){return t==="win32"?"claude.exe":"claude"}function Nc(t,e={}){let n=e.platform??process.platform,r=e.arch??process.arch,s=e.hasLoader??eN(n,r),i=nN(n),o;for(let{name:c,libc:l}of ad(e)){let d=Ze.join(t,...c.split("/"),i);if(Wi(d)){if(l&&!s(l)){o??=d;continue}return d}}let a=Ze.join(t,"@anthropic-ai","claude-agent-sdk","cli.js");return Wi(a)?a:o}function $m(){let t=__dirname;for(let e=0;e<8;e++){let n=Ze.join(t,"node_modules"),r=Nc(n);if(r)return r;let s=Ze.dirname(t);if(s===t)break;t=s}}function rN(t={}){if(ad(t).length>0)return;let e=t.platform??process.platform,n=t.arch??process.arch;return`@anthropic-ai/claude-agent-sdk ships no prebuilt Claude Code binary for ${e}-${n}.`}function Dm(){let t=Ki(),e=Ze.join(t,"node_modules"),n=Ze.join(t,"package.json");try{if(!nn.existsSync(n))return!1;let o=nn.readFileSync(n,"utf8"),c=JSON.parse(o).dependencies??{};for(let[l,d]of Object.entries(Rm))if(c[l]!==d)return!1}catch{return!1}let r=Nc(e),s=Ze.join(e,"@openai","codex","bin","codex.js"),i=Ze.join(e,"@openai","codex-sdk","package.json");return!!r&&nn.existsSync(s)&&nn.existsSync(i)}async function Lm(t=!1){let e=Ki();console.log(`${Gr.COLORS.cyan}Installing engine dependencies...${Gr.COLORS.reset}`),t&&console.log(` Directory: ${e}`),nn.mkdirSync(e,{recursive:!0});let n={name:Pc.AGENTIDE_DEPS_PACKAGE_NAME,version:"1.0.0",private:!0,dependencies:Rm};nn.writeFileSync(Ze.join(e,"package.json"),JSON.stringify(n,null,2)+`
|
|
455
455
|
`);try{let r=process.platform==="win32"?"npm.cmd":"npm";(0,Nm.execSync)(`${r} install --production --no-audit --no-fund`,{cwd:e,stdio:t?"inherit":"pipe"}),console.log(`${Gr.COLORS.green}Dependencies installed${Gr.COLORS.reset}`)}catch(r){throw console.error(`${Gr.COLORS.red}Error: Failed to install dependencies${Gr.COLORS.reset}`),r instanceof Error&&console.error(` ${r.message}`),r}}function Mm(){let t=(process.env.AGENTHIPPO_NO_AUTO_INSTALL??process.env.AGENTIDE_NO_AUTO_INSTALL??"").trim().toLowerCase();return t==="1"||t==="true"||t==="yes"||t==="on"}async function sN(t=!1){if(!Dm()){if($m()&&Cm("@openai/codex","bin/codex.js")){t&&console.error(`${Gr.COLORS.dim}Engine SDKs resolved from the CLI's own node_modules \u2014 skipping install.${Gr.COLORS.reset}`);return}if(Mm()){t&&console.error(`${Gr.COLORS.dim}AGENTHIPPO_NO_AUTO_INSTALL set \u2014 skipping engine dependency install (expecting a self-contained image).${Gr.COLORS.reset}`);return}await Lm(t)}}function iN(){return Nc(Ze.join(Ki(),"node_modules"))}function oN(){let t=Ki(),e=Ze.join(t,"node_modules","@openai","codex","bin","codex.js");if(nn.existsSync(e))return e;let n=Ze.join(t,"node_modules","@openai","codex-sdk"),r=aN();if(!r)return;let s=process.platform==="win32"?"codex.exe":"codex",i=Ze.join(n,"vendor",r,"codex",s);return nn.existsSync(i)?i:void 0}function aN(){let t=process.arch,e=process.platform;if(e==="darwin")return t==="arm64"?"aarch64-apple-darwin":"x86_64-apple-darwin";if(e==="linux")return t==="arm64"?"aarch64-unknown-linux-musl":"x86_64-unknown-linux-musl";if(e==="win32")return t==="arm64"?"aarch64-pc-windows-msvc":"x86_64-pc-windows-msvc"}function cN(t){let e=t==="codex"?Ze.join("@openai","codex"):t==="claude"?Ze.join("@anthropic-ai","claude-agent-sdk"):void 0;if(!e)return;let n=[Ze.join(Ki(),"node_modules",e,"package.json"),Ze.join("/usr/local/lib/node_modules",e,"package.json"),Ze.join("/usr/lib/node_modules",e,"package.json")];for(let r of n)try{if(!nn.existsSync(r))continue;let s=JSON.parse(nn.readFileSync(r,"utf8"));if(s.version)return s.version}catch{}}});var Rn=T(Nt=>{"use strict";var lN=Nt&&Nt.__createBinding||(Object.create?(function(t,e,n,r){r===void 0&&(r=n);var s=Object.getOwnPropertyDescriptor(e,n);(!s||("get"in s?!e.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,s)}):(function(t,e,n,r){r===void 0&&(r=n),t[r]=e[n]})),uN=Nt&&Nt.__setModuleDefault||(Object.create?(function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}):function(t,e){t.default=e}),cd=Nt&&Nt.__importStar||(function(){var t=function(e){return t=Object.getOwnPropertyNames||function(n){var r=[];for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(r[r.length]=s);return r},t(e)};return function(e){if(e&&e.__esModule)return e;var n={};if(e!=null)for(var r=t(e),s=0;s<r.length;s++)r[s]!=="default"&&lN(n,e,r[s]);return uN(n,e),n}})();Object.defineProperty(Nt,"__esModule",{value:!0});Nt.SUGGESTED_LITELLM_SMOKE_TEST_COMMAND=Nt.COLORS=void 0;Nt.getBinaryDir=Cc;Nt.findClaudeCodePath=fN;Nt.findCodexPath=pN;Nt.findBundledBasePromptsDir=gN;Nt.getLiteLLMQuickstartHintLines=hN;var dN=cd(require("os")),mi=cd(require("path")),Xn=cd(require("fs")),Gm=require("child_process"),Rc=hi();function Cc(){return mi.dirname(process.execPath)}function fN(){if(process.env.CLAUDE_CODE_PATH)return process.env.CLAUDE_CODE_PATH;let t=(0,Rc.getClaudeSdkCliPath)();if(t)return t;let e=(0,Rc.resolveClaudeCodeFromOwnDeps)();if(e)return e;let n=Cc(),r=mi.join(n,"claude-sdk","cli.js");try{return Xn.accessSync(r,Xn.constants.R_OK),r}catch{}try{let o=process.platform==="win32"?"where claude":"which claude",a=(0,Gm.execSync)(o,{encoding:"utf8",stdio:["pipe","pipe","ignore"]}).trim();if(a)return a.split(`
|
|
456
456
|
`)[0]}catch{}let s=dN.homedir(),i=[mi.join(s,".npm-global","bin","claude"),mi.join(s,".local","bin","claude"),"/usr/local/bin/claude","/opt/homebrew/bin/claude"];for(let o of i)try{return Xn.accessSync(o,Xn.constants.X_OK),o}catch{}}function pN(){if(process.env.CODEX_PATH)return process.env.CODEX_PATH;let t=(0,Rc.resolveFromOwnDeps)("@openai/codex","bin/codex.js");if(t)return t;let e=(0,Rc.getCodexSdkBinaryPath)();if(e)return e;let n=Cc(),r=process.platform==="win32"?"codex.exe":"codex",s=mi.join(n,"codex-sdk",r);try{return Xn.accessSync(s,Xn.constants.X_OK),s}catch{}try{let i=process.platform==="win32"?"where codex":"which codex",o=(0,Gm.execSync)(i,{encoding:"utf8",stdio:["pipe","pipe","ignore"]}).trim();if(o)return o.split(`
|
|
457
457
|
`)[0]}catch{}}function gN(){let t=process.env.AGENTHIPPO_BASE_PROMPTS_DIR||process.env.AGENTIDE_BASE_PROMPTS_DIR;if(t)try{return Xn.accessSync(t,Xn.constants.R_OK),t}catch{}let e=Cc(),n=mi.join(e,"base-prompts");try{return Xn.accessSync(n,Xn.constants.R_OK),n}catch{}let r=mi.resolve(__dirname,"..","base-prompts");try{return Xn.accessSync(r,Xn.constants.R_OK),r}catch{}}Nt.COLORS={reset:"\x1B[0m",cyan:"\x1B[36m",gray:"\x1B[90m",dim:"\x1B[2m",blue:"\x1B[34m",green:"\x1B[32m",red:"\x1B[31m",yellow:"\x1B[33m"};Nt.SUGGESTED_LITELLM_SMOKE_TEST_COMMAND='AGENTHIPPO_LITELLM_BASE_URL="http://localhost:4000" AGENTHIPPO_LITELLM_API_KEY="dummy" AGENTHIPPO_DEFAULT_MODEL="litellm/gpt-5.5" AGENTHIPPO_DEFAULT_ENGINE="codex" agenthippo ask "Reply with exactly: CLI_TEST_OK"';function hN(){return["Try local LiteLLM quickstart:",` ${Nt.SUGGESTED_LITELLM_SMOKE_TEST_COMMAND}`]}});var qi=T(us=>{"use strict";Object.defineProperty(us,"__esModule",{value:!0});us.TurnRegistry=us.DEFAULT_TURN_DEADLINE_MS=void 0;us.hashNonce=kc;us.isNonceShaped=Hm;var Um=require("crypto");us.DEFAULT_TURN_DEADLINE_MS=30*6e4;function kc(t){return(0,Um.createHash)("sha256").update(t).digest("hex")}var mN=/^[0-9a-f]{32}$/;function Hm(t){return mN.test(t)}var ld=class{entries=new Map;generations=new Map;generationCounter=0;register(e,n){this.prune();let r=n?.renewNonce?.trim()||void 0;if(r!==void 0&&!Hm(r))throw new Error("renewNonce must be a previously minted turn nonce (32 lowercase hex chars)");let s=r??(0,Um.randomBytes)(16).toString("hex"),i=kc(s);this.entries.set(i,{userId:e.userId,sessionKey:e.sessionKey,inboundToken:e.inboundToken,egressStoreKey:e.egressStoreKey,deadline:e.deadline??Date.now()+us.DEFAULT_TURN_DEADLINE_MS});let o=++this.generationCounter;this.generations.set(i,o);let a=!1;return{nonce:s,release:()=>{a||(a=!0,this.generations.get(i)===o&&(this.entries.delete(i),this.generations.delete(i)))}}}releaseByNonce(e){let n=kc(e);return this.generations.delete(n),this.entries.delete(n)}resolve(e,n=Date.now()){let r=kc(e),s=this.entries.get(r);if(s){if(n>s.deadline){this.entries.delete(r),this.generations.delete(r);return}return s}}prune(e=Date.now()){for(let[n,r]of this.entries)e>r.deadline&&(this.entries.delete(n),this.generations.delete(n))}get size(){return this.entries.size}};us.TurnRegistry=ld});var dd=T(yr=>{"use strict";Object.defineProperty(yr,"__esModule",{value:!0});yr.CredentialBroker=yr.identityProvider=yr.passthroughProvider=void 0;yr.resolveProvider=EN;yr.passthroughProvider={name:"passthrough",refreshOnInbound:!0,async acquire({userId:t,inboundToken:e}){let n=e?.trim();if(!n)throw new Error(`No inbound data credential for user (fail-closed) \u2014 the platform did not forward a per-user token on this request (userId=${t})`);return{expiresAt:Date.now()+10*6e4,secretRef:n}}};yr.identityProvider={name:"identity",async acquire({userId:t}){if(!t.trim())throw new Error("No verified user (fail-closed)");return{expiresAt:Number.MAX_SAFE_INTEGER,secretRef:null}}};function EN(t){let e=(t??"passthrough").trim().toLowerCase();if(e==="passthrough")return yr.passthroughProvider;if(e==="identity")return yr.identityProvider;throw new Error(`Unknown data credential provider "${t}" (supported: passthrough, identity)`)}var ud=class{provider;cache=new Map;constructor(e){this.provider=e}async acquire(e,n){if(this.provider.refreshOnInbound)return this.provider.acquire({userId:e,inboundToken:n});let r=this.cache.get(e);if(r&&r.expiresAt>Date.now())return r;this.cache.delete(e);let s=await this.provider.acquire({userId:e,inboundToken:n});return this.cache.set(e,s),s}invalidate(e){this.cache.delete(e)}};yr.CredentialBroker=ud});var pd=T(Cn=>{"use strict";var _N=Cn&&Cn.__createBinding||(Object.create?(function(t,e,n,r){r===void 0&&(r=n);var s=Object.getOwnPropertyDescriptor(e,n);(!s||("get"in s?!e.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,s)}):(function(t,e,n,r){r===void 0&&(r=n),t[r]=e[n]})),yN=Cn&&Cn.__setModuleDefault||(Object.create?(function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}):function(t,e){t.default=e}),wN=Cn&&Cn.__importStar||(function(){var t=function(e){return t=Object.getOwnPropertyNames||function(n){var r=[];for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(r[r.length]=s);return r},t(e)};return function(e){if(e&&e.__esModule)return e;var n={};if(e!=null)for(var r=t(e),s=0;s<r.length;s++)r[s]!=="default"&&_N(n,e,r[s]);return yN(n,e),n}})();Object.defineProperty(Cn,"__esModule",{value:!0});Cn.ConnectorHost=Cn.TURN_META_KEY=void 0;var jm=wN(require("path")),AN=require("fs"),SN=require("@modelcontextprotocol/sdk/client/index.js"),TN=require("@modelcontextprotocol/sdk/client/stdio.js"),bN=require("@modelcontextprotocol/sdk/types.js"),vN=cc();Cn.TURN_META_KEY="ai.agenthippo/turn";var fd=class t{client;logger;connectorPath;constructor(e,n,r){this.client=e,this.logger=n,this.connectorPath=r}static async spawn(e){let n=jm.resolve(e.connectorPath);await AN.promises.access(n);let r=await(0,vN.resolveNodeBinary)(),s=new TN.StdioClientTransport({command:r,args:[n],cwd:jm.dirname(n),env:{...process.env,AGENTHIPPO_DATA_CONNECTOR:"1"},stderr:"pipe"}),i=new SN.Client({name:"agenthippo-data-gateway",version:"1.0.0"});return await i.connect(s),s.stderr?.on("data",o=>{e.logger?.warn(`[DataPlane] connector stderr: ${String(o).trimEnd()}`)}),e.logger?.info(`[DataPlane] connector started: ${n}`),new t(i,e.logger,n)}async listTools(){return(await this.client.listTools()).tools.map(n=>({name:n.name,description:n.description,inputSchema:n.inputSchema??{type:"object"}}))}async callTool(e,n,r){return await this.client.request({method:"tools/call",params:{name:e,arguments:n,_meta:{[Cn.TURN_META_KEY]:{userId:r.userId,credential:r.credential}}}},bN.CallToolResultSchema)}async close(){try{await this.client.close()}catch(e){this.logger?.warn(`[DataPlane] connector close: ${e instanceof Error?e.message:String(e)}`)}}};Cn.ConnectorHost=fd});var Uo=T(kn=>{"use strict";Object.defineProperty(kn,"__esModule",{value:!0});kn.DEFAULT_AUTH_BIND=kn.AUTH_PROXY_SECRET_HEADER=kn.DEFAULT_JWT_SECRET=void 0;kn.resolveAuthBrokerBind=PN;kn.loadAuthBrokerConfig=NN;kn.emailDomainAllowed=RN;function ON(t){return t?.trim()?t.split(",").map(e=>e.trim().toLowerCase()).filter(Boolean):[]}kn.DEFAULT_JWT_SECRET="dev-agenthippo-auth-secret-change-me";kn.AUTH_PROXY_SECRET_HEADER="x-agenthippo-proxy-secret";kn.DEFAULT_AUTH_BIND="127.0.0.1";var IN=new Set(["127.0.0.1","localhost","::1","[::1]"]);function PN(t=process.env){let e=t.AGENTHIPPO_AUTH_BIND?.trim()||kn.DEFAULT_AUTH_BIND;if(!!IN.has(e.toLowerCase()))return{address:e,isPublic:!1};if(t.AGENTHIPPO_AUTH_ALLOW_PUBLIC_BIND!=="1")throw new Error(`Refusing to bind the auth broker to "${e}". This port issues authorization codes and access tokens, and a directly reachable broker can be asked to mint a token for an arbitrary email by forging X-Auth-Request-Email.
|
|
@@ -532,7 +532,7 @@ Shutting down...`),(async()=>{if(at)try{await at.flush()}catch{}if(_a){try{await
|
|
|
532
532
|
2. Set it here: AGENTHIPPO_AUTH_PROXY_SECRET=<secret>
|
|
533
533
|
3. Have the proxy send it, e.g. in the Caddyfile:
|
|
534
534
|
header_up ${ra.AUTH_PROXY_SECRET_HEADER} {env.AGENTHIPPO_AUTH_PROXY_SECRET}
|
|
535
|
-
If the broker is genuinely unreachable except through the proxy and you accept the risk, set AGENTHIPPO_AUTH_TRUST_PROXY_UNVERIFIED=1.`);!e.devMode&&!e.proxySecret&&e.trustProxyHeadersUnverified&&console.warn("[auth-broker] WARNING: AGENTHIPPO_AUTH_TRUST_PROXY_UNVERIFIED=1 \u2014 X-Auth-Request-Email is accepted without proof. Anything that can reach this port can mint a token for any email. Set AGENTHIPPO_AUTH_PROXY_SECRET to close this."),!e.devMode&&e.allowedEmailDomains.length===0&&console.warn('[auth-broker] Warning: AGENTHIPPO_AUTH_ALLOWED_DOMAINS is not set \u2014 any upstream-SSO-authenticated email can receive a token. Set it to restrict sign-in (e.g. "example.com").');let n=e.dataDir?jL.join(e.dataDir,"auth-store.json"):void 0,r=new xL.AuthBrokerStore(e,n),s=new Cf.RateLimiter(e.rateLimitPerMin),i=(m,h={})=>{let y=Object.entries(h).filter(([,E])=>E!==void 0&&E!=="").map(([E,w])=>{let v=!e.logPii&&typeof w=="string"&&w.includes("@")?WL(w):w;return`${E}=${v}`}).join(" ");console.log(`[auth-broker] ${m}${y?" "+y:""}`)},o=1e4,a=2048,c=new Map,l=5e3,d=0;function u(m){let h=(0,gl.extractBearerToken)(m.headers.authorization)||Dy(m.headers.cookie,e.cookieName);if(!h)return;let y=HL.createHash("sha256").update(h).digest("hex"),E=c.get(y);if(E){if(E.exp>Date.now())return E.user;c.delete(y)}let w=(0,gl.verifyJwt)(h,e.jwtSecret);if(!w?.sub||!w.email)return;let v=r.getUser(String(w.sub));if(!v){let I=typeof w.team_id=="string"&&w.team_id?w.team_id:void 0;v={id:String(w.sub),email:String(w.email),displayName:typeof w.name=="string"&&w.name?w.name:String(w.email),teamId:I??(e.teamConfigured?e.team.id:void 0)}}if(w.exp){let I=Math.min(w.exp*1e3,Date.now()+o);if(c.size>=a){let b=Date.now();for(let[A,S]of c)S.exp<b&&c.delete(A);c.size>=a&&c.clear()}c.set(y,{user:v,exp:I})}return v}let f=m=>{let h=(m.headers["x-auth-request-email"]||"").trim().toLowerCase();if(h)return e.devMode||!e.proxySecret&&e.trustProxyHeadersUnverified||(0,Cf.hasTrustedProxyProof)(m.headers,e.proxySecret)?h:null},p=(m,h,y)=>{i("proxy_header.unverified",{ip:h,path:y}),Ie(m,401,{error:"unauthorized",error_description:`X-Auth-Request-Email was present without a valid ${ra.AUTH_PROXY_SECRET_HEADER} header. Only the trusted reverse proxy may assert an identity to this broker.`})},g=UL.createServer(async(m,h)=>{try{let y=m.headers.host||`127.0.0.1:${e.port}`,E=new sa.URL(m.url||"/",`http://${y}`),w=E.pathname,v=(0,Cf.clientIp)(m),I=()=>s.tryConsume(v)?!1:(i("rate_limited",{ip:v,path:w}),h.writeHead(429,{"Content-Type":"application/json","Retry-After":String(s.retryAfterSeconds(v))}),h.end(JSON.stringify({error:"rate_limited"})),!0);if(m.method==="GET"&&(w==="/health"||w==="/api/ide/health")){Ie(h,200,{status:"ok",service:"auth-broker"});return}if(m.method==="GET"&&w==="/oauth/authorize"){if(I())return;let A=E.searchParams.get("client_id")||"",S=E.searchParams.get("redirect_uri")||"",O=E.searchParams.get("code_challenge")||"",M=E.searchParams.get("state")||"",N=E.searchParams.get("login_hint")||E.searchParams.get("email")||"";if(A!==hl.AUTH_CLIENT_ID||!S||!O){i("authorize.invalid_request",{client_id:A}),Ie(h,400,{error:"invalid_request"});return}if(!Ly(S)){i("authorize.invalid_redirect_uri",{client_id:A}),Ie(h,400,{error:"invalid_request",error_description:"redirect_uri not allowed"});return}if(e.devMode&&N){let ce=r.upsertUser(N),fe=r.createAuthCode({userId:ce.id,clientId:A,redirectUri:S,codeChallenge:O});i("authorize.dev",{user:ce.email});let he=new sa.URL(S);he.searchParams.set("code",fe),M&&he.searchParams.set("state",M),h.writeHead(302,{Location:he.toString()}),h.end();return}let L=f(m);if(L===null){p(h,v,w);return}if(!L){Ie(h,401,{error:"unauthorized",error_description:"No authenticated user (missing X-Auth-Request-Email)"});return}let ee=r.upsertUser(L),q=r.createAuthCode({userId:ee.id,clientId:A,redirectUri:S,codeChallenge:O});i("authorize.signin",{user:ee.email});let ge=new sa.URL(S);ge.searchParams.set("code",q),M&&ge.searchParams.set("state",M),h.writeHead(302,{Location:ge.toString()}),h.end();return}if(m.method==="GET"&&w==="/oauth/browser-login"){if(I())return;let A=f(m);if(A===null){p(h,v,w);return}if(!A){Ie(h,401,{error:"unauthorized",error_description:"No authenticated user (missing X-Auth-Request-Email)"});return}let S=r.upsertUser(A),O=r.issueTokens(S,hl.AUTH_CLIENT_ID,"openid profile email agenthippo:ide");i("browser.login",{user:S.email});let M=e.publicBaseUrl.startsWith("https"),N=[`${e.cookieName}=${O.accessToken}`,"Path=/","HttpOnly","SameSite=Lax",...M?["Secure"]:[],...e.cookieDomain?[`Domain=${e.cookieDomain}`]:[],`Max-Age=${O.expiresIn}`],L=qL(E.searchParams.get("return_to")||"",e.cookieDomain,e.publicBaseUrl||"/");h.writeHead(302,{"Set-Cookie":N.join("; "),Location:L}),h.end();return}if(m.method==="POST"&&w==="/oauth/dev/login"){if(I())return;if(!e.devMode){Ie(h,404,{error:"not_found"});return}let A=await kf(m),S=A.trim().startsWith("{")?JSON.parse(A):$f(A),O=S.email?.trim(),M=S.code_challenge?.trim(),N=S.redirect_uri?.trim(),L=S.client_id?.trim()||hl.AUTH_CLIENT_ID;if(!O||!M||!N){Ie(h,400,{error:"invalid_request"});return}let ee=r.upsertUser(O),q=r.createAuthCode({userId:ee.id,clientId:L,redirectUri:N,codeChallenge:M});i("dev.login",{user:ee.email}),Ie(h,200,{code:q});return}if(m.method==="POST"&&w==="/oauth/token"){if(I())return;let A=await kf(m),S=$f(A),O=S.grant_type,M=S.client_id||hl.AUTH_CLIENT_ID,N=S.scope||"openid profile email agenthippo:ide";if(O==="authorization_code"){let L=S.code,ee=S.code_verifier,q=S.redirect_uri;if(!L||!ee||!q){Ie(h,400,{error:"invalid_request"});return}let ge=r.consumeAuthCode(L);if(!ge||ge.clientId!==M||ge.redirectUri!==q){Ie(h,400,{error:"invalid_grant"});return}if(!(0,BL.verifyPkceChallenge)(ee,ge.codeChallenge)){Ie(h,400,{error:"invalid_grant",error_description:"PKCE verification failed"});return}let ce=r.getUser(ge.userId);if(!ce){Ie(h,400,{error:"invalid_grant"});return}let fe=r.issueTokens(ce,M,N);i("token.signin",{user:ce.email,client_id:M}),Ie(h,200,{access_token:fe.accessToken,refresh_token:fe.refreshToken,token_type:"Bearer",expires_in:fe.expiresIn,scope:N});return}if(O==="refresh_token"){let L=S.refresh_token;if(!L){Ie(h,400,{error:"invalid_request"});return}let ee=r.consumeRefreshToken(L);if(!ee||ee.clientId!==M){i("token.refresh.invalid_grant"),Ie(h,400,{error:"invalid_grant"});return}let q=r.getUser(ee.userId);if(!q){Ie(h,400,{error:"invalid_grant"});return}let ge=r.issueTokens(q,M,N);i("token.refresh",{user:q.email}),Ie(h,200,{access_token:ge.accessToken,refresh_token:ge.refreshToken,token_type:"Bearer",expires_in:ge.expiresIn,scope:N});return}Ie(h,400,{error:"unsupported_grant_type"});return}if(m.method==="POST"&&w==="/oauth/introspect"){if(!u(m)){i("introspect.unauthorized"),Ie(h,401,{error:"unauthorized"});return}let A=await kf(m),O=$f(A).token;if(!O){Ie(h,400,{error:"invalid_request"});return}let M=(0,gl.verifyJwt)(O,e.jwtSecret);if(!M?.sub){Ie(h,200,{active:!1});return}Ie(h,200,{active:!0,sub:M.sub,email:M.email,team_id:M.team_id,scope:M.scope,exp:M.exp});return}let b=u(m);if(m.method==="GET"&&w==="/api/ide/me"){if(!b){let S=Date.now();S-d>l&&(d=S,i("me.unauthorized")),Ie(h,401,{error:"unauthorized"});return}h.setHeader("X-Agent-User",b.email||b.id);let A=(0,gl.extractBearerToken)(m.headers.authorization)||Dy(m.headers.cookie,e.cookieName);A&&h.setHeader("X-Agent-User-Assertion",A),Ie(h,200,r.buildMe(b));return}if(m.method==="GET"&&w==="/api/ide/bootstrap"){if(!b){Ie(h,401,{error:"unauthorized"});return}Ie(h,200,r.buildBootstrap(b));return}if(m.method==="POST"&&w==="/api/ide/signout"){b&&(r.revokeRefreshTokensForUser(b.id),i("signout",{user:b.email})),Ie(h,200,{ok:!0});return}Ie(h,404,{error:"not_found"})}catch(y){let E=y.status;if(E===413){Ie(h,413,{error:"request_entity_too_large"});return}if(E===403){i("forbidden",{reason:"email_domain"}),Ie(h,403,{error:"forbidden",error_description:"email domain not allowed"});return}console.error("[auth-broker] internal error",y),Ie(h,500,{error:"server_error"})}});return{server:g,baseUrl:e.publicBaseUrl,store:r,config:e,close:()=>new Promise((m,h)=>{g.close(y=>y?h(y):m())})}}async function YL(t){let e=My(t),n=(0,ra.resolveAuthBrokerBind)();return n.isPublic&&console.warn(`[auth-broker] WARNING: listening on ${n.address}:${e.config.port} \u2014 this token-issuing endpoint is reachable from off-host (AGENTHIPPO_AUTH_ALLOW_PUBLIC_BIND=1). Ensure only the reverse proxy can reach it and that AGENTHIPPO_AUTH_PROXY_SECRET is set.`),await new Promise((r,s)=>{e.server.listen(e.config.port,n.address,()=>r()),e.server.once("error",s)}),e}});var Uy=T(Lf=>{"use strict";Object.defineProperty(Lf,"__esModule",{value:!0});Lf.authBrokerCommand=zL;var XL=Uo(),JL=Gy();async function zL(){let t=(0,XL.loadAuthBrokerConfig)(),e=await(0,JL.startAuthBrokerServer)(t);console.log(`AgentHippo auth broker listening on ${e.baseUrl}`),console.log(" GET /health"),console.log(" GET /oauth/authorize"),console.log(" POST /oauth/token"),console.log(" GET /api/ide/bootstrap"),await new Promise(n=>{let r=()=>{e.close().then(n).catch(()=>n())};process.once("SIGINT",r),process.once("SIGTERM",r)})}});var Gf=T(Ir=>{"use strict";Object.defineProperty(Ir,"__esModule",{value:!0});Ir.EDGE_AGENT_PORT=void 0;Ir.renderIdentityLayer=Mf;Ir.renderSidecarEdgeCaddyfile=QL;Ir.renderSidecarFedCaddyfile=ZL;var Hy=[" # Proves this hop is the trusted proxy \u2014 the broker 401s X-Auth-Request-Email without it."," header_up x-agenthippo-proxy-secret {env.AGENTHIPPO_AUTH_PROXY_SECRET}"];function Mf(t){let{mode:e,authBrokerAddr:n,healthProxy:r,agentProxy:s}=t,i=t.oauth2ProxyAddr??"",o=t.catchallBody??s,a=t.turnRegistration,c=a?[" # Turn registration \u2014 after identity: X-Agent-User (set above) names the user the"," # sidecar mints the nonce for. Only POST chat turns match; others pass through."," # X-Agent-User-Assertion (also set above) rides along as the turn's inboundToken:"," # without it `passthrough` fails closed on every call and an RFC 8693 `exchange`"," # provider has no subject_token.",` forward_auth @turnStart ${a.connectorAddr} {`," uri /edge/turn"," copy_headers X-Agent-Turn-Nonce",` header_up Authorization "Bearer {$${a.tokenEnvVar}}"`," }"]:[],l=f=>f.map(p=>p.trim()?` ${p}`:p),d=(f,p)=>a?[" route {",...l(f),...c,...l(p)," }"]:[...f,...p],u=[];if(u.push(" # Hygiene: never let a client-supplied nonce or assertion header through. The trusted"," # values are set from the verified /api/ide/me response below, never from the client \u2014"," # a forged assertion would ride as the turn's inboundToken."," request_header -X-Agent-Turn-Nonce"," request_header -X-Agent-User-Assertion"),e==="self-issued"&&u.push(" # IDE PKCE sign-in: broker requires X-Auth-Request-Email from oauth2-proxy. Must come"," # before the generic /oauth/* handle so authorize is gated (not dumped straight to broker)."," handle /oauth/authorize {",` forward_auth ${i} {`," uri /oauth2/auth"," copy_headers X-Auth-Request-Email"," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," @unauth status 401"," handle_response @unauth {",' redir * "/oauth2/sign_in?rd={http.request.uri}"'," }"," }",` reverse_proxy ${n} {`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}",...Hy," }"," }"," # Federated SSO (A\u2032): browsers from other subdomains land here to stamp the shared cookie."," # Same oauth2-proxy gate as authorize; must precede the generic /oauth/* handle."," handle /oauth/browser-login {",` forward_auth ${i} {`," uri /oauth2/auth"," copy_headers X-Auth-Request-Email"," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," @unauth status 401"," handle_response @unauth {",' redir * "/oauth2/sign_in?rd={http.request.uri}"'," }"," }",` reverse_proxy ${n} {`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}",...Hy," }"," }"," # Auth-broker: token exchange + other /oauth/* routes that must stay ungated."," handle /oauth/* {",` reverse_proxy ${n} {`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," }"," }"),u.push(" handle /api/ide/* {",` reverse_proxy ${n} {`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," }"," }"),e==="self-issued"&&u.push(" # oauth2-proxy: Google SSO callback + sign-in."," handle /oauth2/* {",` reverse_proxy ${i} {`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," }"," }"),u.push(" @health path /health /ready"," handle @health {",...r," }"),a&&u.push(" # Chat-turn matcher for edge turn registration (P1 fix): only POST chat turns register."," @turnStart {"," method POST"," path /v1/chat /v1/chat/stream"," }"),u.push(" # API clients (IDE) with a Bearer token: validate via auth-broker, copy identity header."," @apiWithAuth {"," path /v1/*"," header Authorization *"," }"," handle @apiWithAuth {",...d([` forward_auth ${n} {`," uri /api/ide/me",` copy_headers X-Agent-User${a?" X-Agent-User-Assertion":""}`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," @unauth status 401"," handle_response @unauth {",' respond "Unauthorized" 401'," }"," }"],s)," }"),e==="self-issued")u.push(" # Browser clients: SSO via oauth2-proxy, identity injected as X-Agent-User."," handle {",...d([` forward_auth ${i} {`," uri /oauth2/auth"," copy_headers X-Auth-Request-Email>X-Agent-User"," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," @unauth status 401"," handle_response @unauth {",' redir * "/oauth2/sign_in?rd={http.request.uri}"'," }"," }"],o)," }");else{let f=t.idpUrl?.trim()||"",p=f?` redir * "${f}/oauth/browser-login?return_to=https://{http.request.host}{http.request.uri}" 302`:' respond "Unauthorized" 401';u.push(" # Browser clients: the shared .agenthippo.ai cookie is verified by auth-broker. No cookie"," # \u2192 redirect to the central IdP to log in (it stamps the cookie), then return."," handle {",...d([` forward_auth ${n} {`," uri /api/ide/me"," copy_headers X-Agent-User"," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," @unauth status 401"," handle_response @unauth {",p," }"," }"],o)," }")}return u}Ir.EDGE_AGENT_PORT=8090;function QL(){return["# Sidecar-platform edge ingress. TLS terminates in front; Caddy listens on HTTP :{$PORT}.","# Containers share one task/pod's localhost \u2192 upstreams are 127.0.0.1:<port> (not docker-DNS names).","{"," auto_https off","}","",":{$PORT} {",...Mf({mode:"self-issued",oauth2ProxyAddr:"localhost:4180",authBrokerAddr:"localhost:9780",healthProxy:[` reverse_proxy localhost:${Ir.EDGE_AGENT_PORT}`],agentProxy:[` reverse_proxy localhost:${Ir.EDGE_AGENT_PORT}`]}),"}",""].join(`
|
|
535
|
+
If the broker is genuinely unreachable except through the proxy and you accept the risk, set AGENTHIPPO_AUTH_TRUST_PROXY_UNVERIFIED=1.`);!e.devMode&&!e.proxySecret&&e.trustProxyHeadersUnverified&&console.warn("[auth-broker] WARNING: AGENTHIPPO_AUTH_TRUST_PROXY_UNVERIFIED=1 \u2014 X-Auth-Request-Email is accepted without proof. Anything that can reach this port can mint a token for any email. Set AGENTHIPPO_AUTH_PROXY_SECRET to close this."),!e.devMode&&e.allowedEmailDomains.length===0&&console.warn('[auth-broker] Warning: AGENTHIPPO_AUTH_ALLOWED_DOMAINS is not set \u2014 any upstream-SSO-authenticated email can receive a token. Set it to restrict sign-in (e.g. "example.com").');let n=e.dataDir?jL.join(e.dataDir,"auth-store.json"):void 0,r=new xL.AuthBrokerStore(e,n),s=new Cf.RateLimiter(e.rateLimitPerMin),i=(m,h={})=>{let y=Object.entries(h).filter(([,E])=>E!==void 0&&E!=="").map(([E,w])=>{let v=!e.logPii&&typeof w=="string"&&w.includes("@")?WL(w):w;return`${E}=${v}`}).join(" ");console.log(`[auth-broker] ${m}${y?" "+y:""}`)},o=1e4,a=2048,c=new Map,l=5e3,d=0;function u(m){let h=(0,gl.extractBearerToken)(m.headers.authorization)||Dy(m.headers.cookie,e.cookieName);if(!h)return;let y=HL.createHash("sha256").update(h).digest("hex"),E=c.get(y);if(E){if(E.exp>Date.now())return E.user;c.delete(y)}let w=(0,gl.verifyJwt)(h,e.jwtSecret);if(!w?.sub||!w.email)return;let v=r.getUser(String(w.sub));if(!v){let I=typeof w.team_id=="string"&&w.team_id?w.team_id:void 0;v={id:String(w.sub),email:String(w.email),displayName:typeof w.name=="string"&&w.name?w.name:String(w.email),teamId:I??(e.teamConfigured?e.team.id:void 0)}}if(w.exp){let I=Math.min(w.exp*1e3,Date.now()+o);if(c.size>=a){let b=Date.now();for(let[A,S]of c)S.exp<b&&c.delete(A);c.size>=a&&c.clear()}c.set(y,{user:v,exp:I})}return v}let f=m=>{let h=(m.headers["x-auth-request-email"]||"").trim().toLowerCase();if(h)return e.devMode||!e.proxySecret&&e.trustProxyHeadersUnverified||(0,Cf.hasTrustedProxyProof)(m.headers,e.proxySecret)?h:null},p=(m,h,y)=>{i("proxy_header.unverified",{ip:h,path:y}),Ie(m,401,{error:"unauthorized",error_description:`X-Auth-Request-Email was present without a valid ${ra.AUTH_PROXY_SECRET_HEADER} header. Only the trusted reverse proxy may assert an identity to this broker.`})},g=UL.createServer(async(m,h)=>{try{let y=m.headers.host||`127.0.0.1:${e.port}`,E=new sa.URL(m.url||"/",`http://${y}`),w=E.pathname,v=(0,Cf.clientIp)(m),I=()=>s.tryConsume(v)?!1:(i("rate_limited",{ip:v,path:w}),h.writeHead(429,{"Content-Type":"application/json","Retry-After":String(s.retryAfterSeconds(v))}),h.end(JSON.stringify({error:"rate_limited"})),!0);if(m.method==="GET"&&(w==="/health"||w==="/api/ide/health")){Ie(h,200,{status:"ok",service:"auth-broker"});return}if(m.method==="GET"&&w==="/oauth/authorize"){if(I())return;let A=E.searchParams.get("client_id")||"",S=E.searchParams.get("redirect_uri")||"",O=E.searchParams.get("code_challenge")||"",M=E.searchParams.get("state")||"",N=E.searchParams.get("login_hint")||E.searchParams.get("email")||"";if(A!==hl.AUTH_CLIENT_ID||!S||!O){i("authorize.invalid_request",{client_id:A}),Ie(h,400,{error:"invalid_request"});return}if(!Ly(S)){i("authorize.invalid_redirect_uri",{client_id:A}),Ie(h,400,{error:"invalid_request",error_description:"redirect_uri not allowed"});return}if(e.devMode&&N){let ce=r.upsertUser(N),fe=r.createAuthCode({userId:ce.id,clientId:A,redirectUri:S,codeChallenge:O});i("authorize.dev",{user:ce.email});let he=new sa.URL(S);he.searchParams.set("code",fe),M&&he.searchParams.set("state",M),h.writeHead(302,{Location:he.toString()}),h.end();return}let L=f(m);if(L===null){p(h,v,w);return}if(!L){Ie(h,401,{error:"unauthorized",error_description:"No authenticated user (missing X-Auth-Request-Email)"});return}let ee=r.upsertUser(L),q=r.createAuthCode({userId:ee.id,clientId:A,redirectUri:S,codeChallenge:O});i("authorize.signin",{user:ee.email});let ge=new sa.URL(S);ge.searchParams.set("code",q),M&&ge.searchParams.set("state",M),h.writeHead(302,{Location:ge.toString()}),h.end();return}if(m.method==="GET"&&w==="/oauth/browser-login"){if(I())return;let A=f(m);if(A===null){p(h,v,w);return}if(!A){Ie(h,401,{error:"unauthorized",error_description:"No authenticated user (missing X-Auth-Request-Email)"});return}let S=r.upsertUser(A),O=r.issueTokens(S,hl.AUTH_CLIENT_ID,"openid profile email agenthippo:ide");i("browser.login",{user:S.email});let M=e.publicBaseUrl.startsWith("https"),N=[`${e.cookieName}=${O.accessToken}`,"Path=/","HttpOnly","SameSite=Lax",...M?["Secure"]:[],...e.cookieDomain?[`Domain=${e.cookieDomain}`]:[],`Max-Age=${O.expiresIn}`],L=qL(E.searchParams.get("return_to")||"",e.cookieDomain,e.publicBaseUrl||"/");h.writeHead(302,{"Set-Cookie":N.join("; "),Location:L}),h.end();return}if(m.method==="POST"&&w==="/oauth/dev/login"){if(I())return;if(!e.devMode){Ie(h,404,{error:"not_found"});return}let A=await kf(m),S=A.trim().startsWith("{")?JSON.parse(A):$f(A),O=S.email?.trim(),M=S.code_challenge?.trim(),N=S.redirect_uri?.trim(),L=S.client_id?.trim()||hl.AUTH_CLIENT_ID;if(!O||!M||!N){Ie(h,400,{error:"invalid_request"});return}let ee=r.upsertUser(O),q=r.createAuthCode({userId:ee.id,clientId:L,redirectUri:N,codeChallenge:M});i("dev.login",{user:ee.email}),Ie(h,200,{code:q});return}if(m.method==="POST"&&w==="/oauth/token"){if(I())return;let A=await kf(m),S=$f(A),O=S.grant_type,M=S.client_id||hl.AUTH_CLIENT_ID,N=S.scope||"openid profile email agenthippo:ide";if(O==="authorization_code"){let L=S.code,ee=S.code_verifier,q=S.redirect_uri;if(!L||!ee||!q){Ie(h,400,{error:"invalid_request"});return}let ge=r.consumeAuthCode(L);if(!ge||ge.clientId!==M||ge.redirectUri!==q){Ie(h,400,{error:"invalid_grant"});return}if(!(0,BL.verifyPkceChallenge)(ee,ge.codeChallenge)){Ie(h,400,{error:"invalid_grant",error_description:"PKCE verification failed"});return}let ce=r.getUser(ge.userId);if(!ce){Ie(h,400,{error:"invalid_grant"});return}let fe=r.issueTokens(ce,M,N);i("token.signin",{user:ce.email,client_id:M}),Ie(h,200,{access_token:fe.accessToken,refresh_token:fe.refreshToken,token_type:"Bearer",expires_in:fe.expiresIn,scope:N});return}if(O==="refresh_token"){let L=S.refresh_token;if(!L){Ie(h,400,{error:"invalid_request"});return}let ee=r.consumeRefreshToken(L);if(!ee||ee.clientId!==M){i("token.refresh.invalid_grant"),Ie(h,400,{error:"invalid_grant"});return}let q=r.getUser(ee.userId);if(!q){Ie(h,400,{error:"invalid_grant"});return}let ge=r.issueTokens(q,M,N);i("token.refresh",{user:q.email}),Ie(h,200,{access_token:ge.accessToken,refresh_token:ge.refreshToken,token_type:"Bearer",expires_in:ge.expiresIn,scope:N});return}Ie(h,400,{error:"unsupported_grant_type"});return}if(m.method==="POST"&&w==="/oauth/introspect"){if(!u(m)){i("introspect.unauthorized"),Ie(h,401,{error:"unauthorized"});return}let A=await kf(m),O=$f(A).token;if(!O){Ie(h,400,{error:"invalid_request"});return}let M=(0,gl.verifyJwt)(O,e.jwtSecret);if(!M?.sub){Ie(h,200,{active:!1});return}Ie(h,200,{active:!0,sub:M.sub,email:M.email,team_id:M.team_id,scope:M.scope,exp:M.exp});return}let b=u(m);if(m.method==="GET"&&w==="/api/ide/me"){if(!b){let S=Date.now();S-d>l&&(d=S,i("me.unauthorized")),Ie(h,401,{error:"unauthorized"});return}h.setHeader("X-Agent-User",b.email||b.id);let A=(0,gl.extractBearerToken)(m.headers.authorization)||Dy(m.headers.cookie,e.cookieName);A&&h.setHeader("X-Agent-User-Assertion",A),Ie(h,200,r.buildMe(b));return}if(m.method==="GET"&&w==="/api/ide/bootstrap"){if(!b){Ie(h,401,{error:"unauthorized"});return}Ie(h,200,r.buildBootstrap(b));return}if(m.method==="POST"&&w==="/api/ide/signout"){b&&(r.revokeRefreshTokensForUser(b.id),i("signout",{user:b.email})),Ie(h,200,{ok:!0});return}Ie(h,404,{error:"not_found"})}catch(y){let E=y.status;if(E===413){Ie(h,413,{error:"request_entity_too_large"});return}if(E===403){i("forbidden",{reason:"email_domain"}),Ie(h,403,{error:"forbidden",error_description:"email domain not allowed"});return}console.error("[auth-broker] internal error",y),Ie(h,500,{error:"server_error"})}});return{server:g,baseUrl:e.publicBaseUrl,store:r,config:e,close:()=>new Promise((m,h)=>{g.close(y=>y?h(y):m())})}}async function YL(t){let e=My(t),n=(0,ra.resolveAuthBrokerBind)();return n.isPublic&&console.warn(`[auth-broker] WARNING: listening on ${n.address}:${e.config.port} \u2014 this token-issuing endpoint is reachable from off-host (AGENTHIPPO_AUTH_ALLOW_PUBLIC_BIND=1). Ensure only the reverse proxy can reach it and that AGENTHIPPO_AUTH_PROXY_SECRET is set.`),await new Promise((r,s)=>{e.server.listen(e.config.port,n.address,()=>r()),e.server.once("error",s)}),e}});var Uy=T(Lf=>{"use strict";Object.defineProperty(Lf,"__esModule",{value:!0});Lf.authBrokerCommand=zL;var XL=Uo(),JL=Gy();async function zL(){let t=(0,XL.loadAuthBrokerConfig)(),e=await(0,JL.startAuthBrokerServer)(t);console.log(`AgentHippo auth broker listening on ${e.baseUrl}`),console.log(" GET /health"),console.log(" GET /oauth/authorize"),console.log(" POST /oauth/token"),console.log(" GET /api/ide/bootstrap"),await new Promise(n=>{let r=()=>{e.close().then(n).catch(()=>n())};process.once("SIGINT",r),process.once("SIGTERM",r)})}});var Gf=T(Ir=>{"use strict";Object.defineProperty(Ir,"__esModule",{value:!0});Ir.EDGE_AGENT_PORT=void 0;Ir.renderIdentityLayer=Mf;Ir.renderSidecarEdgeCaddyfile=QL;Ir.renderSidecarFedCaddyfile=ZL;var Hy=[" # Proves this hop is the trusted proxy \u2014 the broker 401s X-Auth-Request-Email without it."," header_up x-agenthippo-proxy-secret {env.AGENTHIPPO_AUTH_PROXY_SECRET}"];function Mf(t){let{mode:e,authBrokerAddr:n,healthProxy:r,agentProxy:s}=t,i=t.oauth2ProxyAddr??"",o=t.catchallBody??s,a=t.turnRegistration,c=a?[" # Turn registration \u2014 after identity: X-Agent-User (set above) names the user the"," # sidecar mints the nonce for. Only POST chat turns match; others pass through."," # X-Agent-User-Assertion (also set above) rides along as the turn's inboundToken:"," # without it `passthrough` fails closed on every call and an RFC 8693 `exchange`"," # provider has no subject_token.",` forward_auth @turnStart ${a.connectorAddr} {`," uri /edge/turn"," copy_headers X-Agent-Turn-Nonce",` header_up Authorization "Bearer {$${a.tokenEnvVar}}"`," }"]:[],l=f=>f.map(p=>p.trim()?` ${p}`:p),d=(f,p)=>a?[" route {",...l(f),...c,...l(p)," }"]:[...f,...p],u=[];if(u.push(" # Hygiene: never let a client-supplied nonce or assertion header through. The trusted"," # values are set from the verified /api/ide/me response below, never from the client \u2014"," # a forged assertion would ride as the turn's inboundToken."," request_header -X-Agent-Turn-Nonce"," request_header -X-Agent-User-Assertion"),e==="self-issued"&&u.push(" # IDE PKCE sign-in: broker requires X-Auth-Request-Email from oauth2-proxy. Must come"," # before the generic /oauth/* handle so authorize is gated (not dumped straight to broker)."," handle /oauth/authorize {",` forward_auth ${i} {`," uri /oauth2/auth"," copy_headers X-Auth-Request-Email"," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," @unauth status 401"," handle_response @unauth {",' redir * "/oauth2/sign_in?rd=https://{http.request.host}{http.request.uri_escaped}"'," }"," }",` reverse_proxy ${n} {`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}",...Hy," }"," }"," # Federated SSO (A\u2032): browsers from other subdomains land here to stamp the shared cookie."," # Same oauth2-proxy gate as authorize; must precede the generic /oauth/* handle."," handle /oauth/browser-login {",` forward_auth ${i} {`," uri /oauth2/auth"," copy_headers X-Auth-Request-Email"," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," @unauth status 401"," handle_response @unauth {",' redir * "/oauth2/sign_in?rd=https://{http.request.host}{http.request.uri_escaped}"'," }"," }",` reverse_proxy ${n} {`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}",...Hy," }"," }"," # Auth-broker: token exchange + other /oauth/* routes that must stay ungated."," handle /oauth/* {",` reverse_proxy ${n} {`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," }"," }"),u.push(" handle /api/ide/* {",` reverse_proxy ${n} {`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," }"," }"),e==="self-issued"&&u.push(" # oauth2-proxy: Google SSO callback + sign-in."," handle /oauth2/* {",` reverse_proxy ${i} {`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," }"," }"),u.push(" @health path /health /ready"," handle @health {",...r," }"),a&&u.push(" # Chat-turn matcher for edge turn registration (P1 fix): only POST chat turns register."," @turnStart {"," method POST"," path /v1/chat /v1/chat/stream"," }"),u.push(" # API clients (IDE) with a Bearer token: validate via auth-broker, copy identity header."," @apiWithAuth {"," path /v1/*"," header Authorization *"," }"," handle @apiWithAuth {",...d([` forward_auth ${n} {`," uri /api/ide/me",` copy_headers X-Agent-User${a?" X-Agent-User-Assertion":""}`," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," @unauth status 401"," handle_response @unauth {",' respond "Unauthorized" 401'," }"," }"],s)," }"),e==="self-issued")u.push(" # Browser clients: SSO via oauth2-proxy, identity injected as X-Agent-User."," handle {",...d([` forward_auth ${i} {`," uri /oauth2/auth"," copy_headers X-Auth-Request-Email>X-Agent-User"," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," @unauth status 401"," handle_response @unauth {",' redir * "/oauth2/sign_in?rd=https://{http.request.host}{http.request.uri_escaped}"'," }"," }"],o)," }");else{let f=t.idpUrl?.trim()||"",p=f?` redir * "${f}/oauth/browser-login?return_to=https://{http.request.host}{http.request.uri_escaped}" 302`:' respond "Unauthorized" 401';u.push(" # Browser clients: the shared .agenthippo.ai cookie is verified by auth-broker. No cookie"," # \u2192 redirect to the central IdP to log in (it stamps the cookie), then return."," handle {",...d([` forward_auth ${n} {`," uri /api/ide/me"," copy_headers X-Agent-User"," header_up X-Forwarded-Proto https"," header_up X-Forwarded-Host {http.request.host}"," @unauth status 401"," handle_response @unauth {",p," }"," }"],o)," }")}return u}Ir.EDGE_AGENT_PORT=8090;function QL(){return["# Sidecar-platform edge ingress. TLS terminates in front; Caddy listens on HTTP :{$PORT}.","# Containers share one task/pod's localhost \u2192 upstreams are 127.0.0.1:<port> (not docker-DNS names).","{"," auto_https off","}","",":{$PORT} {",...Mf({mode:"self-issued",oauth2ProxyAddr:"localhost:4180",authBrokerAddr:"localhost:9780",healthProxy:[` reverse_proxy localhost:${Ir.EDGE_AGENT_PORT}`],agentProxy:[` reverse_proxy localhost:${Ir.EDGE_AGENT_PORT}`]}),"}",""].join(`
|
|
536
536
|
`)}function ZL(t){let e=(t||"").trim().replace(/\/+$/,"");return["# Sidecar-platform federated edge (A\u2032). TLS terminates in front; Caddy listens on HTTP :{$PORT}.",`# No oauth2-proxy \u2014 Google login happens once at the central IdP (${e||"<IDP_URL>"});`,"# this agent only verifies the shared AgentHippo JWT (Bearer for IDE, cookie for browser).","{"," auto_https off","}","",":{$PORT} {",...Mf({mode:"federated",authBrokerAddr:"localhost:9780",idpUrl:e,healthProxy:[` reverse_proxy localhost:${Ir.EDGE_AGENT_PORT}`],agentProxy:[` reverse_proxy localhost:${Ir.EDGE_AGENT_PORT}`]}),"}",""].join(`
|
|
537
537
|
`)}});var Ff=T(X=>{"use strict";var e0=X&&X.__createBinding||(Object.create?(function(t,e,n,r){r===void 0&&(r=n);var s=Object.getOwnPropertyDescriptor(e,n);(!s||("get"in s?!e.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,s)}):(function(t,e,n,r){r===void 0&&(r=n),t[r]=e[n]})),t0=X&&X.__setModuleDefault||(Object.create?(function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}):function(t,e){t.default=e}),jy=X&&X.__importStar||(function(){var t=function(e){return t=Object.getOwnPropertyNames||function(n){var r=[];for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(r[r.length]=s);return r},t(e)};return function(e){if(e&&e.__esModule)return e;var n={};if(e!=null)for(var r=t(e),s=0;s<r.length;s++)r[s]!=="default"&&e0(n,e,r[s]);return t0(n,e),n}})();Object.defineProperty(X,"__esModule",{value:!0});X.DEPLOY_PUBLIC_HOSTNAME=X.DEFAULT_DEPLOY_PUBLIC_HOSTNAME=X.AGENTHIPPO_HIPPOCLAW_NPM=X.AGENTHIPPO_LITELLM_IMAGE_TAG=X.AGENTHIPPO_RUNTIME_IMAGE_TAG=X.AGENTHIPPO_CLI_NPM_VERSION_FALLBACK=X.AGENTHIPPO_CLI_NPM_PACKAGE=X.AGENTHIPPO_IMAGE_REGISTRY=X.DEPLOY_TARGETS=void 0;X.isDeployTarget=n0;X.resolveCliReleaseVersion=Uf;X.resolveAgentHippoCliNpmVersion=Fy;X.agenthippoImage=ws;X.defaultRuntimeImage=r0;X.packAttributionBody=Vy;X.packAttributionComment=Hf;X.withPackAttribution=Wy;X.serveCmdArgs=Ti;X.resolveDeployPublicHostname=jf;X.scanEnvRefs=o0;X.renderEnvExample=Ky;X.renderAuthEnvExample=qy;X.renderAuthBrokerDockerfile=xf;X.renderChannelsDockerfile=Yy;X.isSecretLikeBasename=a0;X.dataPlaneTokenEnvVar=tw;X.renderTarget=R0;var oo=jy(require("fs")),ao=jy(require("path")),ml=Ur(),gt=Gf();X.DEPLOY_TARGETS=["vm","compose","cloudrun","databricks-app","spcs","cloudflare","aws-ecs","azure-container-apps"];function n0(t){return X.DEPLOY_TARGETS.includes(t)}X.AGENTHIPPO_IMAGE_REGISTRY="ghcr.io/agenthippoai";X.AGENTHIPPO_CLI_NPM_PACKAGE="@agenthippo-ai/agenthippo";X.AGENTHIPPO_CLI_NPM_VERSION_FALLBACK="latest";function xy(){try{return Oc()}catch{try{let t=ao.join(__dirname,"..","..","package.json");if(oo.existsSync(t))return JSON.parse(oo.readFileSync(t,"utf8"))}catch{}return}}function By(t){let e=xy()?.agenthippo?.[t]?.trim();if(!e)throw new Error(`extensions/agentide/package.json agenthippo.${t} is missing`);return e}function Uf(){let t=xy()?.version?.trim();if(t&&t!=="0.0.0"&&t!=="dev")return t;try{let e=ao.join(__dirname,"..","..","..","..","package.json");if(oo.existsSync(e)){let n=JSON.parse(oo.readFileSync(e,"utf8")).version?.trim();if(n&&n!=="0.0.0"&&n!=="dev")return n}}catch{}}function Fy(){return Uf()??X.AGENTHIPPO_CLI_NPM_VERSION_FALLBACK}X.AGENTHIPPO_RUNTIME_IMAGE_TAG=Uf()??"dev";X.AGENTHIPPO_LITELLM_IMAGE_TAG=By("litellmImageTag");X.AGENTHIPPO_HIPPOCLAW_NPM=By("hippoclawNpm");function ws(t,e){let n=t==="runtime"?X.AGENTHIPPO_RUNTIME_IMAGE_TAG:X.AGENTHIPPO_LITELLM_IMAGE_TAG;return`${X.AGENTHIPPO_IMAGE_REGISTRY}/${t}:${n}${e?`-${e}`:""}`}function r0(t){return t.trim().toLowerCase()==="cursor"?ws("runtime","cursor"):ws("runtime")}function Vy(t){return`Generated from Agent Pack ${t.packName}@${t.packVersion} \u2014 packs.agenthippo.ai`}function Hf(t,e="hash"){let n=Vy(t);return e==="html"?`<!-- ${n} -->`:e==="sql"?`-- ${n}`:`# ${n}`}function Wy(t,e,n){if(n.slice(0,400).includes("packs.agenthippo.ai"))return n;let s=ao.basename(e),i=ao.extname(s).toLowerCase();if(i===".json"||i===".jsonc"||s==="wrangler.toml"||s==="package.json")return n;let a=Hf(t,i===".html"||i===".md"?"html":i===".sql"?"sql":"hash");if(n.startsWith("#!")){let c=n.indexOf(`
|
|
538
538
|
`);return c===-1?`${n}
|
|
@@ -543,7 +543,7 @@ ${n}`}function Ti(t,e){let n=["serve","--agent","/app/pack","--workspace","/stat
|
|
|
543
543
|
`)+`
|
|
544
544
|
`}function qy(t){let e=jf(t?.hostname);if(t?.fed){let r=(t.idpUrl||"https://auth.agenthippo.ai").replace(/\/+$/,""),s=t.cookieDomain||".agenthippo.ai";return["# Generated by `agenthippo deploy init --ingress edge-fed`. Copy to .env.auth and fill in.","# Federated edge (A\u2032): NO Google OAuth client here. Google login happens once at the",`# central IdP (${r}); this agent only VERIFIES the shared ${s} AgentHippo JWT.`,"# Loaded only by auth-broker. Never given to the agent (limits token-forgery blast radius).","","# AgentHippo JWT signing key \u2014 MUST match the central IdP (auth.agenthippo.ai).","# Copy it from there (or the GCP secret it stores) so tokens issued by the IdP verify here.","AGENTHIPPO_AUTH_JWT_SECRET= # copy from auth.agenthippo.ai (openssl rand -hex 32 to generate one)",`AGENTHIPPO_AUTH_PUBLIC_URL=https://${e}`,"AGENTHIPPO_AUTH_ALLOWED_DOMAINS=agenthippo.ai","AGENTHIPPO_AUTH_DEV=0 # 1 enables /oauth/dev/login (tests only; never in production)",`AGENTHIPPO_AUTH_COOKIE_DOMAIN=${s} # scope the shared cookie to all subdomains`,`AGENTHIPPO_AUTH_IDP_URL=${r} # where browsers are sent to log in`,"AGENTHIPPO_AUTH_COOKIE_NAME=ah_token","","# Team bootstrap (store + gateway routing) \u2014 same as the IdP if you want parity.","AGENTHIPPO_TEAM_ID=team_default","AGENTHIPPO_TEAM_NAME=Default Team","AGENTHIPPO_TEAM_STORE_REPO=agenthippoai/agenthippo-store","AGENTHIPPO_TEAM_STORE_INDEX_URL=https://agenthippoai.github.io/agenthippo-store","AGENTHIPPO_TEAM_GITHUB_ORG=agenthippoai","# An agenthippo-gateway deployment (JWT lane) \u2014 NOT bare LiteLLM. No key: the account","# JWT is the routing credential.","AGENTHIPPO_TEAM_GATEWAY_URL=https://gateway.agenthippo.ai/v1","","# No OAUTH2_PROXY_* here \u2014 this agent does not talk to Google. The one oauth2-proxy +","# one Google OAuth client live only at the IdP."].join(`
|
|
545
545
|
`)+`
|
|
546
|
-
`}let n=t?.cookieDomain||".agenthippo.ai";return["# Generated by `agenthippo deploy init`. Copy to .env.auth and fill in.","# Loaded only by auth-broker + oauth2-proxy (docker-compose.yml). Never given to the agent.","","# Google SSO (oauth2-proxy). GCP \u2192 OAuth client (Web):",`# Authorized origins: https://${e}`,`# Redirect URI: https://${e}/oauth2/callback`,"OAUTH2_PROXY_CLIENT_ID=","OAUTH2_PROXY_CLIENT_SECRET=","OAUTH2_PROXY_COOKIE_SECRET= # openssl rand -base64 32 | head -c 32","OAUTH2_PROXY_PROVIDER=google","OAUTH2_PROXY_EMAIL_DOMAINS=example.com","OAUTH2_PROXY_REVERSE_PROXY=true",`OAUTH2_PROXY_REDIRECT_URL=https://${e}/oauth2/callback`,"# Federated SSO (A\u2032) \u2014 set when this edge is the central IdP for a fleet of agents:",`OAUTH2_PROXY_COOKIE_DOMAINS=${n} # scope the oauth2-proxy session cookie to all subdomains`,"","# IDE auth broker (team bootstrap for store + LiteLLM routing)","AGENTHIPPO_AUTH_JWT_SECRET= # openssl rand -hex 32","AGENTHIPPO_AUTH_PUBLIC_URL=https://"+e,"AGENTHIPPO_AUTH_ALLOWED_DOMAINS=example.com","AGENTHIPPO_AUTH_DEV=0 # 1 enables /oauth/dev/login (tests only; never in production)","AGENTHIPPO_AUTH_COOKIE_NAME=ah_token # federated cookie holding the AgentHippo JWT",`AGENTHIPPO_AUTH_COOKIE_DOMAIN=${n} # set so /oauth/browser-login stamps a cross-subdomain cookie`,"AGENTHIPPO_TEAM_ID=team_default","AGENTHIPPO_TEAM_NAME=Default Team","AGENTHIPPO_TEAM_STORE_REPO=agenthippoai/agenthippo-store","AGENTHIPPO_TEAM_STORE_INDEX_URL=https://agenthippoai.github.io/agenthippo-store","AGENTHIPPO_TEAM_GITHUB_ORG=agenthippoai","# An agenthippo-gateway deployment (JWT lane) \u2014 NOT bare LiteLLM. No key: the account","# JWT is the routing credential.","AGENTHIPPO_TEAM_GATEWAY_URL=https://gateway.agenthippo.ai/v1"].join(`
|
|
546
|
+
`}let n=t?.cookieDomain||".agenthippo.ai";return["# Generated by `agenthippo deploy init`. Copy to .env.auth and fill in.","# Loaded only by auth-broker + oauth2-proxy (docker-compose.yml). Never given to the agent.","","# Google SSO (oauth2-proxy). GCP \u2192 OAuth client (Web):",`# Authorized origins: https://${e}`,`# Redirect URI: https://${e}/oauth2/callback`,"OAUTH2_PROXY_CLIENT_ID=","OAUTH2_PROXY_CLIENT_SECRET=","OAUTH2_PROXY_COOKIE_SECRET= # openssl rand -base64 32 | head -c 32","OAUTH2_PROXY_PROVIDER=google","OAUTH2_PROXY_EMAIL_DOMAINS=example.com","OAUTH2_PROXY_REVERSE_PROXY=true",`OAUTH2_PROXY_REDIRECT_URL=https://${e}/oauth2/callback`,"# Federated SSO (A\u2032) \u2014 set when this edge is the central IdP for a fleet of agents:",`OAUTH2_PROXY_COOKIE_DOMAINS=${n} # scope the oauth2-proxy session cookie to all subdomains`,"# Hosts oauth2-proxy may send the browser back to after Google login (absolute rd=\u2026). Defaults","# to the IdP hostname in docker-compose.yml; widen to the cookie domain for a subdomain fleet.",`OAUTH2_PROXY_WHITELIST_DOMAINS=${n}`,"","# IDE auth broker (team bootstrap for store + LiteLLM routing)","AGENTHIPPO_AUTH_JWT_SECRET= # openssl rand -hex 32","AGENTHIPPO_AUTH_PUBLIC_URL=https://"+e,"AGENTHIPPO_AUTH_ALLOWED_DOMAINS=example.com","AGENTHIPPO_AUTH_DEV=0 # 1 enables /oauth/dev/login (tests only; never in production)","AGENTHIPPO_AUTH_COOKIE_NAME=ah_token # federated cookie holding the AgentHippo JWT",`AGENTHIPPO_AUTH_COOKIE_DOMAIN=${n} # set so /oauth/browser-login stamps a cross-subdomain cookie`,"AGENTHIPPO_TEAM_ID=team_default","AGENTHIPPO_TEAM_NAME=Default Team","AGENTHIPPO_TEAM_STORE_REPO=agenthippoai/agenthippo-store","AGENTHIPPO_TEAM_STORE_INDEX_URL=https://agenthippoai.github.io/agenthippo-store","AGENTHIPPO_TEAM_GITHUB_ORG=agenthippoai","# An agenthippo-gateway deployment (JWT lane) \u2014 NOT bare LiteLLM. No key: the account","# JWT is the routing credential.","AGENTHIPPO_TEAM_GATEWAY_URL=https://gateway.agenthippo.ai/v1"].join(`
|
|
547
547
|
`)+`
|
|
548
548
|
`}function xf(){return["# Auth broker for IDE sign-in + team bootstrap API.","# Build context = pack root: docker build -f deploy/Dockerfile.auth-broker -t <pack>-auth-broker:local .",`FROM ${ws("runtime")}`,"ENV AGENTHIPPO_AUTH_PORT=9780","ENV AGENTHIPPO_AUTH_DATA_DIR=/data","EXPOSE 9780",'VOLUME ["/data"]','CMD ["auth-broker"]',""].join(`
|
|
549
549
|
`)}function Yy(){return["# Agent Anywhere channels gateway \u2014 built from this pack, not pulled from GHCR.",`# Pins ${X.AGENTHIPPO_HIPPOCLAW_NPM} (same pin as the IDE installSource default).`,"# node:22-alpine: HippoClaw requires Node 22+; apk git is needed for the npm install.","# Avoid Debian slim+apt-get (GPG failures on some Docker Desktop hosts) and full node:22 (huge).","FROM node:22-alpine","RUN apk add --no-cache git",`RUN npm i -g ${X.AGENTHIPPO_HIPPOCLAW_NPM}`,"EXPOSE 18789",'ENTRYPOINT ["openclaw"]','CMD ["gateway"]',""].join(`
|
|
@@ -552,7 +552,7 @@ ${n}`}function Ti(t,e){let n=["serve","--agent","/app/pack","--workspace","/stat
|
|
|
552
552
|
`)}function As(t){return["<!DOCTYPE html>",'<html lang="en">',"<head>",' <meta charset="utf-8" />',' <meta name="viewport" content="width=device-width, initial-scale=1" />',` <title>${t}</title>`," <style>"," :root { color-scheme: light dark; font-family: system-ui, sans-serif; }"," body { max-width: 720px; margin: 2rem auto; padding: 0 1rem; line-height: 1.5; }"," h1 { font-size: 1.4rem; margin-bottom: 0.25rem; }"," .sub { opacity: 0.7; font-size: 0.9rem; margin-bottom: 1.5rem; }"," #log { border: 1px solid #8884; border-radius: 8px; min-height: 200px; padding: 1rem; white-space: pre-wrap; margin-bottom: 1rem; }"," form { display: flex; gap: 0.5rem; }"," input[type=text] { flex: 1; padding: 0.6rem 0.75rem; border-radius: 6px; border: 1px solid #8886; font: inherit; }"," button { padding: 0.6rem 1rem; border-radius: 6px; border: 0; background: #2563eb; color: #fff; font: inherit; cursor: pointer; }"," button:disabled { opacity: 0.6; cursor: wait; }"," </style>","</head>","<body>",` <h1>${t}</h1>`,' <p class="sub">Signed in via Google SSO. Chat uses the agent API behind this page.</p>',' <div id="log">Ready. Send a message below.</div>',' <form id="chat">',' <input id="msg" type="text" placeholder="Ask the agent\u2026" autocomplete="off" required />',' <button type="submit">Send</button>'," </form>"," <script>",' const log = document.getElementById("log");',' const form = document.getElementById("chat");',' const input = document.getElementById("msg");'," // Stable per-browser session id so turns resume the same conversation (multi-turn).",' const sessionId = (self.crypto && crypto.randomUUID) ? crypto.randomUUID() : ("web-" + Date.now().toString(36) + Math.random().toString(36).slice(2));'," function append(text) {",' log.textContent += (log.textContent.endsWith("\\n") || log.textContent === "Ready. Send a message below." ? "" : "\\n") + text;'," }",' form.addEventListener("submit", async (e) => {'," e.preventDefault();"," const message = input.value.trim();"," if (!message) return;",' input.value = "";',' form.querySelector("button").disabled = true;',' append("\\nYou: " + message + "\\nAgent: ");'," try {"," const body = { message, session_id: sessionId };",' const res = await fetch("/v1/chat", {',' method: "POST",',' headers: { "Content-Type": "application/json" },'," body: JSON.stringify(body),"," });"," const data = await res.json();"," if (!res.ok) throw new Error(data.error || res.statusText);"," append(data.text || data.response || JSON.stringify(data));"," } catch (err) {",' append("[error] " + (err.message || err));'," } finally {",' form.querySelector("button").disabled = false;'," input.focus();"," }"," });"," input.focus();"," </script>","</body>","</html>",""].join(`
|
|
553
553
|
`)}function ia(t,e=" "){return[`${e}# serve-v4 P1.1: sticky by user identity (not session body \u2014 edge can't hash POST).`,`${e}# In-process RateLimiter is globally correct per user under this policy.`,`${e}reverse_proxy agent:${t} {`,`${e} lb_policy header X-Agent-User {`,`${e} fallback ip_hash`,`${e} }`,`${e}}`]}function Xy(t){let e=t.ingress==="edge-fed",n=(t.idpUrl||"").trim().replace(/\/+$/,""),r=[" @api path /v1/*"," handle @api {",...ia(t.port," ")," }"," root * /srv/www"," file_server"];return["# SSO edge + Cloudflare Tunnel. Set AGENTHIPPO_PUBLIC_HOSTNAME in .env (Caddy reads it below).","# TLS terminates at Cloudflare; origin listens on HTTP only.",...e?[`# No oauth2-proxy \u2014 Google login happens once at the central IdP (${n||"<IDP_URL>"});`,"# this agent only verifies the shared AgentHippo JWT (Bearer for IDE, cookie for browser)."]:[],"{"," auto_https off","}","","http://{$AGENTHIPPO_PUBLIC_HOSTNAME} {",...e?(0,gt.renderIdentityLayer)({mode:"federated",authBrokerAddr:"auth-broker:9780",idpUrl:n,healthProxy:ia(t.port," "),agentProxy:ia(t.port," "),catchallBody:r}):(0,gt.renderIdentityLayer)({mode:"self-issued",oauth2ProxyAddr:"oauth2-proxy:4180",authBrokerAddr:"auth-broker:9780",healthProxy:ia(t.port," "),agentProxy:ia(t.port," "),catchallBody:r}),"}",""].join(`
|
|
554
554
|
`)}function Jy(t){return["# Cloudflare Tunnel \u2014 run as the compose `cloudflared` service (config-file mode).","# One-time setup:","# cloudflared tunnel create <name> # creates the tunnel + a credentials JSON","# cp <that-creds>.json deploy/tunnel-credentials.json # mounted into the container (gitignore it!)",`# cloudflared tunnel route dns <name> ${t}`,"# set the tunnel id below, then: docker compose --profile edge --profile tunnel up -d","tunnel: <TUNNEL_ID>","credentials-file: /etc/cloudflared/credentials.json # mounted from deploy/tunnel-credentials.json","ingress:",` - hostname: ${t} # must match AGENTHIPPO_PUBLIC_HOSTNAME in .env`," service: http://caddy:80 # cloudflared container \u2192 Caddy over the compose network"," - service: http_status:404",""].join(`
|
|
555
|
-
`)}function zy(t,e=[]){return[" agent:"," build:"," context: .."," dockerfile: deploy/Dockerfile"," env_file:"," - path: .env"," required: false",...e,' volumes: ["agenthippo-state:/state"]',` expose: ["${t.port}"]`," # No host port \u2014 edge reaches agent on the Docker network only."," # T0.5a VPN: bash deploy/run.sh binds 127.0.0.1:"+t.port+" via docker run -p.",""]}function Qy(t){return[...t?[]:[" oauth2-proxy:",' profiles: ["edge"]'," image: quay.io/oauth2-proxy/oauth2-proxy:v7.7.1"," env_file:"," - path: .env"," required: false"," - path: .env.auth # OAuth client/cookie secrets \u2014 not shared with the agent"," required: false"," environment:"," OAUTH2_PROXY_HTTP_ADDRESS: 0.0.0.0:4180"," OAUTH2_PROXY_UPSTREAMS: file:///dev/null",' OAUTH2_PROXY_SET_XAUTHREQUEST: "true"',' OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER: "true"',' OAUTH2_PROXY_SKIP_PROVIDER_BUTTON: "true"',' OAUTH2_PROXY_REVERSE_PROXY: "true"',' expose: ["4180"]',""]," auth-broker:",' profiles: ["edge"]'," build:"," context: .."," dockerfile: deploy/Dockerfile.auth-broker"," env_file:"," - path: .env"," required: false"," - path: .env.auth # JWT signing key + team config \u2014 not shared with the agent"," required: false"," environment:",' AGENTHIPPO_AUTH_PORT: "9780"'," AGENTHIPPO_AUTH_DATA_DIR: /data",' user: "0:0" # named volume /data is root-owned on first create',' volumes: ["auth-broker-data:/data"]',' expose: ["9780"]',""," caddy:",' profiles: ["edge"]'," image: caddy:2.
|
|
555
|
+
`)}function zy(t,e=[]){return[" agent:"," build:"," context: .."," dockerfile: deploy/Dockerfile"," env_file:"," - path: .env"," required: false",...e,' volumes: ["agenthippo-state:/state"]',` expose: ["${t.port}"]`," # No host port \u2014 edge reaches agent on the Docker network only."," # T0.5a VPN: bash deploy/run.sh binds 127.0.0.1:"+t.port+" via docker run -p.",""]}function Qy(t){return[...t?[]:[" oauth2-proxy:",' profiles: ["edge"]'," image: quay.io/oauth2-proxy/oauth2-proxy:v7.7.1"," env_file:"," - path: .env"," required: false"," - path: .env.auth # OAuth client/cookie secrets \u2014 not shared with the agent"," required: false"," environment:"," OAUTH2_PROXY_HTTP_ADDRESS: 0.0.0.0:4180"," OAUTH2_PROXY_UPSTREAMS: file:///dev/null",' OAUTH2_PROXY_SET_XAUTHREQUEST: "true"',' OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER: "true"',' OAUTH2_PROXY_SKIP_PROVIDER_BUTTON: "true"',' OAUTH2_PROXY_REVERSE_PROXY: "true"'," # Absolute post-login redirects (rd=https://<host>/\u2026) are only honoured for whitelisted",' # hosts. The Caddyfile uses absolute rd so an IDE redirect_uri containing "//" inside'," # the authorize query is not rejected by the oauth2-proxy relative-URL validator."," OAUTH2_PROXY_WHITELIST_DOMAINS: ${OAUTH2_PROXY_WHITELIST_DOMAINS:-${AGENTHIPPO_PUBLIC_HOSTNAME}}",' expose: ["4180"]',""]," auth-broker:",' profiles: ["edge"]'," build:"," context: .."," dockerfile: deploy/Dockerfile.auth-broker"," env_file:"," - path: .env"," required: false"," - path: .env.auth # JWT signing key + team config \u2014 not shared with the agent"," required: false"," environment:",' AGENTHIPPO_AUTH_PORT: "9780"'," AGENTHIPPO_AUTH_DATA_DIR: /data",' user: "0:0" # named volume /data is root-owned on first create',' volumes: ["auth-broker-data:/data"]',' expose: ["9780"]',""," caddy:",' profiles: ["edge"]'," image: caddy:2.11-alpine"," env_file:"," - path: .env"," required: false",' ports: ["80:80", "443:443"] # use 80:80 not 127.0.0.1:80 \u2014 breaks vmnetd on some Docker Desktop builds',' volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro", "./www:/srv/www:ro"]',` depends_on: [${t?"agent, auth-broker":"oauth2-proxy, agent, auth-broker"}]`,""]}function Zy(){return[" cloudflared:",' profiles: ["tunnel"]'," image: cloudflare/cloudflared:2024.12.2"," restart: unless-stopped"," # Config-file mode: routing lives in deploy/cloudflared.yml (GitOps), credentials"," # in deploy/tunnel-credentials.json (gitignored secret from `cloudflared tunnel create`)."," command: tunnel --config /etc/cloudflared/config.yml run"," volumes:"," - ./cloudflared.yml:/etc/cloudflared/config.yml:ro"," - ./tunnel-credentials.json:/etc/cloudflared/credentials.json:ro"," depends_on: [caddy]",""]}function ew(t){return["#!/usr/bin/env bash","# T0.5b \u2014 Cloudflare Tunnel + Google SSO (no Access seats). Run: bash deploy/run-edge-tunnel.sh","set -euo pipefail",'cd "$(dirname "$0")"',"docker compose --profile edge --profile tunnel up -d --build",'echo "Public URL: https://'+t+' (after DNS + tunnel route configured)"',""].join(`
|
|
556
556
|
`)}function tw(t){return t.toUpperCase().replace(/[^A-Z0-9]/g,"_")+"_DATA_PLANE_TOKEN"}function nw(t){let e=(t.idpUrl||"").trim().replace(/\/+$/,""),n=`${t.packName}-agent`,r=tw(t.packName),s=["# Colocated agent \u2014 no Caddy/oauth2-proxy/auth-broker of its","# own. Joins the org IdP's docker network so its already-running Caddy can reach this","# agent by name; identity is verified entirely by the IdP's auth-broker (see","# deploy/agent-route.caddy for the route to copy onto the IdP host).",`name: ${t.packName}`,"services:"," agent:",` container_name: ${n}`," build:"," context: .."," dockerfile: deploy/Dockerfile"," env_file:"," - path: .env"," required: false",' volumes: ["agenthippo-state:/state"]'," networks: [colocate]",` expose: ["${t.port}"]`,"","networks:"," colocate:"," external: true"," name: ${AGENTHIPPO_IDP_NETWORK:-agenthippo-idp-colocate}","","volumes:"," agenthippo-state:",""].join(`
|
|
557
557
|
`),i=[`# Colocated route for ${t.publicHostname} \u2014 copy into enterprise-deploy/idp/agents/`,"# (rename e.g. to "+t.packName+".caddy), then on the IdP host:","# docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile","# Requires this agent's docker-compose.yml (already generated) to be up, so",`# ${n} exists on the shared \`agenthippo-idp-colocate\` network.`,"# http:// (not https://) to match the IdP Caddyfile's TLS model: auto_https is off","# there and TLS terminates in front (Cloudflare Tunnel/nginx), so every site block \u2014","# including this imported one \u2014 must be plain HTTP, same as the IdP's own site address.",`http://${t.publicHostname} {`,...(0,gt.renderIdentityLayer)({mode:"federated",authBrokerAddr:"auth-broker:9780",idpUrl:e,healthProxy:[` reverse_proxy ${n}:${t.port}`],agentProxy:[` reverse_proxy ${n}:${t.port}`],...t.edgeTurnConnector?{turnRegistration:{connectorAddr:t.edgeTurnConnector,tokenEnvVar:r}}:{}}),"}",""].join(`
|
|
558
558
|
`);return{Dockerfile:Un(t,{wwwDir:"/app/pack/deploy/www"}),"docker-compose.yml":s,"agent-route.caddy":i,"www/index.html":As(t.packName),"README.md":[`# Deploy: ${t.packName} \u2014 colocated with the org IdP`,"","No Caddy, no oauth2-proxy, no local auth-broker, no auth secrets on this host \u2014 this","agent rides the org IdP's already-running edge (`enterprise-deploy/idp/`) instead.","","```bash","cp deploy/.env.example deploy/.env # model key only \u2014 no auth secrets here at all","# Point at the IdP's colocate network if it was created under a different compose","# project name: export AGENTHIPPO_IDP_NETWORK=agenthippo-idp-colocate (the default)","docker compose -f deploy/docker-compose.yml up -d --build","","# Wire the route into the IdP's Caddy (one-time per agent):",`cp deploy/agent-route.caddy <idp-host>:enterprise-deploy/idp/agents/${t.packName}.caddy`,...t.edgeTurnConnector?["","# EDGE turn registration (P1 fix): the route registers each chat turn with the connector",`# sidecar (${t.edgeTurnConnector}). Set the register token in the IdP stack's env \u2014`,"# same value as the connector's AGENTHIPPO_DATA_PLANE_TOKEN \u2014 before reloading:",`# echo "${r}=<token>" >> enterprise-deploy/idp/.env`,"# The agent container never holds this token (that is the point)."]:[],"docker compose -f enterprise-deploy/idp/docker-compose.yml exec caddy \\"," caddy reload --config /etc/caddy/Caddyfile","```","",`DNS: point \`${t.publicHostname}\` at the IdP host (same as every other agent it fronts).`,"","**hippopennyg / bare-metal (Cloudflare \u2192 nginx:443 \u2192 IdP Caddy :8080):** `deploy init` does","**not** create DNS or nginx for you. Without an explicit Cloudflare **A record**, the name","falls through to the `*.agenthippo.ai` wildcard (`ghs.googlehosted.com` from",'`setup-agenthippo-domain.sh` for Cloud Run) \u2014 the site will look "broken" or hit Google.',"After the steps above, run:","","```bash","./enterprise-deploy/scripts/setup-colocated-agent-edge.sh \\",` --hostname ${t.publicHostname} --ssh root@168.119.174.239`,"```","","See `enterprise-deploy/idp/agents/README.md` for the full checklist.","**Browser:** after Google SSO, open `https://"+t.publicHostname+"/` for the static chat UI","(`deploy/www/index.html` served by the agent container at GET /).","",...tr(t)].join(`
|
|
@@ -567,14 +567,14 @@ ${n}`}function Ti(t,e){let n=["serve","--agent","/app/pack","--workspace","/stat
|
|
|
567
567
|
`)}}function bi(t){let e=(t||"").toLowerCase();return e==="ah-auto"?"AGENTHIPPO_GATEWAY_KEY":e.includes("codex")?"OPENAI_CODEX_API_KEY":e.includes("gpt")||e.includes("openai")?"OPENAI_API_KEY":e.includes("claude")||e.includes("anthropic")?"ANTHROPIC_API_KEY":"OPENAI_API_KEY"}function oa(t){return(t||"").trim().toLowerCase()==="ah-auto"}var rw="https://gateway.agenthippo.ai/v1";function Bf(t){return t.modelRouting==="gateway"}function sw(t){if(t.engine.trim().toLowerCase()==="cursor")return"api2.cursor.sh";if(oa(t.model))return"gateway.agenthippo.ai";let e=(t.model||"").toLowerCase();return e.includes("codex")||e.includes("gpt")||e.includes("openai")?"api.openai.com":"api.anthropic.com"}function tr(t){let e=Bf(t)?".":" \u2014 and, if the LiteLLM gateway/Spotlight sidecar is enabled, its own outbound is the same host.";return[`**Egress:** this container only needs outbound access to \`${sw(t)}:443\` (your model provider)${e}`,"Restrict egress to this allowlist if your org requires it.",""]}function f0(t,e,n,r,s){let i=[`\`${bi(t.model)}\` (or the pack's configured provider key) \u2014 your model provider credential`],o=["**agent container** \u2014 can reach your model provider and read/write its own state volume. Never holds the JWT signing secret or any OAuth client secret (`.env` vs `.env.auth` split)."];return n?(i.push("`AGENTHIPPO_AUTH_JWT_SECRET` \u2014 signs/verifies IDE bootstrap tokens (auth-broker only, never the agent)"),o.push("**auth-broker** \u2014 holds the JWT signing secret"+(r?" (shared with the central IdP)":"")+"; mints/verifies IDE tokens. Does not hold model provider credentials."),r||(i.push("`OAUTH2_PROXY_CLIENT_SECRET` + `OAUTH2_PROXY_COOKIE_SECRET` \u2014 this deployment's own Google OAuth client (oauth2-proxy only, never the agent)"),o.push("**oauth2-proxy** \u2014 holds this deployment's Google OAuth client secret; terminates the Google login flow. Does not hold the JWT signing secret or model credentials."))):s&&o.push("**this deployment holds no auth secrets at all** \u2014 identity is verified entirely by the org IdP's own auth-broker (see `agent-route.caddy`); nothing here signs or stores a JWT secret."),[`# Data & access summary \u2014 ${t.packName} (${e}${t.ingress?` / ${t.ingress}`:""})`,"","Generated by `agenthippo deploy init` \u2014 answers the questions a security review asks first.","","## Secrets",...i.map(a=>`- ${a}`),"","## Egress",`- Outbound network access is needed only to \`${sw(t)}:443\` (your model provider).`,...n&&r?["- API/Bearer requests are verified locally against the shared JWT secret \u2014 no network call to the central IdP per request. The one-time browser login flow (unauthenticated visitors) does redirect to the central IdP's public URL to sign in."]:[],...n&&!r?["- Google SSO calls `accounts.google.com` during login only (oauth2-proxy); no other third party is contacted per request."]:[],...s?["- Identity is verified via the org IdP's own auth-broker over the shared private docker network (`agenthippo-idp-colocate`), not a public network call. The one-time browser login flow (unauthenticated visitors) still redirects to the central IdP's public URL to sign in."]:[],"","## Blast radius per component",...o.map(a=>`- ${a}`),""].join(`
|
|
568
568
|
`)}function p0(t){let e=bi(t.model);return["# Cloud Run multi-container: agent (ingress) + LiteLLM + Spotlight OTEL collector.","# Generated by `agenthippo deploy init`. Deploy with `bash deploy/cloudrun-deploy.sh`","# (it substitutes ${...} and runs `gcloud run services replace`). Traces land in the GCS","# /state volume at /state/analytics/traces \u2014 query with query-analytics.sh (DuckDB).","apiVersion: serving.knative.dev/v1","kind: Service","metadata:"," name: ${SERVICE}","spec:"," template:"," metadata:"," annotations:"," # Sidecars need CPU between requests (the collector flushes async); no scale-to-zero throttle.",' run.googleapis.com/cpu-throttling: "false"',' run.googleapis.com/startup-cpu-boost: "true"'," # Start order: collector \u2192 litellm \u2192 agent, so traces are never dropped on cold start.",` run.googleapis.com/container-dependencies: '{"litellm":["otel-collector"],"agent":["litellm"]}'`,' autoscaling.knative.dev/maxScale: "1"',...t.gatewayChannels?.trim()?[' autoscaling.knative.dev/minScale: "1"']:[]," spec:"," timeoutSeconds: 3600"," containers:"," # Ingress container \u2014 the ONLY one with a port. Routes the engine through LiteLLM."," - name: agent"," image: ${IMAGE}"," ports:"," - name: http1",` containerPort: ${t.port}`," env:"," - { name: AGENTHIPPO_HOME, value: /state }",' - { name: AGENTHIPPO_NO_AUTO_INSTALL, value: "1" }',' - { name: AGENTHIPPO_IDENTITY_HEADER, value: "${IDENTITY_HEADER_VALUE}" }',El()," - { name: AGENTHIPPO_LITELLM_BASE_URL, value: http://localhost:4443 }"," - name: AGENTHIPPO_LITELLM_API_KEY"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-litellm-key, key: latest }"," volumeMounts:"," - { name: state, mountPath: /state }"," # LiteLLM gateway sidecar \u2014 exports every call to the collector (Spotlight cost/latency/tokens)."," - name: litellm"," image: ${LITELLM_IMAGE}"," env:"," - { name: OTEL_EXPORTER, value: otlp_http }"," - { name: OTEL_EXPORTER_OTLP_ENDPOINT, value: http://localhost:4318 }"," - name: LITELLM_MASTER_KEY"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-litellm-key, key: latest }",` # Upstream provider key for ${t.model||"this model"} (matches the gateway's os.environ/${e} ref).`," # Other models need their own key var \u2014 rename to match your model map.",` - name: ${e}`," valueFrom:"," secretKeyRef: { name: ${SERVICE}-provider-key, key: latest }"," # Spotlight OTEL collector sidecar \u2014 writes JSONL traces into the shared /state volume."," - name: otel-collector"," image: otel/opentelemetry-collector-contrib:0.118.0",' args: ["--config=/etc/otel/config.yaml"]'," env:"," - { name: AGENT_HOME_DIR, value: /state }"," volumeMounts:"," - { name: otel-config, mountPath: /etc/otel, readOnly: true }"," - { name: state, mountPath: /state }"," volumes:"," - name: state"," csi:"," driver: gcsfuse.run.googleapis.com"," volumeAttributes:"," bucketName: ${STATE_BUCKET}"," - name: otel-config"," secret:"," secretName: ${SERVICE}-otel-config"," items:"," - { key: latest, path: config.yaml }",""].join(`
|
|
569
569
|
`)}function Wr(){return["# Anchor to the staging root (parent of deploy/) \u2014 this script runs from any cwd.",'cd "$(dirname "$0")/.."']}function iw(){return["# Ingress: corp SSO via IAP (forwards X-Goog-Authenticated-User-Email \u2192 user_id).","# --iap is beta-track only on `gcloud run`; public access on an existing service is","# controlled via the run.invoker IAM binding (no --no-allow-unauthenticated on","# `services update`). So: enable IAP on the beta track, strip any allUsers invoker","# binding (keeps the service private), and grant IAP-secured Web User to each","# principal in IAP_MEMBERS (space-separated, e.g.","# IAP_MEMBERS='user:you@corp.com serviceAccount:sa@project.iam.gserviceaccount.com'","# ). Omit IAP_MEMBERS to rely on bindings you manage out-of-band.",'if [[ "${ENABLE_IAP:-1}" == "1" ]]; then',' gcloud beta run services update "$SERVICE" --region "$REGION" --iap',' gcloud run services remove-iam-policy-binding "$SERVICE" --region "$REGION" \\'," --member=allUsers --role=roles/run.invoker >/dev/null 2>&1 || true"," for m in ${IAP_MEMBERS:-}; do"," gcloud beta iap web add-iam-policy-binding \\",' --resource-type=cloud-run --service="$SERVICE" --region="$REGION" \\',' --member="$m" --role=roles/iap.httpsResourceAccessor'," done","fi"]}function g0(t){return["#!/usr/bin/env bash",`# Cloud Run + Spotlight: agent + LiteLLM + OTEL collector sidecars (engine: ${t.engine}${t.model?`, model ${t.model}`:""}).`,"# Traces persist in the GCS /state bucket; query with query-analytics.sh (DuckDB).","set -euo pipefail",...Wr(),`SERVICE="${t.packName}"`,'REGION="${REGION:-us-central1}"','PROJECT_ID="${PROJECT_ID:-$(gcloud config get-value project)}"','REPO="${ARTIFACT_REPO:-agenthippo}"','IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/${SERVICE}:latest"',`LITELLM_IMAGE="\${LITELLM_IMAGE:-${ws("agenthippo-litellm")}}"`,'STATE_BUCKET="${STATE_BUCKET:?set STATE_BUCKET=your-gcs-bucket (gcloud storage buckets create gs://...)}"',"# LITELLM_KEY = the proxy key the agent presents to LiteLLM (and LiteLLM master key).",'LITELLM_KEY="${LITELLM_KEY:?set LITELLM_KEY=<litellm proxy key>}"',`# PROVIDER_KEY = upstream key the gateway uses for ${t.model||"your model"} (gateway reads ${bi(t.model)}).`,'PROVIDER_KEY="${PROVIDER_KEY:?set PROVIDER_KEY=<upstream provider key>}"',"",'gcloud artifacts repositories describe "$REPO" --location="$REGION" 2>/dev/null \\',' || gcloud artifacts repositories create "$REPO" --repository-format=docker --location="$REGION"','gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet',"","# Rewrite the runtime base to this project Artifact Registry. Customer artifacts pin a","# dated ghcr.io/agenthippoai/runtime tag; mirror that exact tag into AR. No-op if already AR.",'DF_TMP="$(mktemp)"','sed "s|FROM ghcr.io/agenthippoai/runtime:|FROM ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/runtime:|" deploy/Dockerfile > "$DF_TMP"','docker build --pull --platform linux/amd64 -f "$DF_TMP" -t "$IMAGE" .','docker push "$IMAGE"',"","# Secrets: Spotlight collector config + the LiteLLM proxy key.",'ensure_secret() { gcloud secrets describe "$1" >/dev/null 2>&1 || gcloud secrets create "$1" --replication-policy=automatic; }','ensure_secret "${SERVICE}-otel-config"; gcloud secrets versions add "${SERVICE}-otel-config" --data-file=deploy/otel-collector.yaml','ensure_secret "${SERVICE}-litellm-key"; printf %s "$LITELLM_KEY" | gcloud secrets versions add "${SERVICE}-litellm-key" --data-file=-','ensure_secret "${SERVICE}-provider-key"; printf %s "$PROVIDER_KEY" | gcloud secrets versions add "${SERVICE}-provider-key" --data-file=-',"","# IAP (finalized below) forwards X-Goog-Authenticated-User-Email; pin serve to trust ONLY","# that header so an authenticated caller cannot impersonate another user via a spoofed","# X-Agent-User header. Skipped when ENABLE_IAP=0 (first-deploy bootstrap) since IAP never","# sets the header then.",'IDENTITY_HEADER_VALUE=""','[[ "${ENABLE_IAP:-1}" == "1" ]] && IDENTITY_HEADER_VALUE="x-goog-authenticated-user-email" || true',"","# Render the multi-container template for this project (sed \u2014 no gettext dependency) and deploy.",'RENDERED="$(mktemp)"','sed -e "s|\\${SERVICE}|${SERVICE}|g" -e "s|\\${IMAGE}|${IMAGE}|g" \\',' -e "s|\\${LITELLM_IMAGE}|${LITELLM_IMAGE}|g" -e "s|\\${STATE_BUCKET}|${STATE_BUCKET}|g" \\',' -e "s|\\${IDENTITY_HEADER_VALUE}|${IDENTITY_HEADER_VALUE}|g" \\',' deploy/service.yaml > "$RENDERED"','gcloud run services replace "$RENDERED" --region "$REGION"',"",...iw(),"","# Grant the runtime SA access to the secrets if needed:",`# SA="$(gcloud run services describe "$SERVICE" --region "$REGION" --format='value(spec.template.spec.serviceAccountName)')"`,'# for s in "${SERVICE}-otel-config" "${SERVICE}-litellm-key"; do gcloud secrets add-iam-policy-binding "$s" --member="serviceAccount:$SA" --role=roles/secretmanager.secretAccessor; done',""].join(`
|
|
570
|
-
`)}function h0(t){let e=t.engine.trim().toLowerCase()==="cursor",n=bi(t.model),r=Ti(t,{wwwDir:"/app/pack/deploy/www",port:gt.EDGE_AGENT_PORT}).map(o=>`"${o.replace(/"/g,'\\"')}"`).join(", "),i=["# Cloud Run + AgentHippo edge (Option B): Caddy (ingress) + oauth2-proxy + auth-broker","# + agent (+ LiteLLM + OTEL collector for Spotlight). No IAP \u2014 Caddy gates via SSO,","# so the IDE hits the public hostname directly with its AgentHippo Bearer token.","# Generated by `agenthippo deploy init --target cloudrun --ingress edge`.","apiVersion: serving.knative.dev/v1","kind: Service","metadata:"," name: ${SERVICE}","spec:"," template:"," metadata:"," annotations:",' run.googleapis.com/cpu-throttling: "false"',' run.googleapis.com/startup-cpu-boost: "true"',` run.googleapis.com/container-dependencies: '${e?'{"caddy":["agent","oauth2-proxy","auth-broker"]}':'{"caddy":["agent","oauth2-proxy","auth-broker"],"agent":["litellm"],"litellm":["otel-collector"]}'}'`,' autoscaling.knative.dev/maxScale: "1"',...t.gatewayChannels?.trim()?[' autoscaling.knative.dev/minScale: "1"']:[]," spec:"," timeoutSeconds: 3600"," containers:"," # Ingress \u2014 the ONLY container with a port. SSO edge for browsers + IDE Bearer."," - name: caddy"," image: caddy:2.
|
|
570
|
+
`)}function h0(t){let e=t.engine.trim().toLowerCase()==="cursor",n=bi(t.model),r=Ti(t,{wwwDir:"/app/pack/deploy/www",port:gt.EDGE_AGENT_PORT}).map(o=>`"${o.replace(/"/g,'\\"')}"`).join(", "),i=["# Cloud Run + AgentHippo edge (Option B): Caddy (ingress) + oauth2-proxy + auth-broker","# + agent (+ LiteLLM + OTEL collector for Spotlight). No IAP \u2014 Caddy gates via SSO,","# so the IDE hits the public hostname directly with its AgentHippo Bearer token.","# Generated by `agenthippo deploy init --target cloudrun --ingress edge`.","apiVersion: serving.knative.dev/v1","kind: Service","metadata:"," name: ${SERVICE}","spec:"," template:"," metadata:"," annotations:",' run.googleapis.com/cpu-throttling: "false"',' run.googleapis.com/startup-cpu-boost: "true"',` run.googleapis.com/container-dependencies: '${e?'{"caddy":["agent","oauth2-proxy","auth-broker"]}':'{"caddy":["agent","oauth2-proxy","auth-broker"],"agent":["litellm"],"litellm":["otel-collector"]}'}'`,' autoscaling.knative.dev/maxScale: "1"',...t.gatewayChannels?.trim()?[' autoscaling.knative.dev/minScale: "1"']:[]," spec:"," timeoutSeconds: 3600"," containers:"," # Ingress \u2014 the ONLY container with a port. SSO edge for browsers + IDE Bearer."," - name: caddy"," image: caddy:2.11-alpine"," ports:"," - name: http1",` containerPort: ${t.port}`," volumeMounts:"," - { name: caddy-config, mountPath: /etc/caddy, readOnly: true }"," # Auth broker \u2014 validates IDE Bearer JWTs (/api/ide/me) + IDE sign-in (/oauth/*)."," # Reuses the agent runtime image (the `agenthippo` CLI ships `auth-broker`)."," - name: auth-broker"," image: ${IMAGE}",' args: ["auth-broker"]'," env:",' - { name: AGENTHIPPO_AUTH_PORT, value: "9780" }'," - { name: AGENTHIPPO_AUTH_DATA_DIR, value: /data }"," - { name: AGENTHIPPO_AUTH_PUBLIC_URL, value: https://${PUBLIC_HOSTNAME} }"," - { name: AGENTHIPPO_AUTH_ALLOWED_DOMAINS, value: ${ALLOWED_DOMAINS} }"," - name: AGENTHIPPO_AUTH_JWT_SECRET"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-auth-jwt-secret, key: latest }"," # oauth2-proxy \u2014 Google SSO for browser clients (provider=google)."," - name: oauth2-proxy"," image: quay.io/oauth2-proxy/oauth2-proxy:v7.7.1"," env:"," - { name: OAUTH2_PROXY_HTTP_ADDRESS, value: 0.0.0.0:4180 }"," - { name: OAUTH2_PROXY_UPSTREAMS, value: file:///dev/null }",' - { name: OAUTH2_PROXY_SET_XAUTHREQUEST, value: "true" }',' - { name: OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER, value: "true" }',' - { name: OAUTH2_PROXY_SKIP_PROVIDER_BUTTON, value: "true" }',' - { name: OAUTH2_PROXY_REVERSE_PROXY, value: "true" }'," - { name: OAUTH2_PROXY_WHITELIST_DOMAINS, value: ${PUBLIC_HOSTNAME} }"," - { name: OAUTH2_PROXY_PROVIDER, value: google }"," - { name: OAUTH2_PROXY_EMAIL_DOMAINS, value: ${ALLOWED_DOMAINS} }"," - { name: OAUTH2_PROXY_REDIRECT_URL, value: https://${PUBLIC_HOSTNAME}/oauth2/callback }"," - name: OAUTH2_PROXY_CLIENT_ID"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-oauth-client-id, key: latest }"," - name: OAUTH2_PROXY_CLIENT_SECRET"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-oauth-client-secret, key: latest }"," - name: OAUTH2_PROXY_COOKIE_SECRET"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-oauth-cookie-secret, key: latest }"," # Agent \u2014 serves www + /v1/* on localhost (NOT ingress). Routed through LiteLLM."," - name: agent"," image: ${IMAGE}",` args: [${r}]`," env:"," - { name: AGENTHIPPO_HOME, value: /state }",' - { name: AGENTHIPPO_NO_AUTO_INSTALL, value: "1" }',El(),...e?[" - { name: AGENTHIPPO_CUSTOM_ENGINES_DIR, value: /engines }"," - name: CURSOR_API_KEY"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-provider-key, key: latest }"]:[" - { name: AGENTHIPPO_LITELLM_BASE_URL, value: http://localhost:4443 }"," - name: AGENTHIPPO_LITELLM_API_KEY"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-litellm-key, key: latest }"]," volumeMounts:"," - { name: state, mountPath: /state }",...ys(gt.EDGE_AGENT_PORT,!0),...Pr("2000m","2Gi")];return e||i.push(" # LiteLLM gateway sidecar \u2014 exports every call to the collector (Spotlight)."," - name: litellm"," image: ${LITELLM_IMAGE}"," env:"," - { name: OTEL_EXPORTER, value: otlp_http }"," - { name: OTEL_EXPORTER_OTLP_ENDPOINT, value: http://localhost:4318 }"," - name: LITELLM_MASTER_KEY"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-litellm-key, key: latest }",` # Upstream provider key for ${t.model||"this model"} (matches the gateway's os.environ/${n} ref).`,` - name: ${n}`," valueFrom:"," secretKeyRef: { name: ${SERVICE}-provider-key, key: latest }",...ys(4443),...Pr("1000m","1Gi")," # Spotlight OTEL collector sidecar \u2014 writes JSONL traces into the shared /state volume."," - name: otel-collector"," image: otel/opentelemetry-collector-contrib:0.118.0",' args: ["--config=/etc/otel/config.yaml"]'," env:"," - { name: AGENT_HOME_DIR, value: /state }"," volumeMounts:"," - { name: otel-config, mountPath: /etc/otel, readOnly: true }"," - { name: state, mountPath: /state }",...ys(4318),...Pr("1000m","512Mi")),i.push(" volumes:"," - name: state"," csi:"," driver: gcsfuse.run.googleapis.com"," volumeAttributes:"," bucketName: ${STATE_BUCKET}"," - name: caddy-config"," secret:"," secretName: ${SERVICE}-caddyfile"," items:"," - { key: latest, path: Caddyfile }"),e||i.push(" - name: otel-config"," secret:"," secretName: ${SERVICE}-otel-config"," items:"," - { key: latest, path: config.yaml }"),i.push(""),i.join(`
|
|
571
571
|
`)}function m0(t){let e=t.engine.trim().toLowerCase()==="cursor",n=t.model?.trim()?`, model ${t.model.trim()}`:"",r=["#!/usr/bin/env bash","# Cloud Run + AgentHippo edge (Option B): Caddy + oauth2-proxy + auth-broker front the agent.",`# Engine: ${t.engine}${n}. No IAP \u2014 Caddy gates via SSO (Google for browsers, AgentHippo JWT for IDE).`,"# The IDE points spec.remote.url straight at https://$PUBLIC_HOSTNAME (same flow as remote-vm).","set -euo pipefail",...Wr(),`SERVICE="${t.packName}"`,'REGION="${REGION:-us-central1}"','PROJECT_ID="${PROJECT_ID:-$(gcloud config get-value project)}"','REPO="${ARTIFACT_REPO:-agenthippo}"','IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/${SERVICE}:latest"',`LITELLM_IMAGE="\${LITELLM_IMAGE:-${ws("agenthippo-litellm")}}"`,"# Public hostname (custom domain mapped to the service) + the Google Workspace domain allowed by SSO.",'PUBLIC_HOSTNAME="${PUBLIC_HOSTNAME:?set PUBLIC_HOSTNAME=your.edge.hostname (gcloud run domain-mappings create --service $SERVICE --domain ...)}"','ALLOWED_DOMAINS="${ALLOWED_DOMAINS:?set ALLOWED_DOMAINS=yourworkspace.com (oauth2-proxy email domain + broker allowed domains)}"','STATE_BUCKET="${STATE_BUCKET:?set STATE_BUCKET=your-gcs-bucket (gcloud storage buckets create gs://...)}"',"# Edge secrets (from .env.auth): Google OAuth client + cookie secret + AgentHippo JWT signing key.",'OAUTH_CLIENT_ID="${OAUTH_CLIENT_ID:?set OAUTH_CLIENT_ID=<google oauth web client id>}"','OAUTH_CLIENT_SECRET="${OAUTH_CLIENT_SECRET:?set OAUTH_CLIENT_SECRET=<google oauth web client secret>}"','OAUTH_COOKIE_SECRET="${OAUTH_COOKIE_SECRET:?set OAUTH_COOKIE_SECRET=$(openssl rand -base64 32 | head -c 32)}"','AUTH_JWT_SECRET="${AUTH_JWT_SECRET:?set AUTH_JWT_SECRET=$(openssl rand -hex 32)}"',...e?['PROVIDER_KEY="${PROVIDER_KEY:?set PROVIDER_KEY=<cursor api key>}"']:["# LiteLLM proxy key + the upstream provider key the gateway uses for the model.",'LITELLM_KEY="${LITELLM_KEY:?set LITELLM_KEY=<litellm proxy key>}"','PROVIDER_KEY="${PROVIDER_KEY:?set PROVIDER_KEY=<upstream provider key>}"'],"",'gcloud artifacts repositories describe "$REPO" --location="$REGION" 2>/dev/null \\',' || gcloud artifacts repositories create "$REPO" --repository-format=docker --location="$REGION"','gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet',"","# One agent image serves both the agent and the auth-broker containers (CLI ships `auth-broker`).","# Rewrite the runtime base to this project Artifact Registry. Customer artifacts pin a","# dated ghcr.io/agenthippoai/runtime tag; mirror that exact tag into AR. No-op if already AR.",'DF_TMP="$(mktemp)"','sed "s|FROM ghcr.io/agenthippoai/runtime:|FROM ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/runtime:|" deploy/Dockerfile > "$DF_TMP"','docker build --pull --platform linux/amd64 -f "$DF_TMP" -t "$IMAGE" .','docker push "$IMAGE"',"","# Secrets: edge config + provider/litellm keys.",'ensure_secret() { gcloud secrets describe "$1" >/dev/null 2>&1 || gcloud secrets create "$1" --replication-policy=automatic; }','ensure_secret "${SERVICE}-caddyfile"; gcloud secrets versions add "${SERVICE}-caddyfile" --data-file=deploy/Caddyfile','ensure_secret "${SERVICE}-auth-jwt-secret"; printf %s "$AUTH_JWT_SECRET" | gcloud secrets versions add "${SERVICE}-auth-jwt-secret" --data-file=-','ensure_secret "${SERVICE}-oauth-client-id"; printf %s "$OAUTH_CLIENT_ID" | gcloud secrets versions add "${SERVICE}-oauth-client-id" --data-file=-','ensure_secret "${SERVICE}-oauth-client-secret"; printf %s "$OAUTH_CLIENT_SECRET" | gcloud secrets versions add "${SERVICE}-oauth-client-secret" --data-file=-','ensure_secret "${SERVICE}-oauth-cookie-secret"; printf %s "$OAUTH_COOKIE_SECRET" | gcloud secrets versions add "${SERVICE}-oauth-cookie-secret" --data-file=-','ensure_secret "${SERVICE}-provider-key"; printf %s "$PROVIDER_KEY" | gcloud secrets versions add "${SERVICE}-provider-key" --data-file=-'];return e||r.push('ensure_secret "${SERVICE}-otel-config"; gcloud secrets versions add "${SERVICE}-otel-config" --data-file=deploy/otel-collector.yaml','ensure_secret "${SERVICE}-litellm-key"; printf %s "$LITELLM_KEY" | gcloud secrets versions add "${SERVICE}-litellm-key" --data-file=-'),r.push("","# Render the multi-container template for this project (sed \u2014 no gettext dependency) and deploy.",'RENDERED="$(mktemp)"','sed -e "s|\\${SERVICE}|${SERVICE}|g" -e "s|\\${IMAGE}|${IMAGE}|g" \\',' -e "s|\\${LITELLM_IMAGE}|${LITELLM_IMAGE}|g" -e "s|\\${STATE_BUCKET}|${STATE_BUCKET}|g" \\',' -e "s|\\${PUBLIC_HOSTNAME}|${PUBLIC_HOSTNAME}|g" -e "s|\\${ALLOWED_DOMAINS}|${ALLOWED_DOMAINS}|g" \\',' deploy/service.yaml > "$RENDERED"','gcloud run services replace "$RENDERED" --region "$REGION"',"","# No IAP: Caddy is the gate, so Cloud Run itself must allow public ingress (allUsers invoker).","# Without this, Cloud Run 403s before Caddy ever sees the request to run SSO.",'gcloud run services add-iam-policy-binding "$SERVICE" --region "$REGION" \\'," --member=allUsers --role=roles/run.invoker >/dev/null 2>&1 || true","",'# Map the custom domain (once): gcloud run domain-mappings create --service "$SERVICE" --region "$REGION" --domain "$PUBLIC_HOSTNAME"',"# Then add the CNAME Cloud Run prints to your DNS. Google OAuth Web client must list","# https://$PUBLIC_HOSTNAME as an authorized origin + https://$PUBLIC_HOSTNAME/oauth2/callback as a redirect.","","# Grant the runtime SA access to the secrets if needed:",`# SA="$(gcloud run services describe "$SERVICE" --region "$REGION" --format='value(spec.template.spec.serviceAccountName)')"`,'# for s in "${SERVICE}-caddyfile" "${SERVICE}-auth-jwt-secret" "${SERVICE}-oauth-client-id" "${SERVICE}-oauth-client-secret" "${SERVICE}-oauth-cookie-secret" "${SERVICE}-provider-key"; do','# gcloud secrets add-iam-policy-binding "$s" --member="serviceAccount:$SA" --role=roles/secretmanager.secretAccessor',"# done",""),r.join(`
|
|
572
572
|
`)}function E0(t){let e=!!t.gatewayChannels?.trim(),n=t.engine.trim().toLowerCase()==="cursor";if(t.ingress==="edge")return _0(t,e,n);if(t.ingress==="edge-fed")return T0(t,e,n);if(!n)return{Dockerfile:Un(t,{wwwDir:"/app/pack/deploy/www"}),"www/index.html":As(t.packName),"service.yaml":p0(t),"otel-collector.yaml":_l,"cloudrun-deploy.sh":g0(t),"README.md":[`# Deploy: ${t.packName} \u2014 Cloud Run + Spotlight (multi-container)`,"",`Engine: **${t.engine}**${t.model?`, model: **${t.model}**`:""}. Runtime base: \`${t.image}\`.`,"","Three sidecars share the pod's localhost (Cloud Run multi-container):","- **agent** (ingress) \u2192 routes the engine to `http://localhost:4443`","- **litellm** gateway \u2192 exports OTLP to `http://localhost:4318`","- **otel-collector** \u2192 writes JSONL traces to the GCS `/state` volume (`/state/analytics/traces`).","","```bash","gcloud storage buckets create gs://<your-state-bucket> # once","export STATE_BUCKET=<your-state-bucket> LITELLM_KEY=<litellm proxy key>","bash deploy/cloudrun-deploy.sh # builds linux/amd64, creates secrets, `gcloud run services replace service.yaml`","```","","`service.yaml` is the multi-container template (checked in so the wiring is reviewable);","the deploy script substitutes `${SERVICE} ${IMAGE} ${STATE_BUCKET} ${LITELLM_IMAGE}`.","Provider creds (Anthropic key / Vertex ADC / OpenAI \u2026) go on the **litellm** sidecar, not the agent.","",e?"**Inbound channel (1b):** `minScale: 1` pins one always-on instance (the socket can't scale to zero).":"**No inbound channel (1a):** scales to zero; traces + sessions persist on the GCS `/state` volume.","","**Spotlight:** once traffic flows, query the GCS-backed traces with `query-analytics.sh` (DuckDB) against `/state/analytics/traces`.","","**Lean alternative (no Spotlight):** a single-container deploy that calls a provider directly","(or an external LiteLLM) \u2014 set `AGENTHIPPO_LITELLM_BASE_URL` to your endpoint and drop the sidecars.","",...tr(t)].join(`
|
|
573
573
|
`)};let r=e?" --min-instances 1 --max-instances 1 --no-cpu-throttling \\":" --min-instances 0 --max-instances 1 \\",s=n?"AGENTHIPPO_NO_AUTO_INSTALL=1,AGENTHIPPO_HOME=/state,AGENTHIPPO_CUSTOM_ENGINES_DIR=/engines":"CLAUDE_CODE_USE_VERTEX=1,AGENTHIPPO_NO_AUTO_INSTALL=1,AGENTHIPPO_HOME=/state",i=n?"CURSOR_API_KEY=agenthippo-cursor-key:latest":void 0,o=t.model?.trim()?`, model ${t.model.trim()}`:"",a=["#!/usr/bin/env bash","# T1-generic: Cloud Run with built-in IAP (corp SSO, no load balancer).",`# Engine: ${t.engine}${o}. IAP forwards X-Goog-Authenticated-User-Email \u2192 user_id.`,"set -euo pipefail",...Wr(),`SERVICE="${t.packName}"`,'REGION="${REGION:-us-central1}"','PROJECT_ID="${PROJECT_ID:-$(gcloud config get-value project)}"','REPO="${ARTIFACT_REPO:-agenthippo}"','IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/${SERVICE}:latest"',"# Q1: durable state. Cloud Run local disk is ephemeral (wiped on scale-to-zero),","# so mount a GCS bucket at /state \u2014 sessions/provider-sessions/analytics survive.",'STATE_BUCKET="${STATE_BUCKET:?set STATE_BUCKET=your-gcs-bucket (gcloud storage buckets create gs://...)}"',"",'gcloud artifacts repositories describe "$REPO" --location="$REGION" 2>/dev/null \\',' || gcloud artifacts repositories create "$REPO" --repository-format=docker --location="$REGION"','gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet',"","# Build from pack root; Dockerfile is deploy/Dockerfile (not pack root \u2014 gcloud --source . misses it).","# Rewrite the runtime base to this project Artifact Registry. Customer artifacts pin a","# dated ghcr.io/agenthippoai/runtime tag; mirror that exact tag into AR. No-op if already AR.",'DF_TMP="$(mktemp)"','sed "s|FROM ghcr.io/agenthippoai/runtime:|FROM ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/runtime:|" deploy/Dockerfile > "$DF_TMP"','docker build --pull --platform linux/amd64 -f "$DF_TMP" -t "$IMAGE" .','docker push "$IMAGE"',"","# IAP (when enabled below) forwards X-Goog-Authenticated-User-Email; pin serve to trust","# ONLY that header so an authenticated caller cannot impersonate another user via a","# spoofed X-Agent-User header. Skipped when ENABLE_IAP=0 (first-deploy bootstrap) since","# IAP never sets the header then \u2014 pinning to an absent header would make every request","# anonymous instead of leaving body-supplied identity usable during bootstrap.",'IDENTITY_ENV=""','[[ "${ENABLE_IAP:-1}" == "1" ]] && IDENTITY_ENV=",AGENTHIPPO_IDENTITY_HEADER=x-goog-authenticated-user-email" || true',"","# Deploy private (no --allow-unauthenticated). IAP + role grants are applied below on the beta track.",'gcloud run deploy "$SERVICE" \\',' --image "$IMAGE" \\'," --no-allow-unauthenticated \\",` --port ${t.port} \\`,r," --timeout 3600 \\",' --add-volume name=state,type=cloud-storage,bucket="$STATE_BUCKET" \\'," --add-volume-mount volume=state,mount-path=/state \\",` --set-env-vars "${s}\${IDENTITY_ENV}" \\`,...i?[` --set-secrets ${i} \\`]:[],' --region "$REGION"',"",...iw(),"","# Notes:",e?"# - INBOUND channel (1b): pinned min=max=1 + CPU-always-on so the socket stays alive (can't scale to zero).":"# - No inbound channel (1a): scales to zero; state on the GCS volume survives the instance dying.","# - GCS-FUSE adds latency on session read/write vs local disk; fine for chat, watch for heavy I/O.",n?`# - Cursor engine: CURSOR_API_KEY secret + ${ws("runtime","cursor")} base (AGENTHIPPO_CUSTOM_ENGINES_DIR=/engines).`:"# - Vertex path = keyless Claude on your existing GCP entitlement; swap for ANTHROPIC_API_KEY if preferred.","# - First deploy without IAP org setup: ENABLE_IAP=0 bash deploy/cloudrun-deploy.sh",""].join(`
|
|
574
574
|
`),c=n?["2. `gcloud secrets create agenthippo-cursor-key --data-file=-` (paste your `CURSOR_API_KEY`).","3. Grant Cloud Run access: `gcloud secrets add-iam-policy-binding agenthippo-cursor-key --member=serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com --role=roles/secretmanager.secretAccessor`.","4. `bash deploy/cloudrun-deploy.sh` \u2014 builds linux/amd64, pushes to Artifact Registry, deploys with GCS /state."]:["2. `bash deploy/cloudrun-deploy.sh` \u2014 builds linux/amd64, pushes to Artifact Registry, deploys with `--iap` + GCS /state."];return{Dockerfile:Un(t,{wwwDir:"/app/pack/deploy/www"}),"www/index.html":As(t.packName),"cloudrun-deploy.sh":a,"README.md":[`# Deploy: ${t.packName} \u2014 Cloud Run (T1-generic)`,"",`Engine: **${t.engine}**${t.model?`, model: **${t.model}**`:""}. Runtime base: \`${t.image}\`.`,"","1. `gcloud storage buckets create gs://<your-state-bucket>` then `export STATE_BUCKET=<your-state-bucket>`.",...c,"","SSO + ingress: built-in IAP (`X-Goog-Authenticated-User-Email`). **Q1:** `/state` is a mounted GCS bucket.",t.gatewayChannels?"**Inbound channel (1b):** pinned single always-on instance (no scale-to-zero).":"**No inbound channel (1a):** scales to zero; state persists on GCS.","","**Browser:** open the service URL \u2192 Google login \u2192 chat UI at `/` (same as vm `deploy/www/index.html`).","","**Verify** (smoke test via local proxy \u2014 bypasses IAP auth for direct testing):","```bash",`gcloud run services proxy ${t.packName} --region "\${REGION:-us-central1}" &`,"PROXY=http://127.0.0.1:8080",'curl -s "$PROXY/ready"','curl -s -XPOST "$PROXY/v1/chat" \\',' -H "X-Goog-Authenticated-User-Email: accounts.google.com:you@corp.com" \\',' -H "Content-Type: application/json" \\',` -d '{"message":"Reply with exactly: CLOUDRUN_OK","session_id":"cr-1"}'`,"```","",...tr(t)].join(`
|
|
575
575
|
`)}}function _0(t,e,n){let r=n?"Caddy (ingress) + oauth2-proxy + auth-broker + agent.":"Caddy (ingress) + oauth2-proxy + auth-broker + agent + LiteLLM + OTEL collector (Spotlight).",s={Dockerfile:Un(t,{wwwDir:"/app/pack/deploy/www"}),"www/index.html":As(t.packName),Caddyfile:(0,gt.renderSidecarEdgeCaddyfile)(),"service.yaml":h0(t),"cloudrun-deploy.sh":m0(t)};return n||(s["otel-collector.yaml"]=_l),s["README.md"]=[`# Deploy: ${t.packName} \u2014 Cloud Run + AgentHippo edge (Option B)`,"",`Engine: **${t.engine}**${t.model?`, model: **${t.model}**`:""}. Runtime base: \`${t.image}\`.`,"",`${r} Sidecars share the pod's localhost (Cloud Run multi-container):`,"- **caddy** (ingress, the only port) \u2192 SSO edge: oauth2-proxy for browsers, auth-broker for IDE Bearer",`- **agent** \u2192 serves www + /v1/* on localhost:${gt.EDGE_AGENT_PORT} (NOT ingress)`,"- **auth-broker** \u2192 validates IDE AgentHippo JWTs (`/api/ide/me`) + IDE sign-in (`/oauth/*`)","- **oauth2-proxy** \u2192 Google SSO for browser clients (`provider=google`)",...n?[]:["- **litellm** gateway \u2192 exports OTLP to `http://localhost:4318`","- **otel-collector** \u2192 writes JSONL traces to the GCS `/state` volume"],"","**No IAP** \u2014 Caddy is the gate, so the IDE points `spec.remote.url` straight at","`https://$PUBLIC_HOSTNAME` and the existing AgentHippo SSO flow works (same as `remote-vm`).","Cloud Run itself is public (`allUsers` invoker) so traffic can reach Caddy to be authed.","","```bash","gcloud storage buckets create gs://<state-bucket> # once","cp deploy/.env.example deploy/.env && cp deploy/.env.auth.example deploy/.env.auth # fill both","export PUBLIC_HOSTNAME=<your.edge.hostname> ALLOWED_DOMAINS=<yourworkspace.com>","export OAUTH_CLIENT_ID=\u2026 OAUTH_CLIENT_SECRET=\u2026 OAUTH_COOKIE_SECRET=\u2026 AUTH_JWT_SECRET=\u2026",...n?["export PROVIDER_KEY=<cursor api key>"]:["export LITELLM_KEY=\u2026 PROVIDER_KEY=\u2026 STATE_BUCKET=<state-bucket>"],"bash deploy/cloudrun-deploy.sh # builds linux/amd64, upserts secrets, `gcloud run services replace`",'gcloud run domain-mappings create --service "$SERVICE" --region "${REGION:-us-central1}" --domain "$PUBLIC_HOSTNAME" # once',"```","","Google OAuth Web client (for oauth2-proxy): authorized origin `https://$PUBLIC_HOSTNAME`,","redirect `https://$PUBLIC_HOSTNAME/oauth2/callback`. Restrict to your Workspace domain via","`ALLOWED_DOMAINS` (oauth2-proxy `email_domains` + broker `allowed_domains`).","","`service.yaml` placeholders (`${SERVICE} ${IMAGE} ${LITELLM_IMAGE} ${STATE_BUCKET} ${PUBLIC_HOSTNAME} ${ALLOWED_DOMAINS}`)","are substituted by the deploy script. The Caddyfile is stored in Secret Manager (`${SERVICE}-caddyfile`).","",e?"**Inbound channel (1b):** `minScale: 1` pins one always-on instance (the socket can't scale to zero).":"**No inbound channel (1a):** scales to zero; traces + sessions persist on the GCS `/state` volume.","",...tr(t)].join(`
|
|
576
|
-
`),s}function ys(t,e=!1){return e?[" startupProbe:"," failureThreshold: 48"," periodSeconds: 5"," tcpSocket:",` port: ${t}`," timeoutSeconds: 3"]:[" startupProbe:"," failureThreshold: 30"," periodSeconds: 5"," tcpSocket:",` port: ${t}`," timeoutSeconds: 3"]}function Pr(t,e){return[" resources:"," limits:",` cpu: ${t}`,` memory: ${e}`]}function y0(t){let e=t.engine.trim().toLowerCase()==="cursor",n=bi(t.model),r=Ti(t,{wwwDir:"/app/pack/deploy/www",port:gt.EDGE_AGENT_PORT}).map(c=>`"${c.replace(/"/g,'\\"')}"`).join(", "),s=(t.cookieDomain||".agenthippo.ai").trim(),i=(t.idpUrl||"https://auth.agenthippo.ai").trim().replace(/\/+$/,""),a=["# Cloud Run + AgentHippo federated edge (A\u2032): Caddy (ingress) + auth-broker + agent","# (+ LiteLLM + OTEL collector for Spotlight). No oauth2-proxy \u2014 Google login happens once","# at the central IdP; this agent verifies the shared .agenthippo.ai AgentHippo JWT.","# Generated by `agenthippo deploy init --target cloudrun --ingress edge-fed`.","apiVersion: serving.knative.dev/v1","kind: Service","metadata:"," name: ${SERVICE}","spec:"," template:"," metadata:"," annotations:",' run.googleapis.com/cpu-throttling: "false"',' run.googleapis.com/startup-cpu-boost: "true"',` run.googleapis.com/container-dependencies: '${e?'{"caddy":["agent","auth-broker"]}':'{"caddy":["agent","auth-broker"],"agent":["litellm"],"litellm":["otel-collector"]}'}'`,' autoscaling.knative.dev/maxScale: "1"',...t.gatewayChannels?.trim()?[' autoscaling.knative.dev/minScale: "1"']:[]," spec:"," timeoutSeconds: 3600"," containers:"," # Ingress \u2014 the ONLY container with a port. Verifies the shared JWT (no Google here)."," - name: caddy"," image: caddy:2.
|
|
577
|
-
`)}function w0(t){let e=Ti(t,{wwwDir:"/app/pack/deploy/www",port:gt.EDGE_AGENT_PORT}).map(i=>`"${i.replace(/"/g,'\\"')}"`).join(", "),n=(t.cookieDomain||".agenthippo.ai").trim(),r=(t.idpUrl||"https://auth.agenthippo.ai").trim().replace(/\/+$/,"");return["# Cloud Run + AgentHippo federated edge (A\u2032) \u2014 gateway-direct (lean):","# Caddy (ingress) + auth-broker + agent. No LiteLLM/OTEL sidecars.","# Agent routes ah-auto (and other gateway models) to gateway.agenthippo.ai directly.","# Generated by `agenthippo deploy init --target cloudrun --ingress edge-fed --model-routing gateway`.","apiVersion: serving.knative.dev/v1","kind: Service","metadata:"," name: ${SERVICE}","spec:"," template:"," metadata:"," annotations:",' run.googleapis.com/cpu-throttling: "false"',' run.googleapis.com/startup-cpu-boost: "true"',` run.googleapis.com/container-dependencies: '{"caddy":["agent","auth-broker"]}'`,' autoscaling.knative.dev/maxScale: "1"',...t.gatewayChannels?.trim()?[' autoscaling.knative.dev/minScale: "1"']:[]," spec:"," timeoutSeconds: 3600"," containers:"," - name: caddy"," image: caddy:2.
|
|
576
|
+
`),s}function ys(t,e=!1){return e?[" startupProbe:"," failureThreshold: 48"," periodSeconds: 5"," tcpSocket:",` port: ${t}`," timeoutSeconds: 3"]:[" startupProbe:"," failureThreshold: 30"," periodSeconds: 5"," tcpSocket:",` port: ${t}`," timeoutSeconds: 3"]}function Pr(t,e){return[" resources:"," limits:",` cpu: ${t}`,` memory: ${e}`]}function y0(t){let e=t.engine.trim().toLowerCase()==="cursor",n=bi(t.model),r=Ti(t,{wwwDir:"/app/pack/deploy/www",port:gt.EDGE_AGENT_PORT}).map(c=>`"${c.replace(/"/g,'\\"')}"`).join(", "),s=(t.cookieDomain||".agenthippo.ai").trim(),i=(t.idpUrl||"https://auth.agenthippo.ai").trim().replace(/\/+$/,""),a=["# Cloud Run + AgentHippo federated edge (A\u2032): Caddy (ingress) + auth-broker + agent","# (+ LiteLLM + OTEL collector for Spotlight). No oauth2-proxy \u2014 Google login happens once","# at the central IdP; this agent verifies the shared .agenthippo.ai AgentHippo JWT.","# Generated by `agenthippo deploy init --target cloudrun --ingress edge-fed`.","apiVersion: serving.knative.dev/v1","kind: Service","metadata:"," name: ${SERVICE}","spec:"," template:"," metadata:"," annotations:",' run.googleapis.com/cpu-throttling: "false"',' run.googleapis.com/startup-cpu-boost: "true"',` run.googleapis.com/container-dependencies: '${e?'{"caddy":["agent","auth-broker"]}':'{"caddy":["agent","auth-broker"],"agent":["litellm"],"litellm":["otel-collector"]}'}'`,' autoscaling.knative.dev/maxScale: "1"',...t.gatewayChannels?.trim()?[' autoscaling.knative.dev/minScale: "1"']:[]," spec:"," timeoutSeconds: 3600"," containers:"," # Ingress \u2014 the ONLY container with a port. Verifies the shared JWT (no Google here)."," - name: caddy"," image: caddy:2.11-alpine"," ports:"," - name: http1",` containerPort: ${t.port}`," volumeMounts:"," - { name: caddy-config, mountPath: /etc/caddy, readOnly: true }",...Pr("1000m","512Mi")," # Auth broker \u2014 verifies IDE Bearer JWTs AND the federated .agenthippo.ai cookie (/api/ide/me)."," # Same shared AGENTHIPPO_AUTH_JWT_SECRET as the central IdP so its tokens verify here."," - name: auth-broker"," image: ${IMAGE}",' args: ["auth-broker"]'," env:",' - { name: AGENTHIPPO_AUTH_PORT, value: "9780" }'," - { name: AGENTHIPPO_AUTH_DATA_DIR, value: /data }"," - { name: AGENTHIPPO_AUTH_PUBLIC_URL, value: https://${PUBLIC_HOSTNAME} }"," - { name: AGENTHIPPO_AUTH_ALLOWED_DOMAINS, value: ${ALLOWED_DOMAINS} }",` - { name: AGENTHIPPO_AUTH_COOKIE_DOMAIN, value: "${s}" }`,` - { name: AGENTHIPPO_AUTH_IDP_URL, value: "${i}" }`,' - { name: AGENTHIPPO_AUTH_COOKIE_NAME, value: "ah_token" }'," - name: AGENTHIPPO_AUTH_JWT_SECRET"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-auth-jwt-secret, key: latest }",...ys(9780),...Pr("1000m","512Mi")," # Agent \u2014 serves www + /v1/* on localhost (NOT ingress). Routed through LiteLLM."," - name: agent"," image: ${IMAGE}",` args: [${r}]`," env:"," - { name: AGENTHIPPO_HOME, value: /state }",' - { name: AGENTHIPPO_NO_AUTO_INSTALL, value: "1" }',El(),...e?[" - { name: AGENTHIPPO_CUSTOM_ENGINES_DIR, value: /engines }"," - name: CURSOR_API_KEY"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-provider-key, key: latest }"]:[" - { name: AGENTHIPPO_LITELLM_BASE_URL, value: http://localhost:4443 }"," - name: AGENTHIPPO_LITELLM_API_KEY"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-litellm-key, key: latest }"]," volumeMounts:"," - { name: state, mountPath: /state }",...ys(gt.EDGE_AGENT_PORT,!0),...Pr("2000m","2Gi")];return e||a.push(" # LiteLLM gateway sidecar \u2014 exports every call to the collector (Spotlight)."," - name: litellm"," image: ${LITELLM_IMAGE}"," env:"," - { name: OTEL_EXPORTER, value: otlp_http }"," - { name: OTEL_EXPORTER_OTLP_ENDPOINT, value: http://localhost:4318 }"," - name: LITELLM_MASTER_KEY"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-litellm-key, key: latest }",oa(t.model)?" # ah-auto \u2192 gateway.agenthippo.ai (AGENTHIPPO_GATEWAY_KEY = ahgw_\u2026 service key).":` # Upstream provider key for ${t.model||"this model"} (matches os.environ/${n}).`,` - name: ${n}`," valueFrom:"," secretKeyRef: { name: ${SERVICE}-provider-key, key: latest }",...ys(4443),...Pr("1000m","1Gi")," # Spotlight OTEL collector sidecar \u2014 writes JSONL traces into the shared /state volume."," - name: otel-collector"," image: otel/opentelemetry-collector-contrib:0.118.0",' args: ["--config=/etc/otel/config.yaml"]'," env:"," - { name: AGENT_HOME_DIR, value: /state }"," volumeMounts:"," - { name: otel-config, mountPath: /etc/otel, readOnly: true }"," - { name: state, mountPath: /state }",...ys(4318),...Pr("1000m","512Mi")),a.push(" volumes:"," # In-memory state (matches the working spotlight deploy \u2014 no GCS SA perms needed)."," - name: state"," emptyDir:"," medium: Memory"," sizeLimit: 1Gi"," - name: caddy-config"," secret:"," secretName: ${SERVICE}-caddyfile"," items:"," - { key: latest, path: Caddyfile }"),e||a.push(" - name: otel-config"," secret:"," secretName: ${SERVICE}-otel-config"," items:"," - { key: latest, path: config.yaml }"),a.push(""),a.join(`
|
|
577
|
+
`)}function w0(t){let e=Ti(t,{wwwDir:"/app/pack/deploy/www",port:gt.EDGE_AGENT_PORT}).map(i=>`"${i.replace(/"/g,'\\"')}"`).join(", "),n=(t.cookieDomain||".agenthippo.ai").trim(),r=(t.idpUrl||"https://auth.agenthippo.ai").trim().replace(/\/+$/,"");return["# Cloud Run + AgentHippo federated edge (A\u2032) \u2014 gateway-direct (lean):","# Caddy (ingress) + auth-broker + agent. No LiteLLM/OTEL sidecars.","# Agent routes ah-auto (and other gateway models) to gateway.agenthippo.ai directly.","# Generated by `agenthippo deploy init --target cloudrun --ingress edge-fed --model-routing gateway`.","apiVersion: serving.knative.dev/v1","kind: Service","metadata:"," name: ${SERVICE}","spec:"," template:"," metadata:"," annotations:",' run.googleapis.com/cpu-throttling: "false"',' run.googleapis.com/startup-cpu-boost: "true"',` run.googleapis.com/container-dependencies: '{"caddy":["agent","auth-broker"]}'`,' autoscaling.knative.dev/maxScale: "1"',...t.gatewayChannels?.trim()?[' autoscaling.knative.dev/minScale: "1"']:[]," spec:"," timeoutSeconds: 3600"," containers:"," - name: caddy"," image: caddy:2.11-alpine"," ports:"," - name: http1",` containerPort: ${t.port}`," volumeMounts:"," - { name: caddy-config, mountPath: /etc/caddy, readOnly: true }",...Pr("1000m","512Mi")," - name: auth-broker"," image: ${IMAGE}",' args: ["auth-broker"]'," env:",' - { name: AGENTHIPPO_AUTH_PORT, value: "9780" }'," - { name: AGENTHIPPO_AUTH_DATA_DIR, value: /data }"," - { name: AGENTHIPPO_AUTH_PUBLIC_URL, value: https://${PUBLIC_HOSTNAME} }"," - { name: AGENTHIPPO_AUTH_ALLOWED_DOMAINS, value: ${ALLOWED_DOMAINS} }",` - { name: AGENTHIPPO_AUTH_COOKIE_DOMAIN, value: "${n}" }`,` - { name: AGENTHIPPO_AUTH_IDP_URL, value: "${r}" }`,' - { name: AGENTHIPPO_AUTH_COOKIE_NAME, value: "ah_token" }'," - name: AGENTHIPPO_AUTH_JWT_SECRET"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-auth-jwt-secret, key: latest }",...ys(9780),...Pr("1000m","512Mi")," - name: agent"," image: ${IMAGE}",` args: [${e}]`," env:"," - { name: AGENTHIPPO_HOME, value: /state }",' - { name: AGENTHIPPO_NO_AUTO_INSTALL, value: "1" }',El(),` - { name: AGENTHIPPO_LITELLM_BASE_URL, value: "${rw}" }`," - name: AGENTHIPPO_LITELLM_API_KEY"," valueFrom:"," secretKeyRef: { name: ${SERVICE}-provider-key, key: latest }"," volumeMounts:"," - { name: state, mountPath: /state }",...ys(gt.EDGE_AGENT_PORT,!0),...Pr("2000m","2Gi")," volumes:"," - name: state"," emptyDir:"," medium: Memory"," sizeLimit: 1Gi"," - name: caddy-config"," secret:"," secretName: ${SERVICE}-caddyfile"," items:"," - { key: latest, path: Caddyfile }",""].join(`
|
|
578
578
|
`)}function A0(t){let e=t.model?.trim()?`, model ${t.model.trim()}`:"",n=oa(t.model)?'PROVIDER_KEY="${PROVIDER_KEY:?set PROVIDER_KEY=<ahgw_\u2026 from create-gateway-key on gateway host>}"':'PROVIDER_KEY="${PROVIDER_KEY:?set PROVIDER_KEY=<gateway service key or provider key>}"';return["#!/usr/bin/env bash","# Cloud Run + AgentHippo federated edge (A\u2032) \u2014 gateway-direct (lean).",`# Engine: ${t.engine}${e}. Caddy + auth-broker + agent only \u2014 agent calls`,"# gateway.agenthippo.ai directly (no in-pod LiteLLM/OTEL sidecars). Same model routing","# as colocated agents on hippopennyg.","set -euo pipefail",...Wr(),`SERVICE="${t.packName}"`,'REGION="${REGION:-us-central1}"','PROJECT_ID="${PROJECT_ID:-$(gcloud config get-value project)}"','REPO="${ARTIFACT_REPO:-agenthippo}"','IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/${SERVICE}:latest"','PUBLIC_HOSTNAME="${PUBLIC_HOSTNAME:?set PUBLIC_HOSTNAME=<subdomain>.agenthippo.ai}"','ALLOWED_DOMAINS="${ALLOWED_DOMAINS:-agenthippo.ai}"','AUTH_JWT_SECRET="${AUTH_JWT_SECRET:?set AUTH_JWT_SECRET=<copy from auth.agenthippo.ai>}"',n,"",'gcloud artifacts repositories describe "$REPO" --location="$REGION" 2>/dev/null \\',' || gcloud artifacts repositories create "$REPO" --repository-format=docker --location="$REGION"','gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet',"",'DF_TMP="$(mktemp)"','sed "s|FROM ghcr.io/agenthippoai/runtime:|FROM ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/runtime:|" deploy/Dockerfile > "$DF_TMP"','docker build --pull --platform linux/amd64 -f "$DF_TMP" -t "$IMAGE" .','docker push "$IMAGE"',"",'ensure_secret() { gcloud secrets describe "$1" >/dev/null 2>&1 || gcloud secrets create "$1" --replication-policy=automatic; }','ensure_secret "${SERVICE}-caddyfile"; gcloud secrets versions add "${SERVICE}-caddyfile" --data-file=deploy/Caddyfile','ensure_secret "${SERVICE}-auth-jwt-secret"; printf %s "$AUTH_JWT_SECRET" | gcloud secrets versions add "${SERVICE}-auth-jwt-secret" --data-file=-','ensure_secret "${SERVICE}-provider-key"; printf %s "$PROVIDER_KEY" | gcloud secrets versions add "${SERVICE}-provider-key" --data-file=-',"",'RENDERED="$(mktemp)"','sed -e "s|\\${SERVICE}|${SERVICE}|g" -e "s|\\${IMAGE}|${IMAGE}|g" \\',' -e "s|\\${PUBLIC_HOSTNAME}|${PUBLIC_HOSTNAME}|g" -e "s|\\${ALLOWED_DOMAINS}|${ALLOWED_DOMAINS}|g" \\',' deploy/service.yaml > "$RENDERED"','gcloud run services replace "$RENDERED" --region "$REGION"',"",'gcloud run services add-iam-policy-binding "$SERVICE" --region "$REGION" \\'," --member=allUsers --role=roles/run.invoker >/dev/null 2>&1 || true","",'if [[ "$PUBLIC_HOSTNAME" != *.run.app ]]; then \\',' gcloud beta run domain-mappings describe "$PUBLIC_HOSTNAME" --region "$REGION" >/dev/null 2>&1 \\',' || gcloud beta run domain-mappings create --service "$SERVICE" --region "$REGION" --domain "$PUBLIC_HOSTNAME"; \\',"fi",""].join(`
|
|
579
579
|
`)}function S0(t){let e=t.engine.trim().toLowerCase()==="cursor",n=t.model?.trim()?`, model ${t.model.trim()}`:"",r=["#!/usr/bin/env bash","# Cloud Run + AgentHippo federated edge (A\u2032): Caddy + auth-broker front the agent.",`# Engine: ${t.engine}${n}. No oauth2-proxy, no per-service Google OAuth client \u2014`,"# Google login happens once at the central IdP (auth.agenthippo.ai); this agent verifies","# the shared .agenthippo.ai AgentHippo JWT (Bearer for IDE, cookie for browser). No IAP.","# The IDE points spec.remote.url straight at https://$PUBLIC_HOSTNAME (same flow as remote-vm).","set -euo pipefail",...Wr(),`SERVICE="${t.packName}"`,'REGION="${REGION:-us-central1}"','PROJECT_ID="${PROJECT_ID:-$(gcloud config get-value project)}"','REPO="${ARTIFACT_REPO:-agenthippo}"','IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/${SERVICE}:latest"',`LITELLM_IMAGE="\${LITELLM_IMAGE:-${ws("agenthippo-litellm")}}"`,"# Public hostname = a *.agenthippo.ai subdomain mapped to this service (one-time wildcard DNS).",'PUBLIC_HOSTNAME="${PUBLIC_HOSTNAME:?set PUBLIC_HOSTNAME=<subdomain>.agenthippo.ai}"','ALLOWED_DOMAINS="${ALLOWED_DOMAINS:-agenthippo.ai}"',"# Shared AgentHippo JWT signing key \u2014 MUST match the central IdP (auth.agenthippo.ai).","# Copy it from the IdP (or the GCP secret it stores) so tokens issued there verify here.",'AUTH_JWT_SECRET="${AUTH_JWT_SECRET:?set AUTH_JWT_SECRET=<copy from auth.agenthippo.ai>}"',...e?['PROVIDER_KEY="${PROVIDER_KEY:?set PROVIDER_KEY=<cursor api key>}"']:oa(t.model)?["# LiteLLM proxy key + AgentHippo gateway service key (ahgw_\u2026) for ah-auto routing.",'LITELLM_KEY="${LITELLM_KEY:?set LITELLM_KEY=<litellm proxy key>}"','PROVIDER_KEY="${PROVIDER_KEY:?set PROVIDER_KEY=<ahgw_\u2026 from create-gateway-key on gateway host>}"']:["# LiteLLM proxy key + the upstream provider key the gateway uses for the model.",'LITELLM_KEY="${LITELLM_KEY:?set LITELLM_KEY=<litellm proxy key>}"','PROVIDER_KEY="${PROVIDER_KEY:?set PROVIDER_KEY=<upstream provider key>}"'],"",'gcloud artifacts repositories describe "$REPO" --location="$REGION" 2>/dev/null \\',' || gcloud artifacts repositories create "$REPO" --repository-format=docker --location="$REGION"','gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet',"","# One agent image serves both the agent and the auth-broker containers (CLI ships `auth-broker`).","# Rewrite the runtime base to this project Artifact Registry. Customer artifacts pin a","# dated ghcr.io/agenthippoai/runtime tag; mirror that exact tag into AR. No-op if already AR.",'DF_TMP="$(mktemp)"','sed "s|FROM ghcr.io/agenthippoai/runtime:|FROM ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/runtime:|" deploy/Dockerfile > "$DF_TMP"','docker build --pull --platform linux/amd64 -f "$DF_TMP" -t "$IMAGE" .','docker push "$IMAGE"',"","# Secrets: caddyfile + shared JWT secret + provider/litellm keys. NO OAuth client secrets.",'ensure_secret() { gcloud secrets describe "$1" >/dev/null 2>&1 || gcloud secrets create "$1" --replication-policy=automatic; }','ensure_secret "${SERVICE}-caddyfile"; gcloud secrets versions add "${SERVICE}-caddyfile" --data-file=deploy/Caddyfile','ensure_secret "${SERVICE}-auth-jwt-secret"; printf %s "$AUTH_JWT_SECRET" | gcloud secrets versions add "${SERVICE}-auth-jwt-secret" --data-file=-','ensure_secret "${SERVICE}-provider-key"; printf %s "$PROVIDER_KEY" | gcloud secrets versions add "${SERVICE}-provider-key" --data-file=-'];return e||r.push('ensure_secret "${SERVICE}-otel-config"; gcloud secrets versions add "${SERVICE}-otel-config" --data-file=deploy/otel-collector.yaml','ensure_secret "${SERVICE}-litellm-key"; printf %s "$LITELLM_KEY" | gcloud secrets versions add "${SERVICE}-litellm-key" --data-file=-'),r.push("","# Render the multi-container template (sed \u2014 no gettext) and deploy.",'RENDERED="$(mktemp)"','sed -e "s|\\${SERVICE}|${SERVICE}|g" -e "s|\\${IMAGE}|${IMAGE}|g" \\',' -e "s|\\${LITELLM_IMAGE}|${LITELLM_IMAGE}|g" \\',' -e "s|\\${PUBLIC_HOSTNAME}|${PUBLIC_HOSTNAME}|g" -e "s|\\${ALLOWED_DOMAINS}|${ALLOWED_DOMAINS}|g" \\',' deploy/service.yaml > "$RENDERED"','gcloud run services replace "$RENDERED" --region "$REGION"',"","# No IAP: Caddy is the gate, so Cloud Run must allow public ingress (allUsers invoker).",'gcloud run services add-iam-policy-binding "$SERVICE" --region "$REGION" \\'," --member=allUsers --role=roles/run.invoker >/dev/null 2>&1 || true","","# Map the *.agenthippo.ai subdomain (idempotent). Requires one-time wildcard DNS","# *.agenthippo.ai \u2192 Cloud Run (gctrl.run.app). No per-subdomain DNS, no Google OAuth edit.","# Skip for *.run.app default URLs (no custom mapping needed there).",'if [[ "$PUBLIC_HOSTNAME" != *.run.app ]]; then \\',' gcloud beta run domain-mappings describe "$PUBLIC_HOSTNAME" --region "$REGION" >/dev/null 2>&1 \\',' || gcloud beta run domain-mappings create --service "$SERVICE" --region "$REGION" --domain "$PUBLIC_HOSTNAME"; \\',"fi","","# Grant the runtime SA access to the secrets if needed:",`# SA="$(gcloud run services describe "$SERVICE" --region "$REGION" --format='value(spec.template.spec.serviceAccountName)')"`,"# for s in caddyfile auth-jwt-secret provider-key; do",'# gcloud secrets add-iam-policy-binding "${SERVICE}-$s" --member="serviceAccount:$SA" --role=roles/secretmanager.secretAccessor',"# done",""),r.join(`
|
|
580
580
|
`)}function T0(t,e,n){let r=Bf(t)&&!n,s=n?"Caddy (ingress) + auth-broker + agent.":r?"Caddy (ingress) + auth-broker + agent (gateway-direct \u2014 no LiteLLM/OTEL sidecars).":"Caddy (ingress) + auth-broker + agent + LiteLLM + OTEL collector (Spotlight).",i=(t.idpUrl||"https://auth.agenthippo.ai").trim().replace(/\/+$/,""),o={Dockerfile:Un(t,{wwwDir:"/app/pack/deploy/www"}),"www/index.html":As(t.packName),Caddyfile:(0,gt.renderSidecarFedCaddyfile)(t.idpUrl),"service.yaml":r?w0(t):y0(t),"cloudrun-deploy.sh":r?A0(t):S0(t)};return!n&&!r&&(o["otel-collector.yaml"]=_l),o["README.md"]=[`# Deploy: ${t.packName} \u2014 Cloud Run + AgentHippo federated edge (A\u2032${r?", gateway-direct":""})`,"",`Engine: **${t.engine}**${t.model?`, model: **${t.model}**`:""}. Runtime base: \`${t.image}\`.`,"",`${s} Sidecars share the pod's localhost (Cloud Run multi-container):`,"- **caddy** (ingress, the only port) \u2192 verifies the shared AgentHippo JWT (Bearer for IDE, cookie for browser)",`- **agent** \u2192 serves www + /v1/* on localhost:${gt.EDGE_AGENT_PORT} (NOT ingress)`,"- **auth-broker** \u2192 validates the shared `.agenthippo.ai` JWT via `/api/ide/me` (same secret as the IdP)",...r?[`- **model routing** \u2192 agent calls \`${rw}\` directly (ahgw_\u2026 service key) \u2014 same as colocated agents`,"- **no Spotlight sidecars** \u2014 traces are not collected in-pod; use gateway/Spotlight fleet analytics instead"]:n?[]:["- **litellm** gateway \u2192 exports OTLP to `http://localhost:4318`","- **otel-collector** \u2192 writes JSONL traces to the in-memory `/state` volume"],"","**No oauth2-proxy, no per-service Google OAuth client.** Google login happens once at the",`central IdP (${i}); this agent only verifies the resulting shared \`.agenthippo.ai\` JWT.`,"The IDE points `spec.remote.url` at `https://$PUBLIC_HOSTNAME` and the existing AgentHippo","SSO flow works (same as `remote-vm`). Browser visits with no cookie are redirected to the","IdP to log in, then return. Cloud Run is public (`allUsers` invoker) so traffic reaches Caddy.","","```bash","cp deploy/.env.example deploy/.env && cp deploy/.env.auth.example deploy/.env.auth # fill both","export PUBLIC_HOSTNAME=<subdomain>.agenthippo.ai # e.g. cs.agenthippo.ai","export AUTH_JWT_SECRET=<copy from auth.agenthippo.ai> # shared signing key",...n?["export PROVIDER_KEY=<cursor api key>"]:r?["export PROVIDER_KEY=<ahgw_\u2026 from create-gateway-key on gateway host>"]:oa(t.model)?["export LITELLM_KEY=\u2026 PROVIDER_KEY=<ahgw_\u2026 from create-gateway-key on gateway host>"]:["export LITELLM_KEY=\u2026 PROVIDER_KEY=\u2026"],"bash deploy/cloudrun-deploy.sh # builds linux/amd64, upserts secrets, `gcloud run services replace`, maps the subdomain","```","","**One-time setup (not per agent):** wildcard DNS `*.agenthippo.ai` \u2192 Cloud Run (`gctrl.run.app`),",`and the IdP (${i}) must run the \`/oauth/browser-login\` route (oauth2-proxy forward_auth \u2192`,"auth-broker sets the `ah_token` cookie on `.agenthippo.ai`). After that, every new agent is",r?"just: `deploy init --ingress edge-fed --model-routing gateway` + this script \u2014 zero Console/OAuth work.":"just: `deploy init --ingress edge-fed` + this script \u2014 zero Console/OAuth work.","",r?"`service.yaml` placeholders (`${SERVICE} ${IMAGE} ${PUBLIC_HOSTNAME} ${ALLOWED_DOMAINS}`) are substituted by the deploy script. The Caddyfile is stored in Secret Manager (`${SERVICE}-caddyfile`).":"`service.yaml` placeholders (`${SERVICE} ${IMAGE} ${LITELLM_IMAGE} ${PUBLIC_HOSTNAME} ${ALLOWED_DOMAINS}`) are substituted by the deploy script. The Caddyfile is stored in Secret Manager (`${SERVICE}-caddyfile`).","`AGENTHIPPO_AUTH_COOKIE_DOMAIN` / `AGENTHIPPO_AUTH_IDP_URL` are baked in at generation time.","",e?"**Inbound channel (1b):** `minScale: 1` pins one always-on instance (the socket can't scale to zero).":"**No inbound channel (1a):** scales to zero; in-memory `/state` is wiped on cold start (use the `edge` mode + GCS bucket if you need durable state).","",...tr(t)].join(`
|
|
@@ -590,17 +590,17 @@ ${n}`}function Ti(t,e){let n=["serve","--agent","/app/pack","--workspace","/stat
|
|
|
590
590
|
`)}}function I0(t){let e=t.ingress==="edge-fed",n=t.ingress==="edge"||e,r=bi(t.model);if(!n){let c={family:"${SERVICE}",networkMode:"awsvpc",requiresCompatibilities:["FARGATE"],cpu:"512",memory:"1024",containerDefinitions:[{name:"agent",image:"${IMAGE}",essential:!0,portMappings:[{containerPort:t.port,protocol:"tcp"}],environment:[{name:"AGENTHIPPO_NO_AUTO_INSTALL",value:"1"}],secrets:[{name:r,valueFrom:"${PROVIDER_KEY_SECRET_ARN}"}],logConfiguration:{logDriver:"awslogs",options:{"awslogs-group":"/ecs/${SERVICE}","awslogs-region":"${REGION}","awslogs-stream-prefix":"agent"}}}]},l=["#!/usr/bin/env bash","# T1-generic: AWS ECS Fargate, single container \u2014 bring your own ingress (ALB/API","# Gateway/internal VPC). For a self-contained ALB + AgentHippo edge, use --ingress edge","# or --ingress edge-fed instead.","set -euo pipefail",...Wr(),'SERVICE="'+t.packName+'"','REGION="${AWS_REGION:-us-east-1}"','ACCOUNT_ID="${ACCOUNT_ID:-$(aws sts get-caller-identity --query Account --output text)}"','REPO="agenthippo/${SERVICE}"','IMAGE="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${REPO}:latest"',"",'aws ecr describe-repositories --repository-names "$REPO" --region "$REGION" >/dev/null 2>&1 \\',' || aws ecr create-repository --repository-name "$REPO" --region "$REGION"','aws ecr get-login-password --region "$REGION" \\',' | docker login --username AWS --password-stdin "${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com"',"",'docker build --platform linux/amd64 -f deploy/Dockerfile -t "$IMAGE" .','docker push "$IMAGE"',"",`PROVIDER_KEY_SECRET_ARN="\${PROVIDER_KEY_SECRET_ARN:?set PROVIDER_KEY_SECRET_ARN \u2014 Secrets Manager ARN holding ${r}}"`,'TASK_JSON="$(sed -e "s|\\${IMAGE}|${IMAGE}|g" -e "s|\\${SERVICE}|${SERVICE}|g" \\',' -e "s|\\${REGION}|${REGION}|g" -e "s|\\${PROVIDER_KEY_SECRET_ARN}|${PROVIDER_KEY_SECRET_ARN}|g" \\',' deploy/task-definition.json)"','aws ecs register-task-definition --region "$REGION" --cli-input-json "$TASK_JSON" >/dev/null',"",'CLUSTER="${CLUSTER:?set CLUSTER \u2014 the ECS cluster to run in}"','aws ecs describe-services --cluster "$CLUSTER" --services "$SERVICE" --region "$REGION" 2>/dev/null \\',` | grep -q '"status": "ACTIVE"' \\`,' && aws ecs update-service --cluster "$CLUSTER" --service "$SERVICE" \\',' --task-definition "$SERVICE" --region "$REGION" >/dev/null \\',' || aws ecs create-service --cluster "$CLUSTER" --service-name "$SERVICE" \\',' --task-definition "$SERVICE" --desired-count 1 --launch-type FARGATE \\',' --network-configuration "awsvpcConfiguration={subnets=[${SUBNETS:?set SUBNETS}],securityGroups=[${SECURITY_GROUPS:?set SECURITY_GROUPS}],assignPublicIp=ENABLED}" \\',' --region "$REGION" >/dev/null','echo "Deployed $SERVICE to ECS cluster $CLUSTER"',""].join(`
|
|
591
591
|
`);return{Dockerfile:Un(t),"task-definition.json":JSON.stringify(c,null,2)+`
|
|
592
592
|
`,"deploy.sh":l,"README.md":[`# Deploy: ${t.packName} \u2014 AWS ECS Fargate (T1-generic)`,"",`Engine: **${t.engine}**${t.model?`, model: **${t.model}**`:""}.`,"","Single container, no ingress opinion \u2014 front it with whatever your org already runs","(ALB, API Gateway, internal VPC access). Use `--ingress edge` or `--ingress edge-fed`","for a self-contained ALB + AgentHippo edge instead.","","```bash","export CLUSTER=<ecs-cluster> SUBNETS=<subnet-ids> SECURITY_GROUPS=<sg-ids>","export PROVIDER_KEY_SECRET_ARN=<Secrets Manager ARN for your model provider key>","bash deploy/deploy.sh","```","","> \u26A0\uFE0F Newer and far less exercised than the Cloud Run target \u2014 read `deploy.sh` before","> running it against a real account.","",...tr(t)].join(`
|
|
593
|
-
`)}}let s=[{name:"caddy",image:"caddy:2.
|
|
593
|
+
`)}}let s=[{name:"caddy",image:"caddy:2.11-alpine",essential:!0,portMappings:[{containerPort:8080,protocol:"tcp"}],environment:[{name:"AGENTHIPPO_PUBLIC_HOSTNAME",value:t.publicHostname}],mountPoints:[{sourceVolume:"caddy-config",containerPath:"/etc/caddy"}],logConfiguration:{logDriver:"awslogs",options:{"awslogs-group":"/ecs/${SERVICE}","awslogs-region":"${REGION}","awslogs-stream-prefix":"caddy"}}},...e?[]:[{name:"oauth2-proxy",image:"quay.io/oauth2-proxy/oauth2-proxy:v7.7.1",essential:!0,environment:[{name:"OAUTH2_PROXY_HTTP_ADDRESS",value:"0.0.0.0:4180"},{name:"OAUTH2_PROXY_UPSTREAMS",value:"file:///dev/null"},{name:"OAUTH2_PROXY_SET_XAUTHREQUEST",value:"true"},{name:"OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER",value:"true"},{name:"OAUTH2_PROXY_SKIP_PROVIDER_BUTTON",value:"true"},{name:"OAUTH2_PROXY_REVERSE_PROXY",value:"true"}],secrets:[{name:"OAUTH2_PROXY_CLIENT_ID",valueFrom:"${OAUTH_CLIENT_ID_SECRET_ARN}"},{name:"OAUTH2_PROXY_CLIENT_SECRET",valueFrom:"${OAUTH_CLIENT_SECRET_SECRET_ARN}"},{name:"OAUTH2_PROXY_COOKIE_SECRET",valueFrom:"${OAUTH_COOKIE_SECRET_SECRET_ARN}"}],logConfiguration:{logDriver:"awslogs",options:{"awslogs-group":"/ecs/${SERVICE}","awslogs-region":"${REGION}","awslogs-stream-prefix":"oauth2-proxy"}}}],{name:"auth-broker",image:"${IMAGE}",essential:!0,command:["auth-broker"],environment:[{name:"AGENTHIPPO_AUTH_PORT",value:"9780"},...e?[{name:"AGENTHIPPO_AUTH_COOKIE_DOMAIN",value:t.cookieDomain||""},{name:"AGENTHIPPO_AUTH_IDP_URL",value:t.idpUrl||""}]:[]],secrets:[{name:"AGENTHIPPO_AUTH_JWT_SECRET",valueFrom:"${AUTH_JWT_SECRET_SECRET_ARN}"}],logConfiguration:{logDriver:"awslogs",options:{"awslogs-group":"/ecs/${SERVICE}","awslogs-region":"${REGION}","awslogs-stream-prefix":"auth-broker"}}},{name:"agent",image:"${IMAGE}",essential:!0,command:Ti(t,{wwwDir:"/app/pack/deploy/www",port:gt.EDGE_AGENT_PORT}),environment:[{name:"AGENTHIPPO_NO_AUTO_INSTALL",value:"1"}],secrets:[{name:r,valueFrom:"${PROVIDER_KEY_SECRET_ARN}"}],logConfiguration:{logDriver:"awslogs",options:{"awslogs-group":"/ecs/${SERVICE}","awslogs-region":"${REGION}","awslogs-stream-prefix":"agent"}}}],i={family:"${SERVICE}",networkMode:"awsvpc",requiresCompatibilities:["FARGATE"],cpu:"1024",memory:"2048",volumes:[{name:"caddy-config"}],containerDefinitions:s},o=e?"PROVIDER_KEY_SECRET_ARN=<...> AUTH_JWT_SECRET_SECRET_ARN=<...>":"PROVIDER_KEY_SECRET_ARN=<...> AUTH_JWT_SECRET_SECRET_ARN=<...> OAUTH_CLIENT_ID_SECRET_ARN=<...> OAUTH_CLIENT_SECRET_SECRET_ARN=<...> OAUTH_COOKIE_SECRET_SECRET_ARN=<...>",a=["#!/usr/bin/env bash",`# AWS ECS Fargate + AgentHippo edge (${e?"edge-fed: federated identity, no oauth2-proxy":"edge: self-issued"}).`,"# One-time setup (not per deploy): create an ALB + target group (protocol HTTP, port 8080,","# target type ip) pointed at this service, and an HTTPS listener forwarding to it \u2014 ALB","# terminates TLS, same as Cloud Run's edge path (Caddy listens on plain HTTP behind it).","set -euo pipefail",...Wr(),'SERVICE="'+t.packName+'"','REGION="${AWS_REGION:-us-east-1}"','ACCOUNT_ID="${ACCOUNT_ID:-$(aws sts get-caller-identity --query Account --output text)}"','REPO="agenthippo/${SERVICE}"','IMAGE="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${REPO}:latest"',"",'aws ecr describe-repositories --repository-names "$REPO" --region "$REGION" >/dev/null 2>&1 \\',' || aws ecr create-repository --repository-name "$REPO" --region "$REGION"','aws ecr get-login-password --region "$REGION" \\',' | docker login --username AWS --password-stdin "${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com"',"","# auth-broker reuses the agent image (CLI ships `auth-broker`).",'docker build --platform linux/amd64 -f deploy/Dockerfile -t "$IMAGE" .','docker push "$IMAGE"',"",'PROVIDER_KEY_SECRET_ARN="${PROVIDER_KEY_SECRET_ARN:?set PROVIDER_KEY_SECRET_ARN}"','AUTH_JWT_SECRET_SECRET_ARN="${AUTH_JWT_SECRET_SECRET_ARN:?set AUTH_JWT_SECRET_SECRET_ARN}"',...e?[]:['OAUTH_CLIENT_ID_SECRET_ARN="${OAUTH_CLIENT_ID_SECRET_ARN:?set OAUTH_CLIENT_ID_SECRET_ARN}"','OAUTH_CLIENT_SECRET_SECRET_ARN="${OAUTH_CLIENT_SECRET_SECRET_ARN:?set OAUTH_CLIENT_SECRET_SECRET_ARN}"','OAUTH_COOKIE_SECRET_SECRET_ARN="${OAUTH_COOKIE_SECRET_SECRET_ARN:?set OAUTH_COOKIE_SECRET_SECRET_ARN}"'],'TASK_JSON="$(sed -e "s|\\${IMAGE}|${IMAGE}|g" -e "s|\\${SERVICE}|${SERVICE}|g" -e "s|\\${REGION}|${REGION}|g" \\',' -e "s|\\${PROVIDER_KEY_SECRET_ARN}|${PROVIDER_KEY_SECRET_ARN}|g" \\',' -e "s|\\${AUTH_JWT_SECRET_SECRET_ARN}|${AUTH_JWT_SECRET_SECRET_ARN}|g" \\',...e?[]:[' -e "s|\\${OAUTH_CLIENT_ID_SECRET_ARN}|${OAUTH_CLIENT_ID_SECRET_ARN}|g" \\',' -e "s|\\${OAUTH_CLIENT_SECRET_SECRET_ARN}|${OAUTH_CLIENT_SECRET_SECRET_ARN}|g" \\',' -e "s|\\${OAUTH_COOKIE_SECRET_SECRET_ARN}|${OAUTH_COOKIE_SECRET_SECRET_ARN}|g" \\'],' deploy/task-definition.json)"','aws ecs register-task-definition --region "$REGION" --cli-input-json "$TASK_JSON" >/dev/null',"",'CLUSTER="${CLUSTER:?set CLUSTER}"','TARGET_GROUP_ARN="${TARGET_GROUP_ARN:?set TARGET_GROUP_ARN \u2014 from the one-time ALB setup above}"','aws ecs describe-services --cluster "$CLUSTER" --services "$SERVICE" --region "$REGION" 2>/dev/null \\',` | grep -q '"status": "ACTIVE"' \\`,' && aws ecs update-service --cluster "$CLUSTER" --service "$SERVICE" \\',' --task-definition "$SERVICE" --region "$REGION" >/dev/null \\',' || aws ecs create-service --cluster "$CLUSTER" --service-name "$SERVICE" \\',' --task-definition "$SERVICE" --desired-count 1 --launch-type FARGATE \\',' --load-balancers "targetGroupArn=$TARGET_GROUP_ARN,containerName=caddy,containerPort=8080" \\',' --network-configuration "awsvpcConfiguration={subnets=[${SUBNETS:?set SUBNETS}],securityGroups=[${SECURITY_GROUPS:?set SECURITY_GROUPS}],assignPublicIp=ENABLED}" \\',' --region "$REGION" >/dev/null','echo "Deployed $SERVICE behind the AgentHippo edge on ECS cluster $CLUSTER"',""].join(`
|
|
594
594
|
`);return{Dockerfile:Un(t,{wwwDir:"/app/pack/deploy/www"}),"www/index.html":As(t.packName),Caddyfile:e?(0,gt.renderSidecarFedCaddyfile)(t.idpUrl):(0,gt.renderSidecarEdgeCaddyfile)(),"task-definition.json":JSON.stringify(i,null,2)+`
|
|
595
595
|
`,"deploy.sh":a,"README.md":[`# Deploy: ${t.packName} \u2014 AWS ECS Fargate + AgentHippo edge (${e?"edge-fed":"edge"})`,"",`Engine: **${t.engine}**${t.model?`, model: **${t.model}**`:""}.`,"","One Fargate task, containers share the task's localhost (`awsvpc` network mode) \u2014","the same sidecar shape Cloud Run uses, so the Caddyfile is identical:","- **caddy** (ingress, the only published port) \u2192 "+(e?"verifies the shared AgentHippo JWT":"SSO edge: oauth2-proxy for browsers, auth-broker for IDE Bearer"),`- **agent** \u2192 serves www + /v1/* on localhost:${gt.EDGE_AGENT_PORT} (not published)`,"- **auth-broker** \u2192 validates IDE AgentHippo JWTs (`/api/ide/me`)"+(e?" against the shared IdP secret":""),...e?[]:["- **oauth2-proxy** \u2192 Google SSO for browser clients"],"","**One-time setup (not per deploy):** an ALB + HTTPS listener + target group (HTTP, port","8080, target type `ip`) pointed at this service \u2014 ALB terminates TLS, Caddy stays HTTP.","","```bash","export CLUSTER=<ecs-cluster> SUBNETS=<subnet-ids> SECURITY_GROUPS=<sg-ids> TARGET_GROUP_ARN=<from ALB setup>",`export ${o}`,"bash deploy/deploy.sh","```","","> \u26A0\uFE0F Newer and far less exercised than the Cloud Run target \u2014 read `deploy.sh` before","> running it against a real account.","",...tr(t)].join(`
|
|
596
596
|
`)}}function P0(t){let e=t.ingress==="edge-fed",n=t.ingress==="edge"||e,r=bi(t.model),s=8080;if(!n){let l={properties:{configuration:{ingress:{external:!0,targetPort:t.port},secrets:[{name:"provider-key",value:"${PROVIDER_KEY}"}]},template:{containers:[{name:"agent",image:"${IMAGE}",env:[{name:"AGENTHIPPO_NO_AUTO_INSTALL",value:"1"},{name:r,secretRef:"provider-key"}],resources:{cpu:.5,memory:"1Gi"}}]}}},d=["#!/usr/bin/env bash","# T1-generic: Azure Container Apps, single container \u2014 built-in ingress + TLS, no separate","# load balancer to stand up. For a self-contained AgentHippo edge, use --ingress edge or","# --ingress edge-fed instead.","set -euo pipefail",...Wr(),'SERVICE="'+t.packName+'"','RESOURCE_GROUP="${RESOURCE_GROUP:?set RESOURCE_GROUP}"','ENVIRONMENT="${CONTAINERAPPS_ENVIRONMENT:?set CONTAINERAPPS_ENVIRONMENT \u2014 az containerapp env create (one-time)}"','ACR_NAME="${ACR_NAME:?set ACR_NAME \u2014 an Azure Container Registry (one-time: az acr create)}"','IMAGE="${ACR_NAME}.azurecr.io/${SERVICE}:latest"',`PROVIDER_KEY="\${PROVIDER_KEY:?set PROVIDER_KEY \u2014 your ${r}}"`,"","# Builds in ACR (no local docker login/push step needed).",'az acr build --registry "$ACR_NAME" --image "${SERVICE}:latest" --file deploy/Dockerfile .',"",'az containerapp show --name "$SERVICE" --resource-group "$RESOURCE_GROUP" >/dev/null 2>&1 \\',' && az containerapp update --name "$SERVICE" --resource-group "$RESOURCE_GROUP" \\',' --image "$IMAGE" >/dev/null \\',' || az containerapp create --name "$SERVICE" --resource-group "$RESOURCE_GROUP" \\',' --environment "$ENVIRONMENT" --image "$IMAGE" \\'," --target-port "+t.port+" --ingress external \\",' --secrets "provider-key=$PROVIDER_KEY" \\',` --env-vars "AGENTHIPPO_NO_AUTO_INSTALL=1" "${r}=secretref:provider-key" \\`," >/dev/null",'echo "Deployed $SERVICE to Container Apps environment $ENVIRONMENT"',""].join(`
|
|
597
597
|
`);return{Dockerfile:Un(t),"containerapp.json":JSON.stringify(l,null,2)+`
|
|
598
598
|
`,"deploy.sh":d,"README.md":[`# Deploy: ${t.packName} \u2014 Azure Container Apps (T1-generic)`,"",`Engine: **${t.engine}**${t.model?`, model: **${t.model}**`:""}.`,"","Single container, built-in ingress + TLS \u2014 no separate load balancer to stand up.","Use `--ingress edge` or `--ingress edge-fed` for a self-contained AgentHippo edge instead.","","```bash","export RESOURCE_GROUP=<rg> CONTAINERAPPS_ENVIRONMENT=<env> ACR_NAME=<registry>",`export PROVIDER_KEY=<your ${r}>`,"bash deploy/deploy.sh","```","","> \u26A0\uFE0F Newer and far less exercised than the Cloud Run target \u2014 read `deploy.sh` before","> running it against a real account.","",...tr(t)].join(`
|
|
599
|
-
`)}}let i=[{name:"caddy",image:"caddy:2.
|
|
599
|
+
`)}}let i=[{name:"caddy",image:"caddy:2.11-alpine",env:[{name:"PORT",value:String(s)},{name:"AGENTHIPPO_PUBLIC_HOSTNAME",value:t.publicHostname}],resources:{cpu:.25,memory:"0.5Gi"}},...e?[]:[{name:"oauth2-proxy",image:"quay.io/oauth2-proxy/oauth2-proxy:v7.7.1",env:[{name:"OAUTH2_PROXY_HTTP_ADDRESS",value:"0.0.0.0:4180"},{name:"OAUTH2_PROXY_UPSTREAMS",value:"file:///dev/null"},{name:"OAUTH2_PROXY_SET_XAUTHREQUEST",value:"true"},{name:"OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER",value:"true"},{name:"OAUTH2_PROXY_SKIP_PROVIDER_BUTTON",value:"true"},{name:"OAUTH2_PROXY_REVERSE_PROXY",value:"true"},{name:"OAUTH2_PROXY_CLIENT_ID",secretRef:"oauth-client-id"},{name:"OAUTH2_PROXY_CLIENT_SECRET",secretRef:"oauth-client-secret"},{name:"OAUTH2_PROXY_COOKIE_SECRET",secretRef:"oauth-cookie-secret"}],resources:{cpu:.25,memory:"0.5Gi"}}],{name:"auth-broker",image:"${IMAGE}",command:["agenthippo","auth-broker"],env:[{name:"AGENTHIPPO_AUTH_PORT",value:"9780"},{name:"AGENTHIPPO_AUTH_JWT_SECRET",secretRef:"auth-jwt-secret"},...e?[{name:"AGENTHIPPO_AUTH_COOKIE_DOMAIN",value:t.cookieDomain||""},{name:"AGENTHIPPO_AUTH_IDP_URL",value:t.idpUrl||""}]:[]],resources:{cpu:.25,memory:"0.5Gi"}},{name:"agent",image:"${IMAGE}",command:Ti(t,{wwwDir:"/app/pack/deploy/www",port:gt.EDGE_AGENT_PORT}),env:[{name:"AGENTHIPPO_NO_AUTO_INSTALL",value:"1"},{name:r,secretRef:"provider-key"}],resources:{cpu:.5,memory:"1Gi"}}],o={properties:{configuration:{ingress:{external:!0,targetPort:s},secrets:[{name:"auth-jwt-secret",value:"${AUTH_JWT_SECRET}"},{name:"provider-key",value:"${PROVIDER_KEY}"},...e?[]:[{name:"oauth-client-id",value:"${OAUTH_CLIENT_ID}"},{name:"oauth-client-secret",value:"${OAUTH_CLIENT_SECRET}"},{name:"oauth-cookie-secret",value:"${OAUTH_COOKIE_SECRET}"}]]},template:{containers:i}}},a=['AUTH_JWT_SECRET="${AUTH_JWT_SECRET:?set AUTH_JWT_SECRET}"',`PROVIDER_KEY="\${PROVIDER_KEY:?set PROVIDER_KEY \u2014 your ${r}}"`,...e?[]:['OAUTH_CLIENT_ID="${OAUTH_CLIENT_ID:?set OAUTH_CLIENT_ID}"','OAUTH_CLIENT_SECRET="${OAUTH_CLIENT_SECRET:?set OAUTH_CLIENT_SECRET}"','OAUTH_COOKIE_SECRET="${OAUTH_COOKIE_SECRET:?set OAUTH_COOKIE_SECRET}"']],c=["#!/usr/bin/env bash",`# Azure Container Apps + AgentHippo edge (${e?"edge-fed: federated identity, no oauth2-proxy":"edge: self-issued"}).`,"# Container Apps environments provide built-in ingress + TLS \u2014 no separate load balancer.","set -euo pipefail",...Wr(),'SERVICE="'+t.packName+'"','RESOURCE_GROUP="${RESOURCE_GROUP:?set RESOURCE_GROUP}"','ENVIRONMENT="${CONTAINERAPPS_ENVIRONMENT:?set CONTAINERAPPS_ENVIRONMENT \u2014 az containerapp env create (one-time)}"','ACR_NAME="${ACR_NAME:?set ACR_NAME \u2014 an Azure Container Registry (one-time: az acr create)}"','IMAGE="${ACR_NAME}.azurecr.io/${SERVICE}:latest"',"","# auth-broker reuses the agent image (CLI ships `auth-broker`). Builds in ACR.",'az acr build --registry "$ACR_NAME" --image "${SERVICE}:latest" --file deploy/Dockerfile .',"",...a,'SPEC_JSON="$(sed -e "s|\\${IMAGE}|${IMAGE}|g" -e "s|\\${AUTH_JWT_SECRET}|${AUTH_JWT_SECRET}|g" \\',' -e "s|\\${PROVIDER_KEY}|${PROVIDER_KEY}|g" \\',...e?[]:[' -e "s|\\${OAUTH_CLIENT_ID}|${OAUTH_CLIENT_ID}|g" -e "s|\\${OAUTH_CLIENT_SECRET}|${OAUTH_CLIENT_SECRET}|g" \\',' -e "s|\\${OAUTH_COOKIE_SECRET}|${OAUTH_COOKIE_SECRET}|g" \\'],' deploy/containerapp.json)"','echo "$SPEC_JSON" > /tmp/${SERVICE}-containerapp.json',"","# `--yaml` also accepts JSON (JSON is valid YAML) \u2014 kept as .json so it's reviewable","# alongside the other checked-in templates without a format switch.",'az containerapp show --name "$SERVICE" --resource-group "$RESOURCE_GROUP" >/dev/null 2>&1 \\',' && az containerapp update --name "$SERVICE" --resource-group "$RESOURCE_GROUP" \\'," --yaml /tmp/${SERVICE}-containerapp.json >/dev/null \\",' || az containerapp create --name "$SERVICE" --resource-group "$RESOURCE_GROUP" \\',' --environment "$ENVIRONMENT" --yaml /tmp/${SERVICE}-containerapp.json >/dev/null','echo "Deployed $SERVICE behind the AgentHippo edge in $ENVIRONMENT"',""].join(`
|
|
600
600
|
`);return{Dockerfile:Un(t,{wwwDir:"/app/pack/deploy/www"}),"www/index.html":As(t.packName),Caddyfile:e?(0,gt.renderSidecarFedCaddyfile)(t.idpUrl):(0,gt.renderSidecarEdgeCaddyfile)(),"containerapp.json":JSON.stringify(o,null,2)+`
|
|
601
601
|
`,"deploy.sh":c,"README.md":[`# Deploy: ${t.packName} \u2014 Azure Container Apps + AgentHippo edge (${e?"edge-fed":"edge"})`,"",`Engine: **${t.engine}**${t.model?`, model: **${t.model}**`:""}.`,"","One Container App, containers share the replica's localhost \u2014 the same sidecar shape","Cloud Run/ECS Fargate use, so the Caddyfile is identical:","- **caddy** (ingress, the only published port) \u2192 "+(e?"verifies the shared AgentHippo JWT":"SSO edge: oauth2-proxy for browsers, auth-broker for IDE Bearer"),`- **agent** \u2192 serves www + /v1/* on localhost:${gt.EDGE_AGENT_PORT} (not published)`,"- **auth-broker** \u2192 validates IDE AgentHippo JWTs (`/api/ide/me`)"+(e?" against the shared IdP secret":""),...e?[]:["- **oauth2-proxy** \u2192 Google SSO for browser clients"],"","Built-in ingress + TLS termination \u2014 no separate load balancer to stand up.","","```bash","export RESOURCE_GROUP=<rg> CONTAINERAPPS_ENVIRONMENT=<env> ACR_NAME=<registry>",...a.map(l=>"export "+l.split("=")[0]+"=<...>"),"bash deploy/deploy.sh","```","","> \u26A0\uFE0F Newer and far less exercised than the Cloud Run target \u2014 read `deploy.sh` before","> running it against a real account.","",...tr(t)].join(`
|
|
602
602
|
`)}}var N0={vm:u0,compose:d0,cloudrun:E0,"databricks-app":b0,spcs:v0,cloudflare:O0,"aws-ecs":I0,"azure-container-apps":P0};function R0(t,e){let n=N0[t](e),r=e.ingress==="edge-fed",s=!!e.colocate&&r&&(t==="vm"||t==="compose"),i=Bf(e)&&t==="cloudrun"&&r,o=!s&&(t==="vm"||t==="compose"||(t==="cloudrun"||t==="aws-ecs"||t==="azure-container-apps")&&(e.ingress==="edge"||r)),a=Object.keys(n).some(d=>d==="Dockerfile"||d.endsWith("/Dockerfile")),c={...n,...a?{".dockerignore":c0()}:{},".env.example":Ky(e.envVars,{edge:o,engine:e.engine,hostname:e.publicHostname,gatewayDirect:i}),...o?{".env.auth.example":qy({hostname:e.publicHostname,fed:r,idpUrl:e.idpUrl,cookieDomain:e.cookieDomain})}:{},"DATA-ACCESS.md":f0(e,t,o,r,s)},l={};for(let[d,u]of Object.entries(c))l[d]=Wy(e,d,u);return l}});var ow=T(Kr=>{"use strict";Object.defineProperty(Kr,"__esModule",{value:!0});Kr.BLUEPRINTS=Kr.BLUEPRINT_IDS=void 0;Kr.isBlueprintId=C0;Kr.resolveBlueprint=k0;Kr.BLUEPRINT_IDS=["sandbox","prospect-demo","team-pilot","regulated-air-gapped","enterprise-fleet"];function C0(t){return Kr.BLUEPRINT_IDS.includes(t)}Kr.BLUEPRINTS={sandbox:{id:"sandbox",description:"Internal test, no external users \u2014 no edge, no SSO, no host port beyond localhost.",targets:["vm"]},"prospect-demo":{id:"prospect-demo",description:"Throwaway public demo for a prospect \u2014 Cloudflare Tunnel + a throwaway Google OAuth client.",targets:["vm","compose"]},"team-pilot":{id:"team-pilot",description:"Single-agent pilot. Rides the customer platform's own identity when one exists (GCP IAP, Databricks workspace SSO, Snowflake network, Cloudflare Access); falls back to a self-issued edge (own Caddy + oauth2-proxy + Google client) on vm/compose/aws-ecs/azure-container-apps, which have no platform-native option yet.",targets:["cloudrun","databricks-app","spcs","cloudflare","vm","compose","aws-ecs","azure-container-apps"]},"regulated-air-gapped":{id:"regulated-air-gapped",description:"VPN/localhost only, no tunnel, no external telemetry \u2014 the highest-scrutiny buyer profile. Currently generator-identical to sandbox; the distinction is buyer framing, not yet enforced posture.",targets:["vm"]},"enterprise-fleet":{id:"enterprise-fleet",description:"Federated identity against a central IdP (enterprise-deploy/idp/) for a growing fleet of agents. Same-host colocation (no edge of its own) is available for vm/compose via the raw `--ingress edge-fed --colocate` flag \u2014 not yet encoded as a blueprint variant.",targets:["cloudrun","vm","compose","aws-ecs","azure-container-apps"]}};function k0(t,e){let n=Kr.BLUEPRINTS[t],r=e??n.targets[0];if(!n.targets.includes(r))throw new Error(`Blueprint "${t}" does not support --platform ${r}. Supported: ${n.targets.join(", ")}.`);switch(t){case"sandbox":case"regulated-air-gapped":return{target:r};case"prospect-demo":return{target:r,ingress:"edge"};case"enterprise-fleet":return{target:r,ingress:"edge-fed"};case"team-pilot":return r==="cloudrun"?{target:r,ingress:"iap"}:r==="databricks-app"||r==="spcs"||r==="cloudflare"?{target:r}:{target:r,ingress:"edge"}}}});var cw=T(Xr=>{"use strict";var $0=Xr&&Xr.__createBinding||(Object.create?(function(t,e,n,r){r===void 0&&(r=n);var s=Object.getOwnPropertyDescriptor(e,n);(!s||("get"in s?!e.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return e[n]}}),Object.defineProperty(t,r,s)}):(function(t,e,n,r){r===void 0&&(r=n),t[r]=e[n]})),D0=Xr&&Xr.__setModuleDefault||(Object.create?(function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}):function(t,e){t.default=e}),Wf=Xr&&Xr.__importStar||(function(){var t=function(e){return t=Object.getOwnPropertyNames||function(n){var r=[];for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(r[r.length]=s);return r},t(e)};return function(e){if(e&&e.__esModule)return e;var n={};if(e!=null)for(var r=t(e),s=0;s<r.length;s++)r[s]!=="default"&&$0(n,e,r[s]);return D0(n,e),n}})();Object.defineProperty(Xr,"__esModule",{value:!0});Xr.copyPackToStaging=aw;Xr.deployCommand=H0;var Yr=Wf(require("fs")),Ut=Wf(require("path")),L0=Wf(require("js-yaml")),qr=Ff(),Vf=ow(),nr=Rn(),M0=8080;function G0(t){let e=Ut.join(t,"agent.yaml");if(!Yr.existsSync(e))throw new Error(`No agent.yaml in ${t} \u2014 point deploy init at a pack directory.`);let n=L0.load(Yr.readFileSync(e,"utf8")),r=n?.metadata?.name?.trim();if(!r)throw new Error(`agent.yaml is missing metadata.name in ${t}`);let s=n?.metadata?.version?.trim();if(!s)throw new Error(`agent.yaml is missing metadata.version in ${t}`);let i=n?.spec?.model?.trim();return{name:r,version:s,engine:n?.spec?.engine?.trim()||"claude",...i?{model:i}:{}}}var U0=new Set([".git","node_modules","__pycache__",".pytest_cache",".mypy_cache",".ruff_cache",".venv","venv",".next",".turbo",".cache","dist","build",".DS_Store",".idea",".vscode"]);function aw(t,e){let n=[],r=Ut.resolve(e);return Yr.cpSync(t,e,{recursive:!0,force:!0,dereference:!0,preserveTimestamps:!0,filter:s=>{let i=Ut.relative(t,s);if(!i)return!0;if(s===r||s.startsWith(r+Ut.sep))return!1;let o=Ut.basename(s);if(i==="deploy"||i.startsWith("deploy"+Ut.sep)||i==="deploy-out"||i.startsWith("deploy-out"+Ut.sep)||U0.has(o)||o.startsWith(".nfs"))return!1;try{if(Yr.lstatSync(s).isSymbolicLink()&&!Yr.existsSync(s))return n.push(i),!1}catch{return!1}return!(o===".env"||o===".env.local"||o===".env.production"||o.endsWith(".log")||(0,qr.isSecretLikeBasename)(o))}}),n}async function H0(t,e,n){t!=="init"&&(console.error(`Unknown deploy action "${t}". Supported: init`),process.exit(1));let r=n.target,s=n.ingress;if(n.blueprint){if(!(0,Vf.isBlueprintId)(n.blueprint)){console.error(`Unknown --blueprint "${n.blueprint}". Supported: ${Object.keys(Vf.BLUEPRINTS).join(", ")}`),process.exit(1);return}if(n.platform&&!(0,qr.isDeployTarget)(n.platform)){console.error(`Unknown --platform "${n.platform}". Supported: ${qr.DEPLOY_TARGETS.join(", ")}`),process.exit(1);return}try{let f=(0,Vf.resolveBlueprint)(n.blueprint,n.platform);r=f.target,s=f.ingress}catch(f){console.error(`Error: ${f instanceof Error?f.message:f}`),process.exit(1);return}}let i=Ut.resolve(n.packdir||e||process.cwd()),o=r==="all"?[...qr.DEPLOY_TARGETS]:[r];for(let f of o)(0,qr.isDeployTarget)(f)||(console.error(`Unknown --target "${f}". Supported: ${qr.DEPLOY_TARGETS.join(", ")}, all`),process.exit(1));let a;try{a=G0(i)}catch(f){console.error(`Error: ${f instanceof Error?f.message:f}`),process.exit(1);return}let c=(0,qr.scanEnvRefs)(i),l=n.gateway?.trim();if(l)for(let f of["OPENCLAW_GATEWAY_TOKEN","SLACK_BOT_TOKEN","SLACK_APP_TOKEN"])c.includes(f)||c.push(f);let d=n.modelRouting==="gateway"?"gateway":n.modelRouting==="sidecar"?"sidecar":void 0;n.modelRouting&&n.modelRouting!=="gateway"&&n.modelRouting!=="sidecar"&&(console.error(`Unknown --model-routing "${n.modelRouting}". Supported: sidecar, gateway`),process.exit(1));let u={packName:a.name,packVersion:a.version,image:n.image||(0,qr.defaultRuntimeImage)(a.engine),port:n.port||M0,engine:a.engine,model:a.model,envVars:c,gatewayChannels:l,publicHostname:(0,qr.resolveDeployPublicHostname)(n.hostname),ingress:s,idpUrl:n.idpUrl,cookieDomain:n.cookieDomain,colocate:n.colocate,modelRouting:d,edgeTurnConnector:n.edgeTurnConnector?.trim()||void 0};for(let f of o){let p=d==="gateway"?"-gateway":"",g=n.out?o.length>1?Ut.join(Ut.resolve(n.out),f+p):Ut.resolve(n.out):Ut.join(process.cwd(),"deploy-out",`${a.name}-${f}${p}`);Yr.mkdirSync(g,{recursive:!0});let m=aw(i,g);if(m.length>0){let w=m.slice(0,3).join(", ");console.log(` ${nr.COLORS.yellow}\u26A0 ${m.length} broken symlink(s) skipped (not staged \u2014 the image would have no content for these). Sample: ${w}${m.length>3?", \u2026":""}${nr.COLORS.reset}`)}Yr.mkdirSync(Ut.join(g,"deploy"),{recursive:!0});let h=(0,qr.renderTarget)(f,u),y=[],E=[];for(let[w,v]of Object.entries(h)){let I=w===".dockerignore"?Ut.join(g,".dockerignore"):Ut.join(g,"deploy",w);if(Yr.existsSync(I)&&!n.force){E.push(w);continue}Yr.mkdirSync(Ut.dirname(I),{recursive:!0}),Yr.writeFileSync(I,v),y.push(w)}console.log(`${nr.COLORS.green}\u2713${nr.COLORS.reset} ${f} \u2192 ${Ut.relative(process.cwd(),g)||"."}`);for(let w of y)console.log(` ${nr.COLORS.dim}+ ${w}${nr.COLORS.reset}`);for(let w of E)console.log(` ${nr.COLORS.yellow}\u2022 ${w} (exists; --force to overwrite)${nr.COLORS.reset}`)}c.length>0&&console.log(`
|
|
603
|
-
${nr.COLORS.dim}Scanned pack secrets into .env.example: ${c.join(", ")}${nr.COLORS.reset}`),console.log("\nNext: cd into the staging dir above, `cp deploy/.env.example deploy/.env`, then run the command in deploy/README.md (scripts anchor themselves to the staging root)."),console.log(`${nr.COLORS.dim}Tip: run deploy init from the pack's parent directory \u2014 not from inside the pack (default deploy-out/ inside the pack copies into itself and fails).${nr.COLORS.reset}`)}});var uw=T(Kf=>{"use strict";Object.defineProperty(Kf,"__esModule",{value:!0});Kf.renderIdpArtifacts=V0;var j0=Gf(),lw=Ff();function x0(t){if(t.cookieDomain?.trim())return t.cookieDomain.trim();let e=t.hostname.split(".");return e.length>2?`.${e.slice(-2).join(".")}`:`.${t.hostname}`}function B0(){return["# Central IdP.","#","# ./run.sh","#","# Env split (matches `agenthippo deploy init`):","# ~/.agent-hippo/.env (+ optional ./.env) \u2014 hostname only","# ~/.agent-hippo/.env.auth (+ optional ./.env.auth) \u2014 JWT/OAuth; broker + oauth2-proxy ONLY","# Profiles: edge (SSO stack), tunnel (Cloudflare \u2192 Caddy)","# Org identity infra only \u2014 no agent runs here. Per-agent edges are generated separately","# by `agenthippo deploy init` and deployed on their own host/service.","services:"," oauth2-proxy:",' profiles: ["edge"]'," image: quay.io/oauth2-proxy/oauth2-proxy:v7.7.1"," env_file:"," - path: ${HOME}/.agent-hippo/.env"," required: false"," - path: .env"," required: false"," - path: ${HOME}/.agent-hippo/.env.auth"," required: false"," - path: .env.auth"," required: false"," environment:"," OAUTH2_PROXY_HTTP_ADDRESS: 0.0.0.0:4180"," OAUTH2_PROXY_UPSTREAMS: file:///dev/null",' OAUTH2_PROXY_SET_XAUTHREQUEST: "true"',' OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER: "true"',' OAUTH2_PROXY_SKIP_PROVIDER_BUTTON: "true"',' OAUTH2_PROXY_REVERSE_PROXY: "true"',' expose: ["4180"]',""," auth-broker:",' profiles: ["edge"]'," build:"," context: ."," dockerfile: Dockerfile.auth-broker"," env_file:"," - path: ${HOME}/.agent-hippo/.env"," required: false"," - path: .env"," required: false"," - path: ${HOME}/.agent-hippo/.env.auth"," required: false"," - path: .env.auth"," required: false"," environment:",' AGENTHIPPO_AUTH_PORT: "9780"'," AGENTHIPPO_AUTH_DATA_DIR: /data"," # The broker now defaults to binding 127.0.0.1 and REFUSES a public bind unless told"," # otherwise. Here it must listen on the container interface, because Caddy reaches it"," # as `auth-broker:9780` over the compose network \u2014 a loopback bind makes it unreachable."," # Safe: the port is `expose`d, never published to the host, so the only thing that can"," # dial it is another service on this network.",' AGENTHIPPO_AUTH_BIND: "0.0.0.0"',' AGENTHIPPO_AUTH_ALLOW_PUBLIC_BIND: "1"'," # Shared secret proving an X-Auth-Request-Email came from THIS Caddy. Without it the"," # broker treats that header as unverified and 401s the forward_auth-gated routes."," # No default on purpose: compose FAILS to start rather than silently running with an"," # empty secret on both sides, which would accept every caller. Put it in ./.env.",' AGENTHIPPO_AUTH_PROXY_SECRET: "${AGENTHIPPO_AUTH_PROXY_SECRET:?set AGENTHIPPO_AUTH_PROXY_SECRET in .env (openssl rand -hex 32)}"'," # Proxy hops in front of the broker: 1 = Caddy only (edge profile), 2 = Cloudflare"," # Tunnel \u2192 Caddy (run.sh sets 2; at 1 the rate limiter would key every tunnel"," # client on cloudflared's IP \u2014 one shared bucket)."," AGENTHIPPO_XFF_TRUSTED_HOPS: ${AGENTHIPPO_XFF_TRUSTED_HOPS:-1}",' volumes: ["auth-broker-data:/data"]',' expose: ["9780"]',""," caddy:",' profiles: ["edge"]'," image: caddy:2.
|
|
603
|
+
${nr.COLORS.dim}Scanned pack secrets into .env.example: ${c.join(", ")}${nr.COLORS.reset}`),console.log("\nNext: cd into the staging dir above, `cp deploy/.env.example deploy/.env`, then run the command in deploy/README.md (scripts anchor themselves to the staging root)."),console.log(`${nr.COLORS.dim}Tip: run deploy init from the pack's parent directory \u2014 not from inside the pack (default deploy-out/ inside the pack copies into itself and fails).${nr.COLORS.reset}`)}});var uw=T(Kf=>{"use strict";Object.defineProperty(Kf,"__esModule",{value:!0});Kf.renderIdpArtifacts=V0;var j0=Gf(),lw=Ff();function x0(t){if(t.cookieDomain?.trim())return t.cookieDomain.trim();let e=t.hostname.split(".");return e.length>2?`.${e.slice(-2).join(".")}`:`.${t.hostname}`}function B0(){return["# Central IdP.","#","# ./run.sh","#","# Env split (matches `agenthippo deploy init`):","# ~/.agent-hippo/.env (+ optional ./.env) \u2014 hostname only","# ~/.agent-hippo/.env.auth (+ optional ./.env.auth) \u2014 JWT/OAuth; broker + oauth2-proxy ONLY","# Profiles: edge (SSO stack), tunnel (Cloudflare \u2192 Caddy)","# Org identity infra only \u2014 no agent runs here. Per-agent edges are generated separately","# by `agenthippo deploy init` and deployed on their own host/service.","services:"," oauth2-proxy:",' profiles: ["edge"]'," image: quay.io/oauth2-proxy/oauth2-proxy:v7.7.1"," env_file:"," - path: ${HOME}/.agent-hippo/.env"," required: false"," - path: .env"," required: false"," - path: ${HOME}/.agent-hippo/.env.auth"," required: false"," - path: .env.auth"," required: false"," environment:"," OAUTH2_PROXY_HTTP_ADDRESS: 0.0.0.0:4180"," OAUTH2_PROXY_UPSTREAMS: file:///dev/null",' OAUTH2_PROXY_SET_XAUTHREQUEST: "true"',' OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER: "true"',' OAUTH2_PROXY_SKIP_PROVIDER_BUTTON: "true"',' OAUTH2_PROXY_REVERSE_PROXY: "true"'," # Absolute post-login redirects (rd=https://<host>/\u2026) are only honoured for whitelisted",' # hosts. The Caddyfile uses absolute rd so an IDE redirect_uri containing "//" inside'," # the authorize query is not rejected by the oauth2-proxy relative-URL validator."," OAUTH2_PROXY_WHITELIST_DOMAINS: ${OAUTH2_PROXY_WHITELIST_DOMAINS:-${AGENTHIPPO_PUBLIC_HOSTNAME}}",' expose: ["4180"]',""," auth-broker:",' profiles: ["edge"]'," build:"," context: ."," dockerfile: Dockerfile.auth-broker"," env_file:"," - path: ${HOME}/.agent-hippo/.env"," required: false"," - path: .env"," required: false"," - path: ${HOME}/.agent-hippo/.env.auth"," required: false"," - path: .env.auth"," required: false"," environment:",' AGENTHIPPO_AUTH_PORT: "9780"'," AGENTHIPPO_AUTH_DATA_DIR: /data"," # The broker now defaults to binding 127.0.0.1 and REFUSES a public bind unless told"," # otherwise. Here it must listen on the container interface, because Caddy reaches it"," # as `auth-broker:9780` over the compose network \u2014 a loopback bind makes it unreachable."," # Safe: the port is `expose`d, never published to the host, so the only thing that can"," # dial it is another service on this network.",' AGENTHIPPO_AUTH_BIND: "0.0.0.0"',' AGENTHIPPO_AUTH_ALLOW_PUBLIC_BIND: "1"'," # Shared secret proving an X-Auth-Request-Email came from THIS Caddy. Without it the"," # broker treats that header as unverified and 401s the forward_auth-gated routes."," # No default on purpose: compose FAILS to start rather than silently running with an"," # empty secret on both sides, which would accept every caller. Put it in ./.env.",' AGENTHIPPO_AUTH_PROXY_SECRET: "${AGENTHIPPO_AUTH_PROXY_SECRET:?set AGENTHIPPO_AUTH_PROXY_SECRET in .env (openssl rand -hex 32)}"'," # Proxy hops in front of the broker: 1 = Caddy only (edge profile), 2 = Cloudflare"," # Tunnel \u2192 Caddy (run.sh sets 2; at 1 the rate limiter would key every tunnel"," # client on cloudflared's IP \u2014 one shared bucket)."," AGENTHIPPO_XFF_TRUSTED_HOPS: ${AGENTHIPPO_XFF_TRUSTED_HOPS:-1}",' volumes: ["auth-broker-data:/data"]',' expose: ["9780"]',""," caddy:",' profiles: ["edge"]'," image: caddy:2.11-alpine"," env_file:"," - path: ${HOME}/.agent-hippo/.env"," required: false"," - path: .env"," required: false"," # With the tunnel profile, set AGENTHIPPO_CADDY_BIND=127.0.0.1 so a public-IP host"," # cannot be hit directly on 80/443, bypassing Cloudflare."," # Bare-metal / BYO nginx or ALB already on 80/443? Set AGENTHIPPO_CADDY_HTTP_PORT /"," # AGENTHIPPO_CADDY_HTTPS_PORT to alternate host ports and point your own reverse proxy"," # at them \u2014 the container still listens on 80 internally, only the host mapping moves."," ports:",' - "${AGENTHIPPO_CADDY_BIND:-0.0.0.0}:${AGENTHIPPO_CADDY_HTTP_PORT:-80}:80"',' - "${AGENTHIPPO_CADDY_BIND:-0.0.0.0}:${AGENTHIPPO_CADDY_HTTPS_PORT:-443}:443"'," environment:"," # Caddy stamps this on the two broker hops that forward a verified X-Auth-Request-Email"," # (/oauth/authorize, /oauth/browser-login). Same value the broker checks; same fail-closed"," # rule \u2014 compose refuses to start rather than send an empty secret.",' AGENTHIPPO_AUTH_PROXY_SECRET: "${AGENTHIPPO_AUTH_PROXY_SECRET:?set AGENTHIPPO_AUTH_PROXY_SECRET in .env (openssl rand -hex 32)}"'," volumes:"," - ./Caddyfile:/etc/caddy/Caddyfile:ro"," # Colocated agents (deploy init --colocate) drop a route snippet here \u2014 see Caddyfile."," - ./agents:/etc/caddy/agents:ro"," networks: [default, colocate]"," depends_on: [oauth2-proxy, auth-broker]",""," cloudflared:",' profiles: ["tunnel"]'," image: cloudflare/cloudflared:2024.12.2"," restart: unless-stopped"," command: tunnel --config /etc/cloudflared/config.yml run"," volumes:"," - ./cloudflared.yml:/etc/cloudflared/config.yml:ro"," # Set by run.sh from its argument (./run.sh /path/to/tunnel-credentials.json);"," # falls back to the conventional location for a direct `docker compose` invocation."," - ${AGENTHIPPO_TUNNEL_CREDENTIALS:-${HOME}/.agent-hippo/tunnel-credentials.json}:/etc/cloudflared/credentials.json:ro"," depends_on: [caddy]","","volumes:"," auth-broker-data:","","networks:"," default:",` # Stable, explicitly-named network (not the implicit "idp_default") so a colocated agent's`," # own compose project can join it reliably regardless of which directory `docker compose`"," # runs from. Only Caddy needs it \u2014 its per-agent snippets `reverse_proxy` straight to the"," # agent container by name; auth-broker is reached over the (already-shared) default network."," colocate:"," name: agenthippo-idp-colocate",""].join(`
|
|
604
604
|
`)}function F0(){return["# SSO edge + Cloudflare Tunnel. Set AGENTHIPPO_PUBLIC_HOSTNAME in .env (Caddy reads it below).","# TLS terminates at Cloudflare; origin listens on HTTP only.","{"," auto_https off","}","","# Same-host colocation: agents deployed with `deploy init --ingress edge-fed --colocate`","# on THIS host ship a route snippet instead of their own Caddy/oauth2-proxy/auth-broker.","# Drop it in ./agents/<name>.caddy (mounted in docker-compose.yml) and","# `docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile` to pick it up \u2014","# no rebuild, no restart of the IdP itself.","import agents/*.caddy","","http://{$AGENTHIPPO_PUBLIC_HOSTNAME} {",...(0,j0.renderIdentityLayer)({mode:"self-issued",oauth2ProxyAddr:"oauth2-proxy:4180",authBrokerAddr:"auth-broker:9780",healthProxy:[" reverse_proxy auth-broker:9780"],agentProxy:[' respond "not_found" 404'],catchallBody:[' respond "AgentHippo IdP" 200']}),"}",""].join(`
|
|
605
605
|
`)}function V0(t){let e=x0(t);return{"docker-compose.yml":B0(),"Dockerfile.auth-broker":(0,lw.renderAuthBrokerDockerfile)(),Caddyfile:F0(),".env.example":["# Caddy hostname for the central IdP. No agent runs on this stack \u2014 auth secrets:","# see .env.auth.example \u2192 ~/.agent-hippo/.env.auth (broker + oauth2-proxy only).","",`AGENTHIPPO_PUBLIC_HOSTNAME=${t.hostname}`,"","# REQUIRED. Shared secret between Caddy and auth-broker, proving that an","# X-Auth-Request-Email reached the broker through this Caddy rather than from a client","# that merely found the port. The broker 401s that header without a matching secret, and","# docker compose refuses to start if this is unset \u2014 an empty secret on both sides would","# accept everyone. It lives HERE (not .env.auth) because Caddy needs it too, and Caddy","# must not be handed the OAuth client secret or the JWT signing key.","# openssl rand -hex 32","AGENTHIPPO_AUTH_PROXY_SECRET=","","# Proxy hops / Caddy bind (optional; run.sh sets XFF hops=2 for tunnel)","# AGENTHIPPO_XFF_TRUSTED_HOPS=1","# AGENTHIPPO_CADDY_BIND=0.0.0.0","","# Bare metal / BYO nginx or ALB already on 80/443? Rebind Caddy to alternate host ports","# and point your reverse proxy at them (container still listens on 80 internally):","# AGENTHIPPO_CADDY_HTTP_PORT=8080","# AGENTHIPPO_CADDY_HTTPS_PORT=8443",""].join(`
|
|
606
606
|
`),".env.auth.example":(0,lw.renderAuthEnvExample)({hostname:t.hostname,fed:!1,cookieDomain:e}),"cloudflared.yml.example":["# Copy to cloudflared.yml; the credentials JSON lives in ~/.agent-hippo/ (never in the repo).","# cloudflared tunnel create agenthippo-auth","# cp ~/.cloudflared/<tunnel-id>.json ~/.agent-hippo/tunnel-credentials.json",`# cloudflared tunnel route dns agenthippo-auth ${t.hostname}`,"tunnel: REPLACE_WITH_TUNNEL_ID","credentials-file: /etc/cloudflared/credentials.json","ingress:",` - hostname: ${t.hostname}`," service: http://caddy:80"," - service: http_status:404",""].join(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agenthippo-ai/agenthippo",
|
|
3
|
-
"version": "1.110.
|
|
3
|
+
"version": "1.110.30",
|
|
4
4
|
"description": "AgentHippo CLI — serve, chat, and deploy agent packs",
|
|
5
5
|
"author": "AgentHippo <dev@agenthippo.ai>",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.
|
|
27
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.258",
|
|
28
28
|
"@anthropic-ai/sdk": "0.123.0",
|
|
29
29
|
"@cfworker/json-schema": "^4.1.1",
|
|
30
30
|
"@mariozechner/pi-ai": "0.54.0",
|